Import llvm-toolchain-5.0_5.0.orig-clang-tools-extra.tar.bz2
authorSylvestre Ledru <sylvestre@debian.org>
Wed, 6 Sep 2017 11:04:44 +0000 (12:04 +0100)
committerSylvestre Ledru <sylvestre@debian.org>
Wed, 6 Sep 2017 11:04:44 +0000 (12:04 +0100)
[dgit import orig llvm-toolchain-5.0_5.0.orig-clang-tools-extra.tar.bz2]

1272 files changed:
.arcconfig [new file with mode: 0644]
.gitignore [new file with mode: 0644]
CMakeLists.txt [new file with mode: 0644]
CODE_OWNERS.TXT [new file with mode: 0644]
LICENSE.TXT [new file with mode: 0644]
README.txt [new file with mode: 0644]
change-namespace/CMakeLists.txt [new file with mode: 0644]
change-namespace/ChangeNamespace.cpp [new file with mode: 0644]
change-namespace/ChangeNamespace.h [new file with mode: 0644]
change-namespace/tool/CMakeLists.txt [new file with mode: 0644]
change-namespace/tool/ClangChangeNamespace.cpp [new file with mode: 0644]
clang-apply-replacements/CMakeLists.txt [new file with mode: 0644]
clang-apply-replacements/include/clang-apply-replacements/Tooling/ApplyReplacements.h [new file with mode: 0644]
clang-apply-replacements/lib/Tooling/ApplyReplacements.cpp [new file with mode: 0644]
clang-apply-replacements/tool/CMakeLists.txt [new file with mode: 0644]
clang-apply-replacements/tool/ClangApplyReplacementsMain.cpp [new file with mode: 0644]
clang-move/CMakeLists.txt [new file with mode: 0644]
clang-move/ClangMove.cpp [new file with mode: 0644]
clang-move/ClangMove.h [new file with mode: 0644]
clang-move/HelperDeclRefGraph.cpp [new file with mode: 0644]
clang-move/HelperDeclRefGraph.h [new file with mode: 0644]
clang-move/tool/CMakeLists.txt [new file with mode: 0644]
clang-move/tool/ClangMoveMain.cpp [new file with mode: 0644]
clang-query/CMakeLists.txt [new file with mode: 0644]
clang-query/Query.cpp [new file with mode: 0644]
clang-query/Query.h [new file with mode: 0644]
clang-query/QueryParser.cpp [new file with mode: 0644]
clang-query/QueryParser.h [new file with mode: 0644]
clang-query/QuerySession.h [new file with mode: 0644]
clang-query/tool/CMakeLists.txt [new file with mode: 0644]
clang-query/tool/ClangQuery.cpp [new file with mode: 0644]
clang-reorder-fields/CMakeLists.txt [new file with mode: 0644]
clang-reorder-fields/ReorderFieldsAction.cpp [new file with mode: 0644]
clang-reorder-fields/ReorderFieldsAction.h [new file with mode: 0644]
clang-reorder-fields/tool/CMakeLists.txt [new file with mode: 0644]
clang-reorder-fields/tool/ClangReorderFields.cpp [new file with mode: 0644]
clang-tidy-vs/.gitignore [new file with mode: 0644]
clang-tidy-vs/CMakeLists.txt [new file with mode: 0644]
clang-tidy-vs/ClangTidy.sln [new file with mode: 0644]
clang-tidy-vs/ClangTidy/CategoryVerb.cs [new file with mode: 0644]
clang-tidy-vs/ClangTidy/CheckDatabase.cs [new file with mode: 0644]
clang-tidy-vs/ClangTidy/CheckTree.cs [new file with mode: 0644]
clang-tidy-vs/ClangTidy/ClangTidy.csproj [new file with mode: 0644]
clang-tidy-vs/ClangTidy/ClangTidy.vsct [new file with mode: 0644]
clang-tidy-vs/ClangTidy/ClangTidyCheckAttribute.cs [new file with mode: 0644]
clang-tidy-vs/ClangTidy/ClangTidyConfigParser.cs [new file with mode: 0644]
clang-tidy-vs/ClangTidy/ClangTidyConfigurationPage.cs [new file with mode: 0644]
clang-tidy-vs/ClangTidy/ClangTidyPackage.cs [new file with mode: 0644]
clang-tidy-vs/ClangTidy/ClangTidyProperties.cs [new file with mode: 0644]
clang-tidy-vs/ClangTidy/ClangTidyPropertyGrid.Designer.cs [new file with mode: 0644]
clang-tidy-vs/ClangTidy/ClangTidyPropertyGrid.cs [new file with mode: 0644]
clang-tidy-vs/ClangTidy/ClangTidyPropertyGrid.resx [new file with mode: 0644]
clang-tidy-vs/ClangTidy/DynamicPropertyComponent.Designer.cs [new file with mode: 0644]
clang-tidy-vs/ClangTidy/DynamicPropertyComponent.cs [new file with mode: 0644]
clang-tidy-vs/ClangTidy/DynamicPropertyConverter.cs [new file with mode: 0644]
clang-tidy-vs/ClangTidy/DynamicPropertyDescriptor.cs [new file with mode: 0644]
clang-tidy-vs/ClangTidy/ForwardingPropertyDescriptor.cs [new file with mode: 0644]
clang-tidy-vs/ClangTidy/GlobalSuppressions.cs [new file with mode: 0644]
clang-tidy-vs/ClangTidy/Guids.cs [new file with mode: 0644]
clang-tidy-vs/ClangTidy/PkgCmdID.cs [new file with mode: 0644]
clang-tidy-vs/ClangTidy/Properties/AssemblyInfo.cs [new file with mode: 0644]
clang-tidy-vs/ClangTidy/Resources.Designer.cs [new file with mode: 0644]
clang-tidy-vs/ClangTidy/Resources.resx [new file with mode: 0644]
clang-tidy-vs/ClangTidy/Resources/ClangTidyChecks.yaml [new file with mode: 0644]
clang-tidy-vs/ClangTidy/Resources/Images_32bit.bmp [new file with mode: 0644]
clang-tidy-vs/ClangTidy/Resources/Package.ico [new file with mode: 0644]
clang-tidy-vs/ClangTidy/Utility.cs [new file with mode: 0644]
clang-tidy-vs/ClangTidy/VSPackage.resx [new file with mode: 0644]
clang-tidy-vs/ClangTidy/license.txt [new file with mode: 0644]
clang-tidy-vs/ClangTidy/packages.config [new file with mode: 0644]
clang-tidy-vs/ClangTidy/source.extension.vsixmanifest [new file with mode: 0644]
clang-tidy-vs/README.txt [new file with mode: 0644]
clang-tidy-vs/source.extension.vsixmanifest.in [new file with mode: 0644]
clang-tidy/CMakeLists.txt [new file with mode: 0644]
clang-tidy/ClangTidy.cpp [new file with mode: 0644]
clang-tidy/ClangTidy.h [new file with mode: 0644]
clang-tidy/ClangTidyDiagnosticConsumer.cpp [new file with mode: 0644]
clang-tidy/ClangTidyDiagnosticConsumer.h [new file with mode: 0644]
clang-tidy/ClangTidyModule.cpp [new file with mode: 0644]
clang-tidy/ClangTidyModule.h [new file with mode: 0644]
clang-tidy/ClangTidyModuleRegistry.h [new file with mode: 0644]
clang-tidy/ClangTidyOptions.cpp [new file with mode: 0644]
clang-tidy/ClangTidyOptions.h [new file with mode: 0644]
clang-tidy/add_new_check.py [new file with mode: 0755]
clang-tidy/android/AndroidTidyModule.cpp [new file with mode: 0644]
clang-tidy/android/CMakeLists.txt [new file with mode: 0644]
clang-tidy/android/CloexecCreatCheck.cpp [new file with mode: 0644]
clang-tidy/android/CloexecCreatCheck.h [new file with mode: 0644]
clang-tidy/android/CloexecFopenCheck.cpp [new file with mode: 0644]
clang-tidy/android/CloexecFopenCheck.h [new file with mode: 0644]
clang-tidy/android/CloexecOpenCheck.cpp [new file with mode: 0644]
clang-tidy/android/CloexecOpenCheck.h [new file with mode: 0644]
clang-tidy/android/CloexecSocketCheck.cpp [new file with mode: 0644]
clang-tidy/android/CloexecSocketCheck.h [new file with mode: 0644]
clang-tidy/boost/BoostTidyModule.cpp [new file with mode: 0644]
clang-tidy/boost/CMakeLists.txt [new file with mode: 0644]
clang-tidy/boost/UseToStringCheck.cpp [new file with mode: 0644]
clang-tidy/boost/UseToStringCheck.h [new file with mode: 0644]
clang-tidy/bugprone/BugproneTidyModule.cpp [new file with mode: 0644]
clang-tidy/bugprone/CMakeLists.txt [new file with mode: 0644]
clang-tidy/bugprone/SuspiciousMemsetUsageCheck.cpp [new file with mode: 0644]
clang-tidy/bugprone/SuspiciousMemsetUsageCheck.h [new file with mode: 0644]
clang-tidy/bugprone/UndefinedMemoryManipulationCheck.cpp [new file with mode: 0644]
clang-tidy/bugprone/UndefinedMemoryManipulationCheck.h [new file with mode: 0644]
clang-tidy/cert/CERTTidyModule.cpp [new file with mode: 0644]
clang-tidy/cert/CMakeLists.txt [new file with mode: 0644]
clang-tidy/cert/CommandProcessorCheck.cpp [new file with mode: 0644]
clang-tidy/cert/CommandProcessorCheck.h [new file with mode: 0644]
clang-tidy/cert/DontModifyStdNamespaceCheck.cpp [new file with mode: 0644]
clang-tidy/cert/DontModifyStdNamespaceCheck.h [new file with mode: 0644]
clang-tidy/cert/FloatLoopCounter.cpp [new file with mode: 0644]
clang-tidy/cert/FloatLoopCounter.h [new file with mode: 0644]
clang-tidy/cert/LICENSE.TXT [new file with mode: 0644]
clang-tidy/cert/LimitedRandomnessCheck.cpp [new file with mode: 0644]
clang-tidy/cert/LimitedRandomnessCheck.h [new file with mode: 0644]
clang-tidy/cert/PostfixOperatorCheck.cpp [new file with mode: 0644]
clang-tidy/cert/PostfixOperatorCheck.h [new file with mode: 0644]
clang-tidy/cert/SetLongJmpCheck.cpp [new file with mode: 0644]
clang-tidy/cert/SetLongJmpCheck.h [new file with mode: 0644]
clang-tidy/cert/StaticObjectExceptionCheck.cpp [new file with mode: 0644]
clang-tidy/cert/StaticObjectExceptionCheck.h [new file with mode: 0644]
clang-tidy/cert/StrToNumCheck.cpp [new file with mode: 0644]
clang-tidy/cert/StrToNumCheck.h [new file with mode: 0644]
clang-tidy/cert/ThrownExceptionTypeCheck.cpp [new file with mode: 0644]
clang-tidy/cert/ThrownExceptionTypeCheck.h [new file with mode: 0644]
clang-tidy/cert/VariadicFunctionDefCheck.cpp [new file with mode: 0644]
clang-tidy/cert/VariadicFunctionDefCheck.h [new file with mode: 0644]
clang-tidy/cppcoreguidelines/CMakeLists.txt [new file with mode: 0644]
clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp [new file with mode: 0644]
clang-tidy/cppcoreguidelines/InterfacesGlobalInitCheck.cpp [new file with mode: 0644]
clang-tidy/cppcoreguidelines/InterfacesGlobalInitCheck.h [new file with mode: 0644]
clang-tidy/cppcoreguidelines/NoMallocCheck.cpp [new file with mode: 0644]
clang-tidy/cppcoreguidelines/NoMallocCheck.h [new file with mode: 0644]
clang-tidy/cppcoreguidelines/ProBoundsArrayToPointerDecayCheck.cpp [new file with mode: 0644]
clang-tidy/cppcoreguidelines/ProBoundsArrayToPointerDecayCheck.h [new file with mode: 0644]
clang-tidy/cppcoreguidelines/ProBoundsConstantArrayIndexCheck.cpp [new file with mode: 0644]
clang-tidy/cppcoreguidelines/ProBoundsConstantArrayIndexCheck.h [new file with mode: 0644]
clang-tidy/cppcoreguidelines/ProBoundsPointerArithmeticCheck.cpp [new file with mode: 0644]
clang-tidy/cppcoreguidelines/ProBoundsPointerArithmeticCheck.h [new file with mode: 0644]
clang-tidy/cppcoreguidelines/ProTypeConstCastCheck.cpp [new file with mode: 0644]
clang-tidy/cppcoreguidelines/ProTypeConstCastCheck.h [new file with mode: 0644]
clang-tidy/cppcoreguidelines/ProTypeCstyleCastCheck.cpp [new file with mode: 0644]
clang-tidy/cppcoreguidelines/ProTypeCstyleCastCheck.h [new file with mode: 0644]
clang-tidy/cppcoreguidelines/ProTypeMemberInitCheck.cpp [new file with mode: 0644]
clang-tidy/cppcoreguidelines/ProTypeMemberInitCheck.h [new file with mode: 0644]
clang-tidy/cppcoreguidelines/ProTypeReinterpretCastCheck.cpp [new file with mode: 0644]
clang-tidy/cppcoreguidelines/ProTypeReinterpretCastCheck.h [new file with mode: 0644]
clang-tidy/cppcoreguidelines/ProTypeStaticCastDowncastCheck.cpp [new file with mode: 0644]
clang-tidy/cppcoreguidelines/ProTypeStaticCastDowncastCheck.h [new file with mode: 0644]
clang-tidy/cppcoreguidelines/ProTypeUnionAccessCheck.cpp [new file with mode: 0644]
clang-tidy/cppcoreguidelines/ProTypeUnionAccessCheck.h [new file with mode: 0644]
clang-tidy/cppcoreguidelines/ProTypeVarargCheck.cpp [new file with mode: 0644]
clang-tidy/cppcoreguidelines/ProTypeVarargCheck.h [new file with mode: 0644]
clang-tidy/cppcoreguidelines/SlicingCheck.cpp [new file with mode: 0644]
clang-tidy/cppcoreguidelines/SlicingCheck.h [new file with mode: 0644]
clang-tidy/cppcoreguidelines/SpecialMemberFunctionsCheck.cpp [new file with mode: 0644]
clang-tidy/cppcoreguidelines/SpecialMemberFunctionsCheck.h [new file with mode: 0644]
clang-tidy/google/AvoidCStyleCastsCheck.cpp [new file with mode: 0644]
clang-tidy/google/AvoidCStyleCastsCheck.h [new file with mode: 0644]
clang-tidy/google/CMakeLists.txt [new file with mode: 0644]
clang-tidy/google/DefaultArgumentsCheck.cpp [new file with mode: 0644]
clang-tidy/google/DefaultArgumentsCheck.h [new file with mode: 0644]
clang-tidy/google/ExplicitConstructorCheck.cpp [new file with mode: 0644]
clang-tidy/google/ExplicitConstructorCheck.h [new file with mode: 0644]
clang-tidy/google/ExplicitMakePairCheck.cpp [new file with mode: 0644]
clang-tidy/google/ExplicitMakePairCheck.h [new file with mode: 0644]
clang-tidy/google/GlobalNamesInHeadersCheck.cpp [new file with mode: 0644]
clang-tidy/google/GlobalNamesInHeadersCheck.h [new file with mode: 0644]
clang-tidy/google/GoogleTidyModule.cpp [new file with mode: 0644]
clang-tidy/google/IntegerTypesCheck.cpp [new file with mode: 0644]
clang-tidy/google/IntegerTypesCheck.h [new file with mode: 0644]
clang-tidy/google/NonConstReferences.cpp [new file with mode: 0644]
clang-tidy/google/NonConstReferences.h [new file with mode: 0644]
clang-tidy/google/OverloadedUnaryAndCheck.cpp [new file with mode: 0644]
clang-tidy/google/OverloadedUnaryAndCheck.h [new file with mode: 0644]
clang-tidy/google/StringReferenceMemberCheck.cpp [new file with mode: 0644]
clang-tidy/google/StringReferenceMemberCheck.h [new file with mode: 0644]
clang-tidy/google/TodoCommentCheck.cpp [new file with mode: 0644]
clang-tidy/google/TodoCommentCheck.h [new file with mode: 0644]
clang-tidy/google/UnnamedNamespaceInHeaderCheck.cpp [new file with mode: 0644]
clang-tidy/google/UnnamedNamespaceInHeaderCheck.h [new file with mode: 0644]
clang-tidy/google/UsingNamespaceDirectiveCheck.cpp [new file with mode: 0644]
clang-tidy/google/UsingNamespaceDirectiveCheck.h [new file with mode: 0644]
clang-tidy/hicpp/CMakeLists.txt [new file with mode: 0644]
clang-tidy/hicpp/HICPPTidyModule.cpp [new file with mode: 0644]
clang-tidy/hicpp/LICENSE.TXT [new file with mode: 0644]
clang-tidy/hicpp/NoAssemblerCheck.cpp [new file with mode: 0644]
clang-tidy/hicpp/NoAssemblerCheck.h [new file with mode: 0644]
clang-tidy/llvm/CMakeLists.txt [new file with mode: 0644]
clang-tidy/llvm/HeaderGuardCheck.cpp [new file with mode: 0644]
clang-tidy/llvm/HeaderGuardCheck.h [new file with mode: 0644]
clang-tidy/llvm/IncludeOrderCheck.cpp [new file with mode: 0644]
clang-tidy/llvm/IncludeOrderCheck.h [new file with mode: 0644]
clang-tidy/llvm/LLVMTidyModule.cpp [new file with mode: 0644]
clang-tidy/llvm/TwineLocalCheck.cpp [new file with mode: 0644]
clang-tidy/llvm/TwineLocalCheck.h [new file with mode: 0644]
clang-tidy/misc/ArgumentCommentCheck.cpp [new file with mode: 0644]
clang-tidy/misc/ArgumentCommentCheck.h [new file with mode: 0644]
clang-tidy/misc/AssertSideEffectCheck.cpp [new file with mode: 0644]
clang-tidy/misc/AssertSideEffectCheck.h [new file with mode: 0644]
clang-tidy/misc/BoolPointerImplicitConversionCheck.cpp [new file with mode: 0644]
clang-tidy/misc/BoolPointerImplicitConversionCheck.h [new file with mode: 0644]
clang-tidy/misc/CMakeLists.txt [new file with mode: 0644]
clang-tidy/misc/DanglingHandleCheck.cpp [new file with mode: 0644]
clang-tidy/misc/DanglingHandleCheck.h [new file with mode: 0644]
clang-tidy/misc/DefinitionsInHeadersCheck.cpp [new file with mode: 0644]
clang-tidy/misc/DefinitionsInHeadersCheck.h [new file with mode: 0644]
clang-tidy/misc/FoldInitTypeCheck.cpp [new file with mode: 0644]
clang-tidy/misc/FoldInitTypeCheck.h [new file with mode: 0644]
clang-tidy/misc/ForwardDeclarationNamespaceCheck.cpp [new file with mode: 0644]
clang-tidy/misc/ForwardDeclarationNamespaceCheck.h [new file with mode: 0644]
clang-tidy/misc/ForwardingReferenceOverloadCheck.cpp [new file with mode: 0644]
clang-tidy/misc/ForwardingReferenceOverloadCheck.h [new file with mode: 0644]
clang-tidy/misc/InaccurateEraseCheck.cpp [new file with mode: 0644]
clang-tidy/misc/InaccurateEraseCheck.h [new file with mode: 0644]
clang-tidy/misc/IncorrectRoundings.cpp [new file with mode: 0644]
clang-tidy/misc/IncorrectRoundings.h [new file with mode: 0644]
clang-tidy/misc/InefficientAlgorithmCheck.cpp [new file with mode: 0644]
clang-tidy/misc/InefficientAlgorithmCheck.h [new file with mode: 0644]
clang-tidy/misc/LambdaFunctionNameCheck.cpp [new file with mode: 0644]
clang-tidy/misc/LambdaFunctionNameCheck.h [new file with mode: 0644]
clang-tidy/misc/MacroParenthesesCheck.cpp [new file with mode: 0644]
clang-tidy/misc/MacroParenthesesCheck.h [new file with mode: 0644]
clang-tidy/misc/MacroRepeatedSideEffectsCheck.cpp [new file with mode: 0644]
clang-tidy/misc/MacroRepeatedSideEffectsCheck.h [new file with mode: 0644]
clang-tidy/misc/MiscTidyModule.cpp [new file with mode: 0644]
clang-tidy/misc/MisplacedConstCheck.cpp [new file with mode: 0644]
clang-tidy/misc/MisplacedConstCheck.h [new file with mode: 0644]
clang-tidy/misc/MisplacedWideningCastCheck.cpp [new file with mode: 0644]
clang-tidy/misc/MisplacedWideningCastCheck.h [new file with mode: 0644]
clang-tidy/misc/MoveConstantArgumentCheck.cpp [new file with mode: 0644]
clang-tidy/misc/MoveConstantArgumentCheck.h [new file with mode: 0644]
clang-tidy/misc/MoveConstructorInitCheck.cpp [new file with mode: 0644]
clang-tidy/misc/MoveConstructorInitCheck.h [new file with mode: 0644]
clang-tidy/misc/MoveForwardingReferenceCheck.cpp [new file with mode: 0644]
clang-tidy/misc/MoveForwardingReferenceCheck.h [new file with mode: 0644]
clang-tidy/misc/MultipleStatementMacroCheck.cpp [new file with mode: 0644]
clang-tidy/misc/MultipleStatementMacroCheck.h [new file with mode: 0644]
clang-tidy/misc/NewDeleteOverloadsCheck.cpp [new file with mode: 0644]
clang-tidy/misc/NewDeleteOverloadsCheck.h [new file with mode: 0644]
clang-tidy/misc/NoexceptMoveConstructorCheck.cpp [new file with mode: 0644]
clang-tidy/misc/NoexceptMoveConstructorCheck.h [new file with mode: 0644]
clang-tidy/misc/NonCopyableObjects.cpp [new file with mode: 0644]
clang-tidy/misc/NonCopyableObjects.h [new file with mode: 0644]
clang-tidy/misc/RedundantExpressionCheck.cpp [new file with mode: 0644]
clang-tidy/misc/RedundantExpressionCheck.h [new file with mode: 0644]
clang-tidy/misc/SizeofContainerCheck.cpp [new file with mode: 0644]
clang-tidy/misc/SizeofContainerCheck.h [new file with mode: 0644]
clang-tidy/misc/SizeofExpressionCheck.cpp [new file with mode: 0644]
clang-tidy/misc/SizeofExpressionCheck.h [new file with mode: 0644]
clang-tidy/misc/StaticAssertCheck.cpp [new file with mode: 0644]
clang-tidy/misc/StaticAssertCheck.h [new file with mode: 0644]
clang-tidy/misc/StringCompareCheck.cpp [new file with mode: 0644]
clang-tidy/misc/StringCompareCheck.h [new file with mode: 0644]
clang-tidy/misc/StringConstructorCheck.cpp [new file with mode: 0644]
clang-tidy/misc/StringConstructorCheck.h [new file with mode: 0644]
clang-tidy/misc/StringIntegerAssignmentCheck.cpp [new file with mode: 0644]
clang-tidy/misc/StringIntegerAssignmentCheck.h [new file with mode: 0644]
clang-tidy/misc/StringLiteralWithEmbeddedNulCheck.cpp [new file with mode: 0644]
clang-tidy/misc/StringLiteralWithEmbeddedNulCheck.h [new file with mode: 0644]
clang-tidy/misc/SuspiciousEnumUsageCheck.cpp [new file with mode: 0644]
clang-tidy/misc/SuspiciousEnumUsageCheck.h [new file with mode: 0644]
clang-tidy/misc/SuspiciousMissingCommaCheck.cpp [new file with mode: 0644]
clang-tidy/misc/SuspiciousMissingCommaCheck.h [new file with mode: 0644]
clang-tidy/misc/SuspiciousSemicolonCheck.cpp [new file with mode: 0644]
clang-tidy/misc/SuspiciousSemicolonCheck.h [new file with mode: 0644]
clang-tidy/misc/SuspiciousStringCompareCheck.cpp [new file with mode: 0644]
clang-tidy/misc/SuspiciousStringCompareCheck.h [new file with mode: 0644]
clang-tidy/misc/SwappedArgumentsCheck.cpp [new file with mode: 0644]
clang-tidy/misc/SwappedArgumentsCheck.h [new file with mode: 0644]
clang-tidy/misc/ThrowByValueCatchByReferenceCheck.cpp [new file with mode: 0644]
clang-tidy/misc/ThrowByValueCatchByReferenceCheck.h [new file with mode: 0644]
clang-tidy/misc/UnconventionalAssignOperatorCheck.cpp [new file with mode: 0644]
clang-tidy/misc/UnconventionalAssignOperatorCheck.h [new file with mode: 0644]
clang-tidy/misc/UndelegatedConstructor.cpp [new file with mode: 0644]
clang-tidy/misc/UndelegatedConstructor.h [new file with mode: 0644]
clang-tidy/misc/UniqueptrResetReleaseCheck.cpp [new file with mode: 0644]
clang-tidy/misc/UniqueptrResetReleaseCheck.h [new file with mode: 0644]
clang-tidy/misc/UnusedAliasDeclsCheck.cpp [new file with mode: 0644]
clang-tidy/misc/UnusedAliasDeclsCheck.h [new file with mode: 0644]
clang-tidy/misc/UnusedParametersCheck.cpp [new file with mode: 0644]
clang-tidy/misc/UnusedParametersCheck.h [new file with mode: 0644]
clang-tidy/misc/UnusedRAIICheck.cpp [new file with mode: 0644]
clang-tidy/misc/UnusedRAIICheck.h [new file with mode: 0644]
clang-tidy/misc/UnusedUsingDeclsCheck.cpp [new file with mode: 0644]
clang-tidy/misc/UnusedUsingDeclsCheck.h [new file with mode: 0644]
clang-tidy/misc/UseAfterMoveCheck.cpp [new file with mode: 0644]
clang-tidy/misc/UseAfterMoveCheck.h [new file with mode: 0644]
clang-tidy/misc/VirtualNearMissCheck.cpp [new file with mode: 0644]
clang-tidy/misc/VirtualNearMissCheck.h [new file with mode: 0644]
clang-tidy/modernize/AvoidBindCheck.cpp [new file with mode: 0644]
clang-tidy/modernize/AvoidBindCheck.h [new file with mode: 0644]
clang-tidy/modernize/CMakeLists.txt [new file with mode: 0644]
clang-tidy/modernize/DeprecatedHeadersCheck.cpp [new file with mode: 0644]
clang-tidy/modernize/DeprecatedHeadersCheck.h [new file with mode: 0644]
clang-tidy/modernize/LoopConvertCheck.cpp [new file with mode: 0644]
clang-tidy/modernize/LoopConvertCheck.h [new file with mode: 0644]
clang-tidy/modernize/LoopConvertUtils.cpp [new file with mode: 0644]
clang-tidy/modernize/LoopConvertUtils.h [new file with mode: 0644]
clang-tidy/modernize/MakeSharedCheck.cpp [new file with mode: 0644]
clang-tidy/modernize/MakeSharedCheck.h [new file with mode: 0644]
clang-tidy/modernize/MakeSmartPtrCheck.cpp [new file with mode: 0644]
clang-tidy/modernize/MakeSmartPtrCheck.h [new file with mode: 0644]
clang-tidy/modernize/MakeUniqueCheck.cpp [new file with mode: 0644]
clang-tidy/modernize/MakeUniqueCheck.h [new file with mode: 0644]
clang-tidy/modernize/ModernizeTidyModule.cpp [new file with mode: 0644]
clang-tidy/modernize/PassByValueCheck.cpp [new file with mode: 0644]
clang-tidy/modernize/PassByValueCheck.h [new file with mode: 0644]
clang-tidy/modernize/RawStringLiteralCheck.cpp [new file with mode: 0644]
clang-tidy/modernize/RawStringLiteralCheck.h [new file with mode: 0644]
clang-tidy/modernize/RedundantVoidArgCheck.cpp [new file with mode: 0644]
clang-tidy/modernize/RedundantVoidArgCheck.h [new file with mode: 0644]
clang-tidy/modernize/ReplaceAutoPtrCheck.cpp [new file with mode: 0644]
clang-tidy/modernize/ReplaceAutoPtrCheck.h [new file with mode: 0644]
clang-tidy/modernize/ReplaceRandomShuffleCheck.cpp [new file with mode: 0644]
clang-tidy/modernize/ReplaceRandomShuffleCheck.h [new file with mode: 0644]
clang-tidy/modernize/ReturnBracedInitListCheck.cpp [new file with mode: 0644]
clang-tidy/modernize/ReturnBracedInitListCheck.h [new file with mode: 0644]
clang-tidy/modernize/ShrinkToFitCheck.cpp [new file with mode: 0644]
clang-tidy/modernize/ShrinkToFitCheck.h [new file with mode: 0644]
clang-tidy/modernize/UnaryStaticAssertCheck.cpp [new file with mode: 0644]
clang-tidy/modernize/UnaryStaticAssertCheck.h [new file with mode: 0644]
clang-tidy/modernize/UseAutoCheck.cpp [new file with mode: 0644]
clang-tidy/modernize/UseAutoCheck.h [new file with mode: 0644]
clang-tidy/modernize/UseBoolLiteralsCheck.cpp [new file with mode: 0644]
clang-tidy/modernize/UseBoolLiteralsCheck.h [new file with mode: 0644]
clang-tidy/modernize/UseDefaultMemberInitCheck.cpp [new file with mode: 0644]
clang-tidy/modernize/UseDefaultMemberInitCheck.h [new file with mode: 0644]
clang-tidy/modernize/UseEmplaceCheck.cpp [new file with mode: 0644]
clang-tidy/modernize/UseEmplaceCheck.h [new file with mode: 0644]
clang-tidy/modernize/UseEqualsDefaultCheck.cpp [new file with mode: 0644]
clang-tidy/modernize/UseEqualsDefaultCheck.h [new file with mode: 0644]
clang-tidy/modernize/UseEqualsDeleteCheck.cpp [new file with mode: 0644]
clang-tidy/modernize/UseEqualsDeleteCheck.h [new file with mode: 0644]
clang-tidy/modernize/UseNoexceptCheck.cpp [new file with mode: 0644]
clang-tidy/modernize/UseNoexceptCheck.h [new file with mode: 0644]
clang-tidy/modernize/UseNullptrCheck.cpp [new file with mode: 0644]
clang-tidy/modernize/UseNullptrCheck.h [new file with mode: 0644]
clang-tidy/modernize/UseOverrideCheck.cpp [new file with mode: 0644]
clang-tidy/modernize/UseOverrideCheck.h [new file with mode: 0644]
clang-tidy/modernize/UseTransparentFunctorsCheck.cpp [new file with mode: 0644]
clang-tidy/modernize/UseTransparentFunctorsCheck.h [new file with mode: 0644]
clang-tidy/modernize/UseUsingCheck.cpp [new file with mode: 0644]
clang-tidy/modernize/UseUsingCheck.h [new file with mode: 0644]
clang-tidy/mpi/BufferDerefCheck.cpp [new file with mode: 0644]
clang-tidy/mpi/BufferDerefCheck.h [new file with mode: 0644]
clang-tidy/mpi/CMakeLists.txt [new file with mode: 0644]
clang-tidy/mpi/MPITidyModule.cpp [new file with mode: 0644]
clang-tidy/mpi/TypeMismatchCheck.cpp [new file with mode: 0644]
clang-tidy/mpi/TypeMismatchCheck.h [new file with mode: 0644]
clang-tidy/performance/CMakeLists.txt [new file with mode: 0644]
clang-tidy/performance/FasterStringFindCheck.cpp [new file with mode: 0644]
clang-tidy/performance/FasterStringFindCheck.h [new file with mode: 0644]
clang-tidy/performance/ForRangeCopyCheck.cpp [new file with mode: 0644]
clang-tidy/performance/ForRangeCopyCheck.h [new file with mode: 0644]
clang-tidy/performance/ImplicitCastInLoopCheck.cpp [new file with mode: 0644]
clang-tidy/performance/ImplicitCastInLoopCheck.h [new file with mode: 0644]
clang-tidy/performance/InefficientStringConcatenationCheck.cpp [new file with mode: 0644]
clang-tidy/performance/InefficientStringConcatenationCheck.h [new file with mode: 0644]
clang-tidy/performance/InefficientVectorOperationCheck.cpp [new file with mode: 0644]
clang-tidy/performance/InefficientVectorOperationCheck.h [new file with mode: 0644]
clang-tidy/performance/PerformanceTidyModule.cpp [new file with mode: 0644]
clang-tidy/performance/TypePromotionInMathFnCheck.cpp [new file with mode: 0644]
clang-tidy/performance/TypePromotionInMathFnCheck.h [new file with mode: 0644]
clang-tidy/performance/UnnecessaryCopyInitialization.cpp [new file with mode: 0644]
clang-tidy/performance/UnnecessaryCopyInitialization.h [new file with mode: 0644]
clang-tidy/performance/UnnecessaryValueParamCheck.cpp [new file with mode: 0644]
clang-tidy/performance/UnnecessaryValueParamCheck.h [new file with mode: 0644]
clang-tidy/plugin/CMakeLists.txt [new file with mode: 0644]
clang-tidy/plugin/ClangTidyPlugin.cpp [new file with mode: 0644]
clang-tidy/readability/AvoidConstParamsInDecls.cpp [new file with mode: 0644]
clang-tidy/readability/AvoidConstParamsInDecls.h [new file with mode: 0644]
clang-tidy/readability/BracesAroundStatementsCheck.cpp [new file with mode: 0644]
clang-tidy/readability/BracesAroundStatementsCheck.h [new file with mode: 0644]
clang-tidy/readability/CMakeLists.txt [new file with mode: 0644]
clang-tidy/readability/ContainerSizeEmptyCheck.cpp [new file with mode: 0644]
clang-tidy/readability/ContainerSizeEmptyCheck.h [new file with mode: 0644]
clang-tidy/readability/DeleteNullPointerCheck.cpp [new file with mode: 0644]
clang-tidy/readability/DeleteNullPointerCheck.h [new file with mode: 0644]
clang-tidy/readability/DeletedDefaultCheck.cpp [new file with mode: 0644]
clang-tidy/readability/DeletedDefaultCheck.h [new file with mode: 0644]
clang-tidy/readability/ElseAfterReturnCheck.cpp [new file with mode: 0644]
clang-tidy/readability/ElseAfterReturnCheck.h [new file with mode: 0644]
clang-tidy/readability/FunctionSizeCheck.cpp [new file with mode: 0644]
clang-tidy/readability/FunctionSizeCheck.h [new file with mode: 0644]
clang-tidy/readability/IdentifierNamingCheck.cpp [new file with mode: 0644]
clang-tidy/readability/IdentifierNamingCheck.h [new file with mode: 0644]
clang-tidy/readability/ImplicitBoolCastCheck.cpp [new file with mode: 0644]
clang-tidy/readability/ImplicitBoolCastCheck.h [new file with mode: 0644]
clang-tidy/readability/InconsistentDeclarationParameterNameCheck.cpp [new file with mode: 0644]
clang-tidy/readability/InconsistentDeclarationParameterNameCheck.h [new file with mode: 0644]
clang-tidy/readability/MisleadingIndentationCheck.cpp [new file with mode: 0644]
clang-tidy/readability/MisleadingIndentationCheck.h [new file with mode: 0644]
clang-tidy/readability/MisplacedArrayIndexCheck.cpp [new file with mode: 0644]
clang-tidy/readability/MisplacedArrayIndexCheck.h [new file with mode: 0644]
clang-tidy/readability/NamedParameterCheck.cpp [new file with mode: 0644]
clang-tidy/readability/NamedParameterCheck.h [new file with mode: 0644]
clang-tidy/readability/NamespaceCommentCheck.cpp [new file with mode: 0644]
clang-tidy/readability/NamespaceCommentCheck.h [new file with mode: 0644]
clang-tidy/readability/NonConstParameterCheck.cpp [new file with mode: 0644]
clang-tidy/readability/NonConstParameterCheck.h [new file with mode: 0644]
clang-tidy/readability/ReadabilityTidyModule.cpp [new file with mode: 0644]
clang-tidy/readability/RedundantControlFlowCheck.cpp [new file with mode: 0644]
clang-tidy/readability/RedundantControlFlowCheck.h [new file with mode: 0644]
clang-tidy/readability/RedundantDeclarationCheck.cpp [new file with mode: 0644]
clang-tidy/readability/RedundantDeclarationCheck.h [new file with mode: 0644]
clang-tidy/readability/RedundantFunctionPtrDereferenceCheck.cpp [new file with mode: 0644]
clang-tidy/readability/RedundantFunctionPtrDereferenceCheck.h [new file with mode: 0644]
clang-tidy/readability/RedundantMemberInitCheck.cpp [new file with mode: 0644]
clang-tidy/readability/RedundantMemberInitCheck.h [new file with mode: 0644]
clang-tidy/readability/RedundantSmartptrGetCheck.cpp [new file with mode: 0644]
clang-tidy/readability/RedundantSmartptrGetCheck.h [new file with mode: 0644]
clang-tidy/readability/RedundantStringCStrCheck.cpp [new file with mode: 0644]
clang-tidy/readability/RedundantStringCStrCheck.h [new file with mode: 0644]
clang-tidy/readability/RedundantStringInitCheck.cpp [new file with mode: 0644]
clang-tidy/readability/RedundantStringInitCheck.h [new file with mode: 0644]
clang-tidy/readability/SimplifyBooleanExprCheck.cpp [new file with mode: 0644]
clang-tidy/readability/SimplifyBooleanExprCheck.h [new file with mode: 0644]
clang-tidy/readability/StaticDefinitionInAnonymousNamespaceCheck.cpp [new file with mode: 0644]
clang-tidy/readability/StaticDefinitionInAnonymousNamespaceCheck.h [new file with mode: 0644]
clang-tidy/readability/UniqueptrDeleteReleaseCheck.cpp [new file with mode: 0644]
clang-tidy/readability/UniqueptrDeleteReleaseCheck.h [new file with mode: 0644]
clang-tidy/rename_check.py [new file with mode: 0755]
clang-tidy/tool/CMakeLists.txt [new file with mode: 0644]
clang-tidy/tool/ClangTidyMain.cpp [new file with mode: 0644]
clang-tidy/tool/clang-tidy-diff.py [new file with mode: 0755]
clang-tidy/tool/run-clang-tidy.py [new file with mode: 0755]
clang-tidy/utils/ASTUtils.cpp [new file with mode: 0644]
clang-tidy/utils/ASTUtils.h [new file with mode: 0644]
clang-tidy/utils/CMakeLists.txt [new file with mode: 0644]
clang-tidy/utils/DeclRefExprUtils.cpp [new file with mode: 0644]
clang-tidy/utils/DeclRefExprUtils.h [new file with mode: 0644]
clang-tidy/utils/ExprSequence.cpp [new file with mode: 0644]
clang-tidy/utils/ExprSequence.h [new file with mode: 0644]
clang-tidy/utils/FixItHintUtils.cpp [new file with mode: 0644]
clang-tidy/utils/FixItHintUtils.h [new file with mode: 0644]
clang-tidy/utils/HeaderFileExtensionsUtils.cpp [new file with mode: 0644]
clang-tidy/utils/HeaderFileExtensionsUtils.h [new file with mode: 0644]
clang-tidy/utils/HeaderGuard.cpp [new file with mode: 0644]
clang-tidy/utils/HeaderGuard.h [new file with mode: 0644]
clang-tidy/utils/IncludeInserter.cpp [new file with mode: 0644]
clang-tidy/utils/IncludeInserter.h [new file with mode: 0644]
clang-tidy/utils/IncludeSorter.cpp [new file with mode: 0644]
clang-tidy/utils/IncludeSorter.h [new file with mode: 0644]
clang-tidy/utils/LexerUtils.cpp [new file with mode: 0644]
clang-tidy/utils/LexerUtils.h [new file with mode: 0644]
clang-tidy/utils/Matchers.h [new file with mode: 0644]
clang-tidy/utils/NamespaceAliaser.cpp [new file with mode: 0644]
clang-tidy/utils/NamespaceAliaser.h [new file with mode: 0644]
clang-tidy/utils/OptionsUtils.cpp [new file with mode: 0644]
clang-tidy/utils/OptionsUtils.h [new file with mode: 0644]
clang-tidy/utils/TypeTraits.cpp [new file with mode: 0644]
clang-tidy/utils/TypeTraits.h [new file with mode: 0644]
clang-tidy/utils/UsingInserter.cpp [new file with mode: 0644]
clang-tidy/utils/UsingInserter.h [new file with mode: 0644]
clangd/CMakeLists.txt [new file with mode: 0644]
clangd/ClangdLSPServer.cpp [new file with mode: 0644]
clangd/ClangdLSPServer.h [new file with mode: 0644]
clangd/ClangdServer.cpp [new file with mode: 0644]
clangd/ClangdServer.h [new file with mode: 0644]
clangd/ClangdUnit.cpp [new file with mode: 0644]
clangd/ClangdUnit.h [new file with mode: 0644]
clangd/ClangdUnitStore.cpp [new file with mode: 0644]
clangd/ClangdUnitStore.h [new file with mode: 0644]
clangd/DraftStore.cpp [new file with mode: 0644]
clangd/DraftStore.h [new file with mode: 0644]
clangd/GlobalCompilationDatabase.cpp [new file with mode: 0644]
clangd/GlobalCompilationDatabase.h [new file with mode: 0644]
clangd/JSONRPCDispatcher.cpp [new file with mode: 0644]
clangd/JSONRPCDispatcher.h [new file with mode: 0644]
clangd/Path.h [new file with mode: 0644]
clangd/Protocol.cpp [new file with mode: 0644]
clangd/Protocol.h [new file with mode: 0644]
clangd/ProtocolHandlers.cpp [new file with mode: 0644]
clangd/ProtocolHandlers.h [new file with mode: 0644]
clangd/clients/clangd-vscode/.gitignore [new file with mode: 0644]
clangd/clients/clangd-vscode/.vscode/launch.json [new file with mode: 0644]
clangd/clients/clangd-vscode/.vscode/settings.json [new file with mode: 0644]
clangd/clients/clangd-vscode/.vscode/tasks.json [new file with mode: 0644]
clangd/clients/clangd-vscode/.vscodeignore [new file with mode: 0644]
clangd/clients/clangd-vscode/README.txt [new file with mode: 0644]
clangd/clients/clangd-vscode/package.json [new file with mode: 0644]
clangd/clients/clangd-vscode/src/extension.ts [new file with mode: 0644]
clangd/clients/clangd-vscode/test/extension.test.ts [new file with mode: 0644]
clangd/clients/clangd-vscode/test/index.ts [new file with mode: 0644]
clangd/clients/clangd-vscode/tsconfig.json [new file with mode: 0644]
clangd/clients/clangd-vscode/vsc-extension-quickstart.md [new file with mode: 0644]
clangd/tool/CMakeLists.txt [new file with mode: 0644]
clangd/tool/ClangdMain.cpp [new file with mode: 0644]
docs/CMakeLists.txt [new file with mode: 0644]
docs/Doxyfile [new file with mode: 0644]
docs/ModularizeUsage.rst [new file with mode: 0644]
docs/README.txt [new file with mode: 0644]
docs/ReleaseNotes.rst [new file with mode: 0644]
docs/clang-modernize.rst [new file with mode: 0644]
docs/clang-rename.rst [new file with mode: 0644]
docs/clang-tidy.rst [new file with mode: 0644]
docs/clang-tidy/checks/android-cloexec-creat.rst [new file with mode: 0644]
docs/clang-tidy/checks/android-cloexec-fopen.rst [new file with mode: 0644]
docs/clang-tidy/checks/android-cloexec-open.rst [new file with mode: 0644]
docs/clang-tidy/checks/android-cloexec-socket.rst [new file with mode: 0644]
docs/clang-tidy/checks/boost-use-to-string.rst [new file with mode: 0644]
docs/clang-tidy/checks/bugprone-suspicious-memset-usage.rst [new file with mode: 0644]
docs/clang-tidy/checks/bugprone-undefined-memory-manipulation.rst [new file with mode: 0644]
docs/clang-tidy/checks/cert-dcl03-c.rst [new file with mode: 0644]
docs/clang-tidy/checks/cert-dcl21-cpp.rst [new file with mode: 0644]
docs/clang-tidy/checks/cert-dcl50-cpp.rst [new file with mode: 0644]
docs/clang-tidy/checks/cert-dcl54-cpp.rst [new file with mode: 0644]
docs/clang-tidy/checks/cert-dcl58-cpp.rst [new file with mode: 0644]
docs/clang-tidy/checks/cert-dcl59-cpp.rst [new file with mode: 0644]
docs/clang-tidy/checks/cert-env33-c.rst [new file with mode: 0644]
docs/clang-tidy/checks/cert-err09-cpp.rst [new file with mode: 0644]
docs/clang-tidy/checks/cert-err34-c.rst [new file with mode: 0644]
docs/clang-tidy/checks/cert-err52-cpp.rst [new file with mode: 0644]
docs/clang-tidy/checks/cert-err58-cpp.rst [new file with mode: 0644]
docs/clang-tidy/checks/cert-err60-cpp.rst [new file with mode: 0644]
docs/clang-tidy/checks/cert-err61-cpp.rst [new file with mode: 0644]
docs/clang-tidy/checks/cert-fio38-c.rst [new file with mode: 0644]
docs/clang-tidy/checks/cert-flp30-c.rst [new file with mode: 0644]
docs/clang-tidy/checks/cert-msc30-c.rst [new file with mode: 0644]
docs/clang-tidy/checks/cert-msc50-cpp.rst [new file with mode: 0644]
docs/clang-tidy/checks/cert-oop11-cpp.rst [new file with mode: 0644]
docs/clang-tidy/checks/cppcoreguidelines-interfaces-global-init.rst [new file with mode: 0644]
docs/clang-tidy/checks/cppcoreguidelines-no-malloc.rst [new file with mode: 0644]
docs/clang-tidy/checks/cppcoreguidelines-pro-bounds-array-to-pointer-decay.rst [new file with mode: 0644]
docs/clang-tidy/checks/cppcoreguidelines-pro-bounds-constant-array-index.rst [new file with mode: 0644]
docs/clang-tidy/checks/cppcoreguidelines-pro-bounds-pointer-arithmetic.rst [new file with mode: 0644]
docs/clang-tidy/checks/cppcoreguidelines-pro-type-const-cast.rst [new file with mode: 0644]
docs/clang-tidy/checks/cppcoreguidelines-pro-type-cstyle-cast.rst [new file with mode: 0644]
docs/clang-tidy/checks/cppcoreguidelines-pro-type-member-init.rst [new file with mode: 0644]
docs/clang-tidy/checks/cppcoreguidelines-pro-type-reinterpret-cast.rst [new file with mode: 0644]
docs/clang-tidy/checks/cppcoreguidelines-pro-type-static-cast-downcast.rst [new file with mode: 0644]
docs/clang-tidy/checks/cppcoreguidelines-pro-type-union-access.rst [new file with mode: 0644]
docs/clang-tidy/checks/cppcoreguidelines-pro-type-vararg.rst [new file with mode: 0644]
docs/clang-tidy/checks/cppcoreguidelines-slicing.rst [new file with mode: 0644]
docs/clang-tidy/checks/cppcoreguidelines-special-member-functions.rst [new file with mode: 0644]
docs/clang-tidy/checks/google-build-explicit-make-pair.rst [new file with mode: 0644]
docs/clang-tidy/checks/google-build-namespaces.rst [new file with mode: 0644]
docs/clang-tidy/checks/google-build-using-namespace.rst [new file with mode: 0644]
docs/clang-tidy/checks/google-default-arguments.rst [new file with mode: 0644]
docs/clang-tidy/checks/google-explicit-constructor.rst [new file with mode: 0644]
docs/clang-tidy/checks/google-global-names-in-headers.rst [new file with mode: 0644]
docs/clang-tidy/checks/google-readability-braces-around-statements.rst [new file with mode: 0644]
docs/clang-tidy/checks/google-readability-casting.rst [new file with mode: 0644]
docs/clang-tidy/checks/google-readability-function-size.rst [new file with mode: 0644]
docs/clang-tidy/checks/google-readability-namespace-comments.rst [new file with mode: 0644]
docs/clang-tidy/checks/google-readability-redundant-smartptr-get.rst [new file with mode: 0644]
docs/clang-tidy/checks/google-readability-todo.rst [new file with mode: 0644]
docs/clang-tidy/checks/google-runtime-int.rst [new file with mode: 0644]
docs/clang-tidy/checks/google-runtime-member-string-references.rst [new file with mode: 0644]
docs/clang-tidy/checks/google-runtime-operator.rst [new file with mode: 0644]
docs/clang-tidy/checks/google-runtime-references.rst [new file with mode: 0644]
docs/clang-tidy/checks/hicpp-explicit-conversions.rst [new file with mode: 0644]
docs/clang-tidy/checks/hicpp-function-size.rst [new file with mode: 0644]
docs/clang-tidy/checks/hicpp-invalid-access-moved.rst [new file with mode: 0644]
docs/clang-tidy/checks/hicpp-member-init.rst [new file with mode: 0644]
docs/clang-tidy/checks/hicpp-named-parameter.rst [new file with mode: 0644]
docs/clang-tidy/checks/hicpp-new-delete-operators.rst [new file with mode: 0644]
docs/clang-tidy/checks/hicpp-no-assembler.rst [new file with mode: 0644]
docs/clang-tidy/checks/hicpp-noexcept-move.rst [new file with mode: 0644]
docs/clang-tidy/checks/hicpp-special-member-functions.rst [new file with mode: 0644]
docs/clang-tidy/checks/hicpp-undelegated-constructor.rst [new file with mode: 0644]
docs/clang-tidy/checks/hicpp-use-equals-default.rst [new file with mode: 0644]
docs/clang-tidy/checks/hicpp-use-equals-delete.rst [new file with mode: 0644]
docs/clang-tidy/checks/hicpp-use-override.rst [new file with mode: 0644]
docs/clang-tidy/checks/list.rst [new file with mode: 0644]
docs/clang-tidy/checks/llvm-header-guard.rst [new file with mode: 0644]
docs/clang-tidy/checks/llvm-include-order.rst [new file with mode: 0644]
docs/clang-tidy/checks/llvm-namespace-comment.rst [new file with mode: 0644]
docs/clang-tidy/checks/llvm-twine-local.rst [new file with mode: 0644]
docs/clang-tidy/checks/misc-argument-comment.rst [new file with mode: 0644]
docs/clang-tidy/checks/misc-assert-side-effect.rst [new file with mode: 0644]
docs/clang-tidy/checks/misc-bool-pointer-implicit-conversion.rst [new file with mode: 0644]
docs/clang-tidy/checks/misc-dangling-handle.rst [new file with mode: 0644]
docs/clang-tidy/checks/misc-definitions-in-headers.rst [new file with mode: 0644]
docs/clang-tidy/checks/misc-fold-init-type.rst [new file with mode: 0644]
docs/clang-tidy/checks/misc-forward-declaration-namespace.rst [new file with mode: 0644]
docs/clang-tidy/checks/misc-forwarding-reference-overload.rst [new file with mode: 0644]
docs/clang-tidy/checks/misc-inaccurate-erase.rst [new file with mode: 0644]
docs/clang-tidy/checks/misc-incorrect-roundings.rst [new file with mode: 0644]
docs/clang-tidy/checks/misc-inefficient-algorithm.rst [new file with mode: 0644]
docs/clang-tidy/checks/misc-lambda-function-name.rst [new file with mode: 0644]
docs/clang-tidy/checks/misc-macro-parentheses.rst [new file with mode: 0644]
docs/clang-tidy/checks/misc-macro-repeated-side-effects.rst [new file with mode: 0644]
docs/clang-tidy/checks/misc-misplaced-const.rst [new file with mode: 0644]
docs/clang-tidy/checks/misc-misplaced-widening-cast.rst [new file with mode: 0644]
docs/clang-tidy/checks/misc-move-const-arg.rst [new file with mode: 0644]
docs/clang-tidy/checks/misc-move-constructor-init.rst [new file with mode: 0644]
docs/clang-tidy/checks/misc-move-forwarding-reference.rst [new file with mode: 0644]
docs/clang-tidy/checks/misc-multiple-statement-macro.rst [new file with mode: 0644]
docs/clang-tidy/checks/misc-new-delete-overloads.rst [new file with mode: 0644]
docs/clang-tidy/checks/misc-noexcept-move-constructor.rst [new file with mode: 0644]
docs/clang-tidy/checks/misc-non-copyable-objects.rst [new file with mode: 0644]
docs/clang-tidy/checks/misc-redundant-expression.rst [new file with mode: 0644]
docs/clang-tidy/checks/misc-sizeof-container.rst [new file with mode: 0644]
docs/clang-tidy/checks/misc-sizeof-expression.rst [new file with mode: 0644]
docs/clang-tidy/checks/misc-static-assert.rst [new file with mode: 0644]
docs/clang-tidy/checks/misc-string-compare.rst [new file with mode: 0644]
docs/clang-tidy/checks/misc-string-constructor.rst [new file with mode: 0644]
docs/clang-tidy/checks/misc-string-integer-assignment.rst [new file with mode: 0644]
docs/clang-tidy/checks/misc-string-literal-with-embedded-nul.rst [new file with mode: 0644]
docs/clang-tidy/checks/misc-suspicious-enum-usage.rst [new file with mode: 0644]
docs/clang-tidy/checks/misc-suspicious-missing-comma.rst [new file with mode: 0644]
docs/clang-tidy/checks/misc-suspicious-semicolon.rst [new file with mode: 0644]
docs/clang-tidy/checks/misc-suspicious-string-compare.rst [new file with mode: 0644]
docs/clang-tidy/checks/misc-swapped-arguments.rst [new file with mode: 0644]
docs/clang-tidy/checks/misc-throw-by-value-catch-by-reference.rst [new file with mode: 0644]
docs/clang-tidy/checks/misc-unconventional-assign-operator.rst [new file with mode: 0644]
docs/clang-tidy/checks/misc-undelegated-constructor.rst [new file with mode: 0644]
docs/clang-tidy/checks/misc-uniqueptr-reset-release.rst [new file with mode: 0644]
docs/clang-tidy/checks/misc-unused-alias-decls.rst [new file with mode: 0644]
docs/clang-tidy/checks/misc-unused-parameters.rst [new file with mode: 0644]
docs/clang-tidy/checks/misc-unused-raii.rst [new file with mode: 0644]
docs/clang-tidy/checks/misc-unused-using-decls.rst [new file with mode: 0644]
docs/clang-tidy/checks/misc-use-after-move.rst [new file with mode: 0644]
docs/clang-tidy/checks/misc-virtual-near-miss.rst [new file with mode: 0644]
docs/clang-tidy/checks/modernize-avoid-bind.rst [new file with mode: 0644]
docs/clang-tidy/checks/modernize-deprecated-headers.rst [new file with mode: 0644]
docs/clang-tidy/checks/modernize-loop-convert.rst [new file with mode: 0644]
docs/clang-tidy/checks/modernize-make-shared.rst [new file with mode: 0644]
docs/clang-tidy/checks/modernize-make-unique.rst [new file with mode: 0644]
docs/clang-tidy/checks/modernize-pass-by-value.rst [new file with mode: 0644]
docs/clang-tidy/checks/modernize-raw-string-literal.rst [new file with mode: 0644]
docs/clang-tidy/checks/modernize-redundant-void-arg.rst [new file with mode: 0644]
docs/clang-tidy/checks/modernize-replace-auto-ptr.rst [new file with mode: 0644]
docs/clang-tidy/checks/modernize-replace-random-shuffle.rst [new file with mode: 0644]
docs/clang-tidy/checks/modernize-return-braced-init-list.rst [new file with mode: 0644]
docs/clang-tidy/checks/modernize-shrink-to-fit.rst [new file with mode: 0644]
docs/clang-tidy/checks/modernize-unary-static-assert.rst [new file with mode: 0644]
docs/clang-tidy/checks/modernize-use-auto.rst [new file with mode: 0644]
docs/clang-tidy/checks/modernize-use-bool-literals.rst [new file with mode: 0644]
docs/clang-tidy/checks/modernize-use-default-member-init.rst [new file with mode: 0644]
docs/clang-tidy/checks/modernize-use-default.rst [new file with mode: 0644]
docs/clang-tidy/checks/modernize-use-emplace.rst [new file with mode: 0644]
docs/clang-tidy/checks/modernize-use-equals-default.rst [new file with mode: 0644]
docs/clang-tidy/checks/modernize-use-equals-delete.rst [new file with mode: 0644]
docs/clang-tidy/checks/modernize-use-noexcept.rst [new file with mode: 0644]
docs/clang-tidy/checks/modernize-use-nullptr.rst [new file with mode: 0644]
docs/clang-tidy/checks/modernize-use-override.rst [new file with mode: 0644]
docs/clang-tidy/checks/modernize-use-transparent-functors.rst [new file with mode: 0644]
docs/clang-tidy/checks/modernize-use-using.rst [new file with mode: 0644]
docs/clang-tidy/checks/mpi-buffer-deref.rst [new file with mode: 0644]
docs/clang-tidy/checks/mpi-type-mismatch.rst [new file with mode: 0644]
docs/clang-tidy/checks/performance-faster-string-find.rst [new file with mode: 0644]
docs/clang-tidy/checks/performance-for-range-copy.rst [new file with mode: 0644]
docs/clang-tidy/checks/performance-implicit-cast-in-loop.rst [new file with mode: 0644]
docs/clang-tidy/checks/performance-inefficient-string-concatenation.rst [new file with mode: 0644]
docs/clang-tidy/checks/performance-inefficient-vector-operation.rst [new file with mode: 0644]
docs/clang-tidy/checks/performance-type-promotion-in-math-fn.rst [new file with mode: 0644]
docs/clang-tidy/checks/performance-unnecessary-copy-initialization.rst [new file with mode: 0644]
docs/clang-tidy/checks/performance-unnecessary-value-param.rst [new file with mode: 0644]
docs/clang-tidy/checks/readability-avoid-const-params-in-decls.rst [new file with mode: 0644]
docs/clang-tidy/checks/readability-braces-around-statements.rst [new file with mode: 0644]
docs/clang-tidy/checks/readability-container-size-empty.rst [new file with mode: 0644]
docs/clang-tidy/checks/readability-delete-null-pointer.rst [new file with mode: 0644]
docs/clang-tidy/checks/readability-deleted-default.rst [new file with mode: 0644]
docs/clang-tidy/checks/readability-else-after-return.rst [new file with mode: 0644]
docs/clang-tidy/checks/readability-function-size.rst [new file with mode: 0644]
docs/clang-tidy/checks/readability-identifier-naming.rst [new file with mode: 0644]
docs/clang-tidy/checks/readability-implicit-bool-cast.rst [new file with mode: 0644]
docs/clang-tidy/checks/readability-inconsistent-declaration-parameter-name.rst [new file with mode: 0644]
docs/clang-tidy/checks/readability-misleading-indentation.rst [new file with mode: 0644]
docs/clang-tidy/checks/readability-misplaced-array-index.rst [new file with mode: 0644]
docs/clang-tidy/checks/readability-named-parameter.rst [new file with mode: 0644]
docs/clang-tidy/checks/readability-non-const-parameter.rst [new file with mode: 0644]
docs/clang-tidy/checks/readability-redundant-control-flow.rst [new file with mode: 0644]
docs/clang-tidy/checks/readability-redundant-declaration.rst [new file with mode: 0644]
docs/clang-tidy/checks/readability-redundant-function-ptr-dereference.rst [new file with mode: 0644]
docs/clang-tidy/checks/readability-redundant-member-init.rst [new file with mode: 0644]
docs/clang-tidy/checks/readability-redundant-smartptr-get.rst [new file with mode: 0644]
docs/clang-tidy/checks/readability-redundant-string-cstr.rst [new file with mode: 0644]
docs/clang-tidy/checks/readability-redundant-string-init.rst [new file with mode: 0644]
docs/clang-tidy/checks/readability-simplify-boolean-expr.rst [new file with mode: 0644]
docs/clang-tidy/checks/readability-static-definition-in-anonymous-namespace.rst [new file with mode: 0644]
docs/clang-tidy/checks/readability-uniqueptr-delete-release.rst [new file with mode: 0644]
docs/clang-tidy/index.rst [new file with mode: 0644]
docs/clangd.rst [new file with mode: 0644]
docs/conf.py [new file with mode: 0644]
docs/cpp11-migrate.rst [new file with mode: 0644]
docs/doxygen-mainpage.dox [new file with mode: 0644]
docs/doxygen.cfg.in [new file with mode: 0644]
docs/include-fixer.rst [new file with mode: 0644]
docs/index.rst [new file with mode: 0644]
docs/make.bat [new file with mode: 0644]
docs/modularize.rst [new file with mode: 0644]
docs/pp-trace.rst [new file with mode: 0644]
include-fixer/CMakeLists.txt [new file with mode: 0644]
include-fixer/FuzzySymbolIndex.cpp [new file with mode: 0644]
include-fixer/FuzzySymbolIndex.h [new file with mode: 0644]
include-fixer/InMemorySymbolIndex.cpp [new file with mode: 0644]
include-fixer/InMemorySymbolIndex.h [new file with mode: 0644]
include-fixer/IncludeFixer.cpp [new file with mode: 0644]
include-fixer/IncludeFixer.h [new file with mode: 0644]
include-fixer/IncludeFixerContext.cpp [new file with mode: 0644]
include-fixer/IncludeFixerContext.h [new file with mode: 0644]
include-fixer/SymbolIndex.h [new file with mode: 0644]
include-fixer/SymbolIndexManager.cpp [new file with mode: 0644]
include-fixer/SymbolIndexManager.h [new file with mode: 0644]
include-fixer/YamlSymbolIndex.cpp [new file with mode: 0644]
include-fixer/YamlSymbolIndex.h [new file with mode: 0644]
include-fixer/find-all-symbols/CMakeLists.txt [new file with mode: 0644]
include-fixer/find-all-symbols/FindAllMacros.cpp [new file with mode: 0644]
include-fixer/find-all-symbols/FindAllMacros.h [new file with mode: 0644]
include-fixer/find-all-symbols/FindAllSymbols.cpp [new file with mode: 0644]
include-fixer/find-all-symbols/FindAllSymbols.h [new file with mode: 0644]
include-fixer/find-all-symbols/FindAllSymbolsAction.cpp [new file with mode: 0644]
include-fixer/find-all-symbols/FindAllSymbolsAction.h [new file with mode: 0644]
include-fixer/find-all-symbols/HeaderMapCollector.cpp [new file with mode: 0644]
include-fixer/find-all-symbols/HeaderMapCollector.h [new file with mode: 0644]
include-fixer/find-all-symbols/PathConfig.cpp [new file with mode: 0644]
include-fixer/find-all-symbols/PathConfig.h [new file with mode: 0644]
include-fixer/find-all-symbols/PragmaCommentHandler.cpp [new file with mode: 0644]
include-fixer/find-all-symbols/PragmaCommentHandler.h [new file with mode: 0644]
include-fixer/find-all-symbols/STLPostfixHeaderMap.cpp [new file with mode: 0644]
include-fixer/find-all-symbols/STLPostfixHeaderMap.h [new file with mode: 0644]
include-fixer/find-all-symbols/SymbolInfo.cpp [new file with mode: 0644]
include-fixer/find-all-symbols/SymbolInfo.h [new file with mode: 0644]
include-fixer/find-all-symbols/SymbolReporter.h [new file with mode: 0644]
include-fixer/find-all-symbols/tool/CMakeLists.txt [new file with mode: 0644]
include-fixer/find-all-symbols/tool/FindAllSymbolsMain.cpp [new file with mode: 0644]
include-fixer/find-all-symbols/tool/run-find-all-symbols.py [new file with mode: 0755]
include-fixer/plugin/CMakeLists.txt [new file with mode: 0644]
include-fixer/plugin/IncludeFixerPlugin.cpp [new file with mode: 0644]
include-fixer/tool/CMakeLists.txt [new file with mode: 0644]
include-fixer/tool/ClangIncludeFixer.cpp [new file with mode: 0644]
include-fixer/tool/clang-include-fixer-test.el [new file with mode: 0644]
include-fixer/tool/clang-include-fixer.el [new file with mode: 0644]
include-fixer/tool/clang-include-fixer.py [new file with mode: 0644]
modularize/CMakeLists.txt [new file with mode: 0644]
modularize/CoverageChecker.cpp [new file with mode: 0644]
modularize/CoverageChecker.h [new file with mode: 0644]
modularize/Modularize.cpp [new file with mode: 0644]
modularize/Modularize.h [new file with mode: 0644]
modularize/ModularizeUtilities.cpp [new file with mode: 0644]
modularize/ModularizeUtilities.h [new file with mode: 0644]
modularize/ModuleAssistant.cpp [new file with mode: 0644]
modularize/PreprocessorTracker.cpp [new file with mode: 0644]
modularize/PreprocessorTracker.h [new file with mode: 0644]
pp-trace/CMakeLists.txt [new file with mode: 0644]
pp-trace/PPCallbacksTracker.cpp [new file with mode: 0644]
pp-trace/PPCallbacksTracker.h [new file with mode: 0644]
pp-trace/PPTrace.cpp [new file with mode: 0644]
test/.clang-format [new file with mode: 0644]
test/CMakeLists.txt [new file with mode: 0644]
test/Unit/lit.cfg [new file with mode: 0644]
test/Unit/lit.site.cfg.in [new file with mode: 0644]
test/change-namespace/Inputs/fake-std.h [new file with mode: 0644]
test/change-namespace/lambda-function.cpp [new file with mode: 0644]
test/change-namespace/macro.cpp [new file with mode: 0644]
test/change-namespace/simple-move.cpp [new file with mode: 0644]
test/change-namespace/white-list.cpp [new file with mode: 0644]
test/clang-apply-replacements/ClangRenameClassReplacements.cpp [new file with mode: 0644]
test/clang-apply-replacements/Inputs/basic/basic.h [new file with mode: 0644]
test/clang-apply-replacements/Inputs/basic/file1.yaml [new file with mode: 0644]
test/clang-apply-replacements/Inputs/basic/file2.yaml [new file with mode: 0644]
test/clang-apply-replacements/Inputs/conflict/common.h [new file with mode: 0644]
test/clang-apply-replacements/Inputs/conflict/expected.txt [new file with mode: 0644]
test/clang-apply-replacements/Inputs/conflict/file1.yaml [new file with mode: 0644]
test/clang-apply-replacements/Inputs/conflict/file2.yaml [new file with mode: 0644]
test/clang-apply-replacements/Inputs/conflict/file3.yaml [new file with mode: 0644]
test/clang-apply-replacements/Inputs/crlf/crlf.cpp [new file with mode: 0644]
test/clang-apply-replacements/Inputs/crlf/crlf.cpp.expected [new file with mode: 0644]
test/clang-apply-replacements/Inputs/crlf/file1.yaml [new file with mode: 0644]
test/clang-apply-replacements/Inputs/format/no.cpp [new file with mode: 0644]
test/clang-apply-replacements/Inputs/format/no.yaml [new file with mode: 0644]
test/clang-apply-replacements/Inputs/format/yes.cpp [new file with mode: 0644]
test/clang-apply-replacements/Inputs/format/yes.yaml [new file with mode: 0644]
test/clang-apply-replacements/basic.cpp [new file with mode: 0644]
test/clang-apply-replacements/conflict.cpp [new file with mode: 0644]
test/clang-apply-replacements/crlf.cpp [new file with mode: 0644]
test/clang-apply-replacements/format.cpp [new file with mode: 0644]
test/clang-move/Inputs/database_template.json [new file with mode: 0644]
test/clang-move/Inputs/enum.h [new file with mode: 0644]
test/clang-move/Inputs/function_test.cpp [new file with mode: 0644]
test/clang-move/Inputs/function_test.h [new file with mode: 0644]
test/clang-move/Inputs/helper_decls_test.cpp [new file with mode: 0644]
test/clang-move/Inputs/helper_decls_test.h [new file with mode: 0644]
test/clang-move/Inputs/macro_helper_test.cpp [new file with mode: 0644]
test/clang-move/Inputs/macro_helper_test.h [new file with mode: 0644]
test/clang-move/Inputs/multiple_class_test.cpp [new file with mode: 0644]
test/clang-move/Inputs/multiple_class_test.h [new file with mode: 0644]
test/clang-move/Inputs/template_class_test.cpp [new file with mode: 0644]
test/clang-move/Inputs/template_class_test.h [new file with mode: 0644]
test/clang-move/Inputs/test.cpp [new file with mode: 0644]
test/clang-move/Inputs/test.h [new file with mode: 0644]
test/clang-move/Inputs/type_alias.h [new file with mode: 0644]
test/clang-move/Inputs/var_test.cpp [new file with mode: 0644]
test/clang-move/Inputs/var_test.h [new file with mode: 0644]
test/clang-move/move-class.cpp [new file with mode: 0644]
test/clang-move/move-enum-decl.cpp [new file with mode: 0644]
test/clang-move/move-function.cpp [new file with mode: 0644]
test/clang-move/move-multiple-classes.cpp [new file with mode: 0644]
test/clang-move/move-template-class.cpp [new file with mode: 0644]
test/clang-move/move-type-alias.cpp [new file with mode: 0644]
test/clang-move/move-used-helper-decls.cpp [new file with mode: 0644]
test/clang-move/move-var.cpp [new file with mode: 0644]
test/clang-move/no-move-macro-helpers.cpp [new file with mode: 0644]
test/clang-query/Inputs/foo.script [new file with mode: 0644]
test/clang-query/errors.c [new file with mode: 0644]
test/clang-query/function-decl.c [new file with mode: 0644]
test/clang-reorder-fields/AggregatePartialInitialization.cpp [new file with mode: 0644]
test/clang-reorder-fields/CStructAmbiguousName.cpp [new file with mode: 0644]
test/clang-reorder-fields/CStructFieldsOrder.cpp [new file with mode: 0644]
test/clang-reorder-fields/ClassDifferentFieldsAccesses.cpp [new file with mode: 0644]
test/clang-reorder-fields/ClassMixedInitialization.cpp [new file with mode: 0644]
test/clang-reorder-fields/ClassSimpleCtor.cpp [new file with mode: 0644]
test/clang-tidy/Inputs/Headers/a.h [new file with mode: 0644]
test/clang-tidy/Inputs/Headers/b.h [new file with mode: 0644]
test/clang-tidy/Inputs/Headers/clang-c/c.h [new file with mode: 0644]
test/clang-tidy/Inputs/Headers/clang/b.h [new file with mode: 0644]
test/clang-tidy/Inputs/Headers/cross-file-a.h [new file with mode: 0644]
test/clang-tidy/Inputs/Headers/cross-file-b.h [new file with mode: 0644]
test/clang-tidy/Inputs/Headers/cross-file-c.h [new file with mode: 0644]
test/clang-tidy/Inputs/Headers/gtest/foo.h [new file with mode: 0644]
test/clang-tidy/Inputs/Headers/i.h [new file with mode: 0644]
test/clang-tidy/Inputs/Headers/j.h [new file with mode: 0644]
test/clang-tidy/Inputs/Headers/llvm-c/d.h [new file with mode: 0644]
test/clang-tidy/Inputs/Headers/llvm/a.h [new file with mode: 0644]
test/clang-tidy/Inputs/Headers/s.h [new file with mode: 0644]
test/clang-tidy/Inputs/Headers/system-header-simulation.h [new file with mode: 0644]
test/clang-tidy/Inputs/compilation-database/template.json [new file with mode: 0644]
test/clang-tidy/Inputs/config-files/.clang-tidy [new file with mode: 0644]
test/clang-tidy/Inputs/config-files/1/.clang-tidy [new file with mode: 0644]
test/clang-tidy/Inputs/explain-config/.clang-tidy [new file with mode: 0644]
test/clang-tidy/Inputs/file-filter/header1.h [new file with mode: 0644]
test/clang-tidy/Inputs/file-filter/header2.h [new file with mode: 0644]
test/clang-tidy/Inputs/file-filter/system/system-header.h [new file with mode: 0644]
test/clang-tidy/Inputs/google-namespaces.h [new file with mode: 0644]
test/clang-tidy/Inputs/line-filter/header1.h [new file with mode: 0644]
test/clang-tidy/Inputs/line-filter/header2.h [new file with mode: 0644]
test/clang-tidy/Inputs/line-filter/header3.h [new file with mode: 0644]
test/clang-tidy/Inputs/modernize-deprecated-headers/assert.h [new file with mode: 0644]
test/clang-tidy/Inputs/modernize-deprecated-headers/complex.h [new file with mode: 0644]
test/clang-tidy/Inputs/modernize-deprecated-headers/ctype.h [new file with mode: 0644]
test/clang-tidy/Inputs/modernize-deprecated-headers/errno.h [new file with mode: 0644]
test/clang-tidy/Inputs/modernize-deprecated-headers/fenv.h [new file with mode: 0644]
test/clang-tidy/Inputs/modernize-deprecated-headers/float.h [new file with mode: 0644]
test/clang-tidy/Inputs/modernize-deprecated-headers/inttypes.h [new file with mode: 0644]
test/clang-tidy/Inputs/modernize-deprecated-headers/iso646.h [new file with mode: 0644]
test/clang-tidy/Inputs/modernize-deprecated-headers/limits.h [new file with mode: 0644]
test/clang-tidy/Inputs/modernize-deprecated-headers/locale.h [new file with mode: 0644]
test/clang-tidy/Inputs/modernize-deprecated-headers/math.h [new file with mode: 0644]
test/clang-tidy/Inputs/modernize-deprecated-headers/setjmp.h [new file with mode: 0644]
test/clang-tidy/Inputs/modernize-deprecated-headers/signal.h [new file with mode: 0644]
test/clang-tidy/Inputs/modernize-deprecated-headers/stdalign.h [new file with mode: 0644]
test/clang-tidy/Inputs/modernize-deprecated-headers/stdarg.h [new file with mode: 0644]
test/clang-tidy/Inputs/modernize-deprecated-headers/stdbool.h [new file with mode: 0644]
test/clang-tidy/Inputs/modernize-deprecated-headers/stddef.h [new file with mode: 0644]
test/clang-tidy/Inputs/modernize-deprecated-headers/stdint.h [new file with mode: 0644]
test/clang-tidy/Inputs/modernize-deprecated-headers/stdio.h [new file with mode: 0644]
test/clang-tidy/Inputs/modernize-deprecated-headers/stdlib.h [new file with mode: 0644]
test/clang-tidy/Inputs/modernize-deprecated-headers/string.h [new file with mode: 0644]
test/clang-tidy/Inputs/modernize-deprecated-headers/tgmath.h [new file with mode: 0644]
test/clang-tidy/Inputs/modernize-deprecated-headers/time.h [new file with mode: 0644]
test/clang-tidy/Inputs/modernize-deprecated-headers/uchar.h [new file with mode: 0644]
test/clang-tidy/Inputs/modernize-deprecated-headers/wchar.h [new file with mode: 0644]
test/clang-tidy/Inputs/modernize-deprecated-headers/wctype.h [new file with mode: 0644]
test/clang-tidy/Inputs/modernize-loop-convert/structures.h [new file with mode: 0644]
test/clang-tidy/Inputs/modernize-pass-by-value/header-with-fix.h [new file with mode: 0644]
test/clang-tidy/Inputs/modernize-pass-by-value/header.h [new file with mode: 0644]
test/clang-tidy/Inputs/modernize-replace-auto-ptr/memory.h [new file with mode: 0644]
test/clang-tidy/Inputs/modernize-smart-ptr/shared_ptr.h [new file with mode: 0644]
test/clang-tidy/Inputs/modernize-smart-ptr/unique_ptr.h [new file with mode: 0644]
test/clang-tidy/Inputs/modernize-use-auto/containers.h [new file with mode: 0644]
test/clang-tidy/Inputs/mpi-type-mismatch/mpimock.h [new file with mode: 0644]
test/clang-tidy/Inputs/nolint/trigger_warning.h [new file with mode: 0644]
test/clang-tidy/Inputs/overlapping/o.h [new file with mode: 0644]
test/clang-tidy/Inputs/performance-unnecessary-value-param/header-fixed.h [new file with mode: 0644]
test/clang-tidy/Inputs/performance-unnecessary-value-param/header.h [new file with mode: 0644]
test/clang-tidy/Inputs/readability-identifier-naming/system/system-header.h [new file with mode: 0644]
test/clang-tidy/Inputs/readability-identifier-naming/user-header.h [new file with mode: 0644]
test/clang-tidy/Inputs/unused-using-decls.h [new file with mode: 0644]
test/clang-tidy/android-cloexec-creat.cpp [new file with mode: 0644]
test/clang-tidy/android-cloexec-fopen.cpp [new file with mode: 0644]
test/clang-tidy/android-cloexec-open.cpp [new file with mode: 0644]
test/clang-tidy/android-cloexec-socket.cpp [new file with mode: 0644]
test/clang-tidy/basic.cpp [new file with mode: 0644]
test/clang-tidy/boost-use-to-string.cpp [new file with mode: 0644]
test/clang-tidy/bugprone-suspicious-memset-usage.cpp [new file with mode: 0644]
test/clang-tidy/bugprone-undefined-memory-manipulation.cpp [new file with mode: 0644]
test/clang-tidy/cert-dcl21-cpp.cpp [new file with mode: 0644]
test/clang-tidy/cert-dcl58-cpp.cpp [new file with mode: 0644]
test/clang-tidy/cert-env33-c.c [new file with mode: 0644]
test/clang-tidy/cert-err34-c.c [new file with mode: 0644]
test/clang-tidy/cert-err34-c.cpp [new file with mode: 0644]
test/clang-tidy/cert-flp30-c.c [new file with mode: 0644]
test/clang-tidy/cert-limited-randomness.c [new file with mode: 0644]
test/clang-tidy/cert-limited-randomness.cpp [new file with mode: 0644]
test/clang-tidy/cert-oop11-cpp.cpp [new file with mode: 0644]
test/clang-tidy/cert-setlongjmp.cpp [new file with mode: 0644]
test/clang-tidy/cert-static-object-exception.cpp [new file with mode: 0644]
test/clang-tidy/cert-throw-exception-type.cpp [new file with mode: 0644]
test/clang-tidy/cert-variadic-function-def.cpp [new file with mode: 0644]
test/clang-tidy/check_clang_tidy.py [new file with mode: 0755]
test/clang-tidy/clang-tidy-diff.cpp [new file with mode: 0644]
test/clang-tidy/clang-tidy-run-with-database.cpp [new file with mode: 0644]
test/clang-tidy/clean-up-code.cpp [new file with mode: 0644]
test/clang-tidy/config-files.cpp [new file with mode: 0644]
test/clang-tidy/cppcoreguidelines-interfaces-global-init.cpp [new file with mode: 0644]
test/clang-tidy/cppcoreguidelines-no-malloc-custom.cpp [new file with mode: 0644]
test/clang-tidy/cppcoreguidelines-no-malloc-no-functions.cpp [new file with mode: 0644]
test/clang-tidy/cppcoreguidelines-no-malloc.cpp [new file with mode: 0644]
test/clang-tidy/cppcoreguidelines-pro-bounds-array-to-pointer-decay.cpp [new file with mode: 0644]
test/clang-tidy/cppcoreguidelines-pro-bounds-constant-array-index-c++03.cpp [new file with mode: 0644]
test/clang-tidy/cppcoreguidelines-pro-bounds-constant-array-index-gslheader.cpp [new file with mode: 0644]
test/clang-tidy/cppcoreguidelines-pro-bounds-constant-array-index.cpp [new file with mode: 0644]
test/clang-tidy/cppcoreguidelines-pro-bounds-pointer-arithmetic.cpp [new file with mode: 0644]
test/clang-tidy/cppcoreguidelines-pro-type-const-cast.cpp [new file with mode: 0644]
test/clang-tidy/cppcoreguidelines-pro-type-cstyle-cast.cpp [new file with mode: 0644]
test/clang-tidy/cppcoreguidelines-pro-type-member-init-cxx98.cpp [new file with mode: 0644]
test/clang-tidy/cppcoreguidelines-pro-type-member-init-delayed.cpp [new file with mode: 0644]
test/clang-tidy/cppcoreguidelines-pro-type-member-init.cpp [new file with mode: 0644]
test/clang-tidy/cppcoreguidelines-pro-type-reinterpret-cast.cpp [new file with mode: 0644]
test/clang-tidy/cppcoreguidelines-pro-type-static-cast-downcast.cpp [new file with mode: 0644]
test/clang-tidy/cppcoreguidelines-pro-type-union-access.cpp [new file with mode: 0644]
test/clang-tidy/cppcoreguidelines-pro-type-vararg.cpp [new file with mode: 0644]
test/clang-tidy/cppcoreguidelines-slicing.cpp [new file with mode: 0644]
test/clang-tidy/cppcoreguidelines-special-member-functions-cxx-03.cpp [new file with mode: 0644]
test/clang-tidy/cppcoreguidelines-special-member-functions-relaxed.cpp [new file with mode: 0644]
test/clang-tidy/cppcoreguidelines-special-member-functions.cpp [new file with mode: 0644]
test/clang-tidy/custom-diagnostics.cpp [new file with mode: 0644]
test/clang-tidy/deduplication.cpp [new file with mode: 0644]
test/clang-tidy/diagnostic.cpp [new file with mode: 0644]
test/clang-tidy/explain-checks.cpp [new file with mode: 0644]
test/clang-tidy/extra-args.cpp [new file with mode: 0644]
test/clang-tidy/file-filter.cpp [new file with mode: 0644]
test/clang-tidy/fix-errors.cpp [new file with mode: 0644]
test/clang-tidy/fix.cpp [new file with mode: 0644]
test/clang-tidy/google-build-explicit-make-pair.cpp [new file with mode: 0644]
test/clang-tidy/google-default-arguments.cpp [new file with mode: 0644]
test/clang-tidy/google-explicit-constructor.cpp [new file with mode: 0644]
test/clang-tidy/google-module.cpp [new file with mode: 0644]
test/clang-tidy/google-namespaces.cpp [new file with mode: 0644]
test/clang-tidy/google-overloaded-unary-and.cpp [new file with mode: 0644]
test/clang-tidy/google-readability-casting.c [new file with mode: 0644]
test/clang-tidy/google-readability-casting.cpp [new file with mode: 0644]
test/clang-tidy/google-readability-namespace-comments.cpp [new file with mode: 0644]
test/clang-tidy/google-readability-todo.cpp [new file with mode: 0644]
test/clang-tidy/google-runtime-int-std.cpp [new file with mode: 0644]
test/clang-tidy/google-runtime-int.c [new file with mode: 0644]
test/clang-tidy/google-runtime-int.cpp [new file with mode: 0644]
test/clang-tidy/google-runtime-member-string-references.cpp [new file with mode: 0644]
test/clang-tidy/google-runtime-references.cpp [new file with mode: 0644]
test/clang-tidy/hicpp-no-assembler.cpp [new file with mode: 0644]
test/clang-tidy/line-filter.cpp [new file with mode: 0644]
test/clang-tidy/list-checks.cpp [new file with mode: 0644]
test/clang-tidy/llvm-include-order.cpp [new file with mode: 0644]
test/clang-tidy/llvm-twine-local.cpp [new file with mode: 0644]
test/clang-tidy/macros.cpp [new file with mode: 0644]
test/clang-tidy/misc-argument-comment-gmock.cpp [new file with mode: 0644]
test/clang-tidy/misc-argument-comment-strict.cpp [new file with mode: 0644]
test/clang-tidy/misc-argument-comment.cpp [new file with mode: 0644]
test/clang-tidy/misc-assert-side-effect.cpp [new file with mode: 0644]
test/clang-tidy/misc-bool-pointer-implicit-conversion.cpp [new file with mode: 0644]
test/clang-tidy/misc-dangling-handle.cpp [new file with mode: 0644]
test/clang-tidy/misc-definitions-in-headers-1z.hpp [new file with mode: 0644]
test/clang-tidy/misc-definitions-in-headers.hpp [new file with mode: 0644]
test/clang-tidy/misc-fold-init-type.cpp [new file with mode: 0644]
test/clang-tidy/misc-forward-declaration-namespace.cpp [new file with mode: 0644]
test/clang-tidy/misc-forwarding-reference-overload.cpp [new file with mode: 0644]
test/clang-tidy/misc-inaccurate-erase.cpp [new file with mode: 0644]
test/clang-tidy/misc-incorrect-roundings.cpp [new file with mode: 0644]
test/clang-tidy/misc-inefficient-algorithm.cpp [new file with mode: 0644]
test/clang-tidy/misc-lambda-function-name.cpp [new file with mode: 0644]
test/clang-tidy/misc-macro-parentheses-cmdline.cpp [new file with mode: 0644]
test/clang-tidy/misc-macro-parentheses.cpp [new file with mode: 0644]
test/clang-tidy/misc-macro-repeated-side-effects.c [new file with mode: 0644]
test/clang-tidy/misc-misplaced-const.c [new file with mode: 0644]
test/clang-tidy/misc-misplaced-const.cpp [new file with mode: 0644]
test/clang-tidy/misc-misplaced-widening-cast-explicit-only.cpp [new file with mode: 0644]
test/clang-tidy/misc-misplaced-widening-cast-implicit-enabled.cpp [new file with mode: 0644]
test/clang-tidy/misc-move-const-arg.cpp [new file with mode: 0644]
test/clang-tidy/misc-move-constructor-init.cpp [new file with mode: 0644]
test/clang-tidy/misc-move-forwarding-reference.cpp [new file with mode: 0644]
test/clang-tidy/misc-multiple-statement-macro.cpp [new file with mode: 0644]
test/clang-tidy/misc-new-delete-overloads-sized-dealloc.cpp [new file with mode: 0644]
test/clang-tidy/misc-new-delete-overloads.cpp [new file with mode: 0644]
test/clang-tidy/misc-noexcept-move-constructor.cpp [new file with mode: 0644]
test/clang-tidy/misc-non-copyable-objects.c [new file with mode: 0644]
test/clang-tidy/misc-non-copyable-objects.cpp [new file with mode: 0644]
test/clang-tidy/misc-redundant-expression.cpp [new file with mode: 0644]
test/clang-tidy/misc-sizeof-container.cpp [new file with mode: 0644]
test/clang-tidy/misc-sizeof-expression.cpp [new file with mode: 0644]
test/clang-tidy/misc-static-assert.c [new file with mode: 0644]
test/clang-tidy/misc-static-assert.cpp [new file with mode: 0644]
test/clang-tidy/misc-string-compare.cpp [new file with mode: 0644]
test/clang-tidy/misc-string-constructor.cpp [new file with mode: 0644]
test/clang-tidy/misc-string-integer-assignment.cpp [new file with mode: 0644]
test/clang-tidy/misc-string-literal-with-embedded-nul.cpp [new file with mode: 0644]
test/clang-tidy/misc-suspicious-enum-usage-strict.cpp [new file with mode: 0644]
test/clang-tidy/misc-suspicious-enum-usage.cpp [new file with mode: 0644]
test/clang-tidy/misc-suspicious-missing-comma.cpp [new file with mode: 0644]
test/clang-tidy/misc-suspicious-semicolon-fail.cpp [new file with mode: 0644]
test/clang-tidy/misc-suspicious-semicolon.cpp [new file with mode: 0644]
test/clang-tidy/misc-suspicious-string-compare.c [new file with mode: 0644]
test/clang-tidy/misc-suspicious-string-compare.cpp [new file with mode: 0644]
test/clang-tidy/misc-swapped-arguments.cpp [new file with mode: 0644]
test/clang-tidy/misc-throw-by-value-catch-by-reference.cpp [new file with mode: 0644]
test/clang-tidy/misc-unconventional-assign-operator.cpp [new file with mode: 0644]
test/clang-tidy/misc-undelegated-constructor-cxx98.cpp [new file with mode: 0644]
test/clang-tidy/misc-undelegated-constructor.cpp [new file with mode: 0644]
test/clang-tidy/misc-uniqueptr-reset-release.cpp [new file with mode: 0644]
test/clang-tidy/misc-unused-alias-decls.cpp [new file with mode: 0644]
test/clang-tidy/misc-unused-parameters.c [new file with mode: 0644]
test/clang-tidy/misc-unused-parameters.cpp [new file with mode: 0644]
test/clang-tidy/misc-unused-raii.cpp [new file with mode: 0644]
test/clang-tidy/misc-unused-using-decls-errors.cpp [new file with mode: 0644]
test/clang-tidy/misc-unused-using-decls.cpp [new file with mode: 0644]
test/clang-tidy/misc-use-after-move.cpp [new file with mode: 0644]
test/clang-tidy/misc-virtual-near-miss.cpp [new file with mode: 0644]
test/clang-tidy/modernize-avoid-bind.cpp [new file with mode: 0644]
test/clang-tidy/modernize-deprecated-headers-cxx03.cpp [new file with mode: 0644]
test/clang-tidy/modernize-deprecated-headers-cxx11.cpp [new file with mode: 0644]
test/clang-tidy/modernize-loop-convert-assert-failure.cpp [new file with mode: 0644]
test/clang-tidy/modernize-loop-convert-basic.cpp [new file with mode: 0644]
test/clang-tidy/modernize-loop-convert-camelback.cpp [new file with mode: 0644]
test/clang-tidy/modernize-loop-convert-const.cpp [new file with mode: 0644]
test/clang-tidy/modernize-loop-convert-extra.cpp [new file with mode: 0644]
test/clang-tidy/modernize-loop-convert-lowercase.cpp [new file with mode: 0644]
test/clang-tidy/modernize-loop-convert-negative.cpp [new file with mode: 0644]
test/clang-tidy/modernize-loop-convert-uppercase.cpp [new file with mode: 0644]
test/clang-tidy/modernize-loop-convert.c [new file with mode: 0644]
test/clang-tidy/modernize-make-shared-header.cpp [new file with mode: 0644]
test/clang-tidy/modernize-make-shared.cpp [new file with mode: 0644]
test/clang-tidy/modernize-make-unique-header.cpp [new file with mode: 0644]
test/clang-tidy/modernize-make-unique.cpp [new file with mode: 0644]
test/clang-tidy/modernize-pass-by-value-header.cpp [new file with mode: 0644]
test/clang-tidy/modernize-pass-by-value-macro-header.cpp [new file with mode: 0644]
test/clang-tidy/modernize-pass-by-value-multi-fixes.cpp [new file with mode: 0644]
test/clang-tidy/modernize-pass-by-value.cpp [new file with mode: 0644]
test/clang-tidy/modernize-raw-string-literal-delimiter.cpp [new file with mode: 0644]
test/clang-tidy/modernize-raw-string-literal-replace-shorter.cpp [new file with mode: 0644]
test/clang-tidy/modernize-raw-string-literal.cpp [new file with mode: 0644]
test/clang-tidy/modernize-redundant-void-arg-delayed.cpp [new file with mode: 0644]
test/clang-tidy/modernize-redundant-void-arg.c [new file with mode: 0644]
test/clang-tidy/modernize-redundant-void-arg.cpp [new file with mode: 0644]
test/clang-tidy/modernize-replace-auto-ptr.cpp [new file with mode: 0644]
test/clang-tidy/modernize-replace-random-shuffle.cpp [new file with mode: 0644]
test/clang-tidy/modernize-return-braced-init-list.cpp [new file with mode: 0644]
test/clang-tidy/modernize-shrink-to-fit.cpp [new file with mode: 0644]
test/clang-tidy/modernize-unary-static-assert.cpp [new file with mode: 0644]
test/clang-tidy/modernize-use-auto-cast-remove-stars.cpp [new file with mode: 0644]
test/clang-tidy/modernize-use-auto-cast.cpp [new file with mode: 0644]
test/clang-tidy/modernize-use-auto-iterator.cpp [new file with mode: 0644]
test/clang-tidy/modernize-use-auto-new-remove-stars.cpp [new file with mode: 0644]
test/clang-tidy/modernize-use-auto-new.cpp [new file with mode: 0644]
test/clang-tidy/modernize-use-bool-literals-ignore-macros.cpp [new file with mode: 0644]
test/clang-tidy/modernize-use-bool-literals.cpp [new file with mode: 0644]
test/clang-tidy/modernize-use-default-member-init-assignment.cpp [new file with mode: 0644]
test/clang-tidy/modernize-use-default-member-init-macros.cpp [new file with mode: 0644]
test/clang-tidy/modernize-use-default-member-init.cpp [new file with mode: 0644]
test/clang-tidy/modernize-use-emplace.cpp [new file with mode: 0644]
test/clang-tidy/modernize-use-equals-default-copy.cpp [new file with mode: 0644]
test/clang-tidy/modernize-use-equals-default-delayed.cpp [new file with mode: 0644]
test/clang-tidy/modernize-use-equals-default.cpp [new file with mode: 0644]
test/clang-tidy/modernize-use-equals-delete.cpp [new file with mode: 0644]
test/clang-tidy/modernize-use-noexcept-macro.cpp [new file with mode: 0644]
test/clang-tidy/modernize-use-noexcept-opt.cpp [new file with mode: 0644]
test/clang-tidy/modernize-use-noexcept.cpp [new file with mode: 0644]
test/clang-tidy/modernize-use-nullptr-basic.cpp [new file with mode: 0644]
test/clang-tidy/modernize-use-nullptr.c [new file with mode: 0644]
test/clang-tidy/modernize-use-nullptr.cpp [new file with mode: 0644]
test/clang-tidy/modernize-use-override-cxx98.cpp [new file with mode: 0644]
test/clang-tidy/modernize-use-override-ms.cpp [new file with mode: 0644]
test/clang-tidy/modernize-use-override.cpp [new file with mode: 0644]
test/clang-tidy/modernize-use-transparent-functors.cpp [new file with mode: 0644]
test/clang-tidy/modernize-use-using-macros.cpp [new file with mode: 0644]
test/clang-tidy/modernize-use-using.cpp [new file with mode: 0644]
test/clang-tidy/mpi-buffer-deref.cpp [new file with mode: 0644]
test/clang-tidy/mpi-type-mismatch.cpp [new file with mode: 0644]
test/clang-tidy/nolint.cpp [new file with mode: 0644]
test/clang-tidy/nolintnextline.cpp [new file with mode: 0644]
test/clang-tidy/overlapping.cpp [new file with mode: 0644]
test/clang-tidy/performance-faster-string-find.cpp [new file with mode: 0644]
test/clang-tidy/performance-for-range-copy-warn-on-all-auto-copies.cpp [new file with mode: 0644]
test/clang-tidy/performance-for-range-copy.cpp [new file with mode: 0644]
test/clang-tidy/performance-implicit-cast-in-loop.cpp [new file with mode: 0644]
test/clang-tidy/performance-inefficient-string-concatenation.cpp [new file with mode: 0644]
test/clang-tidy/performance-inefficient-vector-operation.cpp [new file with mode: 0644]
test/clang-tidy/performance-type-promotion-in-math-fn.cpp [new file with mode: 0644]
test/clang-tidy/performance-unnecessary-copy-initialization.cpp [new file with mode: 0644]
test/clang-tidy/performance-unnecessary-value-param-delayed.cpp [new file with mode: 0644]
test/clang-tidy/performance-unnecessary-value-param-header.cpp [new file with mode: 0644]
test/clang-tidy/performance-unnecessary-value-param-incomplete-type.cpp [new file with mode: 0644]
test/clang-tidy/performance-unnecessary-value-param.cpp [new file with mode: 0644]
test/clang-tidy/readability-avoid-const-params-in-decls.cpp [new file with mode: 0644]
test/clang-tidy/readability-braces-around-statements-assert-failure.cpp [new file with mode: 0644]
test/clang-tidy/readability-braces-around-statements-few-lines.cpp [new file with mode: 0644]
test/clang-tidy/readability-braces-around-statements-format.cpp [new file with mode: 0644]
test/clang-tidy/readability-braces-around-statements-same-line.cpp [new file with mode: 0644]
test/clang-tidy/readability-braces-around-statements-single-line.cpp [new file with mode: 0644]
test/clang-tidy/readability-braces-around-statements.cpp [new file with mode: 0644]
test/clang-tidy/readability-container-size-empty.cpp [new file with mode: 0644]
test/clang-tidy/readability-delete-null-pointer.cpp [new file with mode: 0644]
test/clang-tidy/readability-deleted-default.cpp [new file with mode: 0644]
test/clang-tidy/readability-else-after-return.cpp [new file with mode: 0644]
test/clang-tidy/readability-function-size.cpp [new file with mode: 0644]
test/clang-tidy/readability-identifier-naming.cpp [new file with mode: 0644]
test/clang-tidy/readability-implicit-bool-cast-allow-conditional-casts.cpp [new file with mode: 0644]
test/clang-tidy/readability-implicit-bool-cast-cxx98.cpp [new file with mode: 0644]
test/clang-tidy/readability-implicit-bool-cast.cpp [new file with mode: 0644]
test/clang-tidy/readability-inconsistent-declaration-parameter-name.cpp [new file with mode: 0644]
test/clang-tidy/readability-misleading-indentation.cpp [new file with mode: 0644]
test/clang-tidy/readability-misplaced-array-index.cpp [new file with mode: 0644]
test/clang-tidy/readability-named-parameter.cpp [new file with mode: 0644]
test/clang-tidy/readability-non-const-parameter.cpp [new file with mode: 0644]
test/clang-tidy/readability-redundant-control-flow.cpp [new file with mode: 0644]
test/clang-tidy/readability-redundant-declaration.cpp [new file with mode: 0644]
test/clang-tidy/readability-redundant-function-ptr-dereference.cpp [new file with mode: 0644]
test/clang-tidy/readability-redundant-member-init.cpp [new file with mode: 0644]
test/clang-tidy/readability-redundant-smartptr-get.cpp [new file with mode: 0644]
test/clang-tidy/readability-redundant-string-cstr-msvc.cpp [new file with mode: 0644]
test/clang-tidy/readability-redundant-string-cstr.cpp [new file with mode: 0644]
test/clang-tidy/readability-redundant-string-init-msvc.cpp [new file with mode: 0644]
test/clang-tidy/readability-redundant-string-init.cpp [new file with mode: 0644]
test/clang-tidy/readability-simplify-bool-expr-chained-conditional-assignment.cpp [new file with mode: 0644]
test/clang-tidy/readability-simplify-bool-expr-chained-conditional-return.cpp [new file with mode: 0644]
test/clang-tidy/readability-simplify-bool-expr.cpp [new file with mode: 0644]
test/clang-tidy/readability-static-definition-in-anonymous-namespace.cpp [new file with mode: 0644]
test/clang-tidy/readability-uniqueptr-delete-release.cpp [new file with mode: 0644]
test/clang-tidy/select-checks.cpp [new file with mode: 0644]
test/clang-tidy/serialize-diagnostics.cpp [new file with mode: 0644]
test/clang-tidy/static-analyzer-config.cpp [new file with mode: 0644]
test/clang-tidy/static-analyzer.cpp [new file with mode: 0644]
test/clang-tidy/temporaries.cpp [new file with mode: 0644]
test/clang-tidy/validate-check-names.cpp [new file with mode: 0644]
test/clang-tidy/werrors-diagnostics.cpp [new file with mode: 0644]
test/clang-tidy/werrors-plural.cpp [new file with mode: 0644]
test/clang-tidy/werrors.cpp [new file with mode: 0644]
test/clangd/authority-less-uri.test [new file with mode: 0644]
test/clangd/completion.test [new file with mode: 0644]
test/clangd/definitions.test [new file with mode: 0644]
test/clangd/diagnostics.test [new file with mode: 0644]
test/clangd/extra-flags.test [new file with mode: 0644]
test/clangd/fixits.test [new file with mode: 0644]
test/clangd/formatting.test [new file with mode: 0644]
test/include-fixer/Inputs/database_template.json [new file with mode: 0644]
test/include-fixer/Inputs/fake_yaml_db.yaml [new file with mode: 0644]
test/include-fixer/Inputs/merge/a.yaml [new file with mode: 0644]
test/include-fixer/Inputs/merge/b.yaml [new file with mode: 0644]
test/include-fixer/commandline_options.cpp [new file with mode: 0644]
test/include-fixer/exit_on_fatal.cpp [new file with mode: 0644]
test/include-fixer/fixeddb.cpp [new file with mode: 0644]
test/include-fixer/include_path.cpp [new file with mode: 0644]
test/include-fixer/merge.test [new file with mode: 0644]
test/include-fixer/multiple_fixes.cpp [new file with mode: 0644]
test/include-fixer/prefix_variable.cpp [new file with mode: 0644]
test/include-fixer/query_symbol.cpp [new file with mode: 0644]
test/include-fixer/ranking.cpp [new file with mode: 0644]
test/include-fixer/yaml_fuzzy.cpp [new file with mode: 0644]
test/include-fixer/yamldb.cpp [new file with mode: 0644]
test/include-fixer/yamldb_autodetect.cpp [new file with mode: 0644]
test/include-fixer/yamldb_plugin.cpp [new file with mode: 0644]
test/lit.cfg [new file with mode: 0644]
test/lit.site.cfg.in [new file with mode: 0644]
test/modularize/Inputs/Anonymous.h [new file with mode: 0644]
test/modularize/Inputs/CompileError/HasError.h [new file with mode: 0644]
test/modularize/Inputs/CompileError/Level1A.h [new file with mode: 0644]
test/modularize/Inputs/CompileError/module.modulemap [new file with mode: 0644]
test/modularize/Inputs/CoverageNoProblems/Includes1/.hidden/DontFindMe.h [new file with mode: 0644]
test/modularize/Inputs/CoverageNoProblems/Includes1/Level1A.h [new file with mode: 0644]
test/modularize/Inputs/CoverageNoProblems/Includes2/Level2A.h [new file with mode: 0644]
test/modularize/Inputs/CoverageNoProblems/NonIncludes/Level3A.h [new file with mode: 0644]
test/modularize/Inputs/CoverageNoProblems/module.modulemap [new file with mode: 0644]
test/modularize/Inputs/CoverageProblems/Level1A.h [new file with mode: 0644]
test/modularize/Inputs/CoverageProblems/Level1B.h [new file with mode: 0644]
test/modularize/Inputs/CoverageProblems/Level2A.h [new file with mode: 0644]
test/modularize/Inputs/CoverageProblems/Level2B.h [new file with mode: 0644]
test/modularize/Inputs/CoverageProblems/Level3A.h [new file with mode: 0644]
test/modularize/Inputs/CoverageProblems/Level3B [new file with mode: 0644]
test/modularize/Inputs/CoverageProblems/Sub/Level3B.h [new file with mode: 0644]
test/modularize/Inputs/CoverageProblems/UmbrellaFile.h [new file with mode: 0644]
test/modularize/Inputs/CoverageProblems/UmbrellaInclude1.h [new file with mode: 0644]
test/modularize/Inputs/CoverageProblems/UmbrellaInclude2.h [new file with mode: 0644]
test/modularize/Inputs/CoverageProblems/UmbrellaSub/Umbrell1.h [new file with mode: 0644]
test/modularize/Inputs/CoverageProblems/UmbrellaSub/Umbrell2.h [new file with mode: 0644]
test/modularize/Inputs/CoverageProblems/module.modulemap [new file with mode: 0644]
test/modularize/Inputs/DuplicateHeader1.h [new file with mode: 0644]
test/modularize/Inputs/DuplicateHeader2.h [new file with mode: 0644]
test/modularize/Inputs/Empty.h [new file with mode: 0644]
test/modularize/Inputs/HeaderGuard.h [new file with mode: 0644]
test/modularize/Inputs/HeaderGuardSub1.h [new file with mode: 0644]
test/modularize/Inputs/HeaderGuardSub2.h [new file with mode: 0644]
test/modularize/Inputs/HeaderGuardSubSub.h [new file with mode: 0644]
test/modularize/Inputs/HeaderGuardSubSubDefined.h [new file with mode: 0644]
test/modularize/Inputs/IncludeInExtern.h [new file with mode: 0644]
test/modularize/Inputs/IncludeInNamespace.h [new file with mode: 0644]
test/modularize/Inputs/InconsistentHeader1.h [new file with mode: 0644]
test/modularize/Inputs/InconsistentHeader2.h [new file with mode: 0644]
test/modularize/Inputs/InconsistentSubHeader.h [new file with mode: 0644]
test/modularize/Inputs/IsDependent.h [new file with mode: 0644]
test/modularize/Inputs/MissingHeader/Level1A.h [new file with mode: 0644]
test/modularize/Inputs/MissingHeader/module.modulemap [new file with mode: 0644]
test/modularize/Inputs/NamespaceClasses.h [new file with mode: 0644]
test/modularize/Inputs/NestedMacro.h [new file with mode: 0644]
test/modularize/Inputs/NoProblems.modulemap [new file with mode: 0644]
test/modularize/Inputs/ProblemsDuplicate.modulemap [new file with mode: 0644]
test/modularize/Inputs/SomeDecls.h [new file with mode: 0644]
test/modularize/Inputs/SomeOtherTypes.h [new file with mode: 0644]
test/modularize/Inputs/SomeTypes.h [new file with mode: 0644]
test/modularize/Inputs/SubModule1/Header1.h [new file with mode: 0644]
test/modularize/Inputs/SubModule1/Header2.h [new file with mode: 0644]
test/modularize/Inputs/SubModule2/Header3.h [new file with mode: 0644]
test/modularize/Inputs/SubModule2/Header4.h [new file with mode: 0644]
test/modularize/Inputs/TemplateClasses.h [new file with mode: 0644]
test/modularize/NoProblems.modularize [new file with mode: 0644]
test/modularize/NoProblemsAnonymous.modularize [new file with mode: 0644]
test/modularize/NoProblemsAssistant.modularize [new file with mode: 0644]
test/modularize/NoProblemsCoverage.modularize [new file with mode: 0644]
test/modularize/NoProblemsDependencies.modularize [new file with mode: 0644]
test/modularize/NoProblemsGuard.modularize [new file with mode: 0644]
test/modularize/NoProblemsList.modularize [new file with mode: 0644]
test/modularize/NoProblemsNamespace.modularize [new file with mode: 0644]
test/modularize/NoProblemsNamespaceClasses.modularize [new file with mode: 0644]
test/modularize/NoProblemsNestedMacro.modularize [new file with mode: 0644]
test/modularize/NoProblemsTemplateClasses.modularize [new file with mode: 0644]
test/modularize/ProblemsCompileError.modularize [new file with mode: 0644]
test/modularize/ProblemsCoverage.modularize [new file with mode: 0644]
test/modularize/ProblemsDisplayLists.modularize [new file with mode: 0644]
test/modularize/ProblemsDuplicate.modularize [new file with mode: 0644]
test/modularize/ProblemsExternC.modularize [new file with mode: 0644]
test/modularize/ProblemsInconsistent.modularize [new file with mode: 0644]
test/modularize/ProblemsMissingHeader.modularize [new file with mode: 0644]
test/modularize/ProblemsNamespace.modularize [new file with mode: 0644]
test/modularize/SubModule2.h [new file with mode: 0644]
test/pp-trace/Inputs/Level1A.h [new file with mode: 0644]
test/pp-trace/Inputs/Level1B.h [new file with mode: 0644]
test/pp-trace/Inputs/Level2A.h [new file with mode: 0644]
test/pp-trace/Inputs/Level2B.h [new file with mode: 0644]
test/pp-trace/Inputs/ModularizeList.txt [new file with mode: 0644]
test/pp-trace/Inputs/module.map [new file with mode: 0644]
test/pp-trace/pp-trace-conditional.cpp [new file with mode: 0644]
test/pp-trace/pp-trace-ident.cpp [new file with mode: 0644]
test/pp-trace/pp-trace-include.cpp [new file with mode: 0644]
test/pp-trace/pp-trace-macro.cpp [new file with mode: 0644]
test/pp-trace/pp-trace-modules.cpp [new file with mode: 0644]
test/pp-trace/pp-trace-pragma-general.cpp [new file with mode: 0644]
test/pp-trace/pp-trace-pragma-ms.cpp [new file with mode: 0644]
test/pp-trace/pp-trace-pragma-opencl.cpp [new file with mode: 0644]
tool-template/CMakeLists.txt [new file with mode: 0644]
tool-template/ToolTemplate.cpp [new file with mode: 0644]
unittests/CMakeLists.txt [new file with mode: 0644]
unittests/change-namespace/CMakeLists.txt [new file with mode: 0644]
unittests/change-namespace/ChangeNamespaceTests.cpp [new file with mode: 0644]
unittests/clang-apply-replacements/ApplyReplacementsTest.cpp [new file with mode: 0644]
unittests/clang-apply-replacements/CMakeLists.txt [new file with mode: 0644]
unittests/clang-apply-replacements/ReformattingTest.cpp [new file with mode: 0644]
unittests/clang-move/CMakeLists.txt [new file with mode: 0644]
unittests/clang-move/ClangMoveTests.cpp [new file with mode: 0644]
unittests/clang-query/CMakeLists.txt [new file with mode: 0644]
unittests/clang-query/QueryEngineTest.cpp [new file with mode: 0644]
unittests/clang-query/QueryParserTest.cpp [new file with mode: 0644]
unittests/clang-tidy/CMakeLists.txt [new file with mode: 0644]
unittests/clang-tidy/ClangTidyDiagnosticConsumerTest.cpp [new file with mode: 0644]
unittests/clang-tidy/ClangTidyOptionsTest.cpp [new file with mode: 0644]
unittests/clang-tidy/ClangTidyTest.h [new file with mode: 0644]
unittests/clang-tidy/GoogleModuleTest.cpp [new file with mode: 0644]
unittests/clang-tidy/IncludeInserterTest.cpp [new file with mode: 0644]
unittests/clang-tidy/LLVMModuleTest.cpp [new file with mode: 0644]
unittests/clang-tidy/MiscModuleTest.cpp [new file with mode: 0644]
unittests/clang-tidy/NamespaceAliaserTest.cpp [new file with mode: 0644]
unittests/clang-tidy/OverlappingReplacementsTest.cpp [new file with mode: 0644]
unittests/clang-tidy/ReadabilityModuleTest.cpp [new file with mode: 0644]
unittests/clang-tidy/UsingInserterTest.cpp [new file with mode: 0644]
unittests/clangd/CMakeLists.txt [new file with mode: 0644]
unittests/clangd/ClangdTests.cpp [new file with mode: 0644]
unittests/include-fixer/CMakeLists.txt [new file with mode: 0644]
unittests/include-fixer/FuzzySymbolIndexTests.cpp [new file with mode: 0644]
unittests/include-fixer/IncludeFixerTest.cpp [new file with mode: 0644]
unittests/include-fixer/find-all-symbols/CMakeLists.txt [new file with mode: 0644]
unittests/include-fixer/find-all-symbols/FindAllSymbolsTests.cpp [new file with mode: 0644]
unittests/include/common/VirtualFileHelper.h [new file with mode: 0644]

diff --git a/.arcconfig b/.arcconfig
new file mode 100644 (file)
index 0000000..f846581
--- /dev/null
@@ -0,0 +1,4 @@
+{
+  "project_id" : "clang-tools-extra",
+  "conduit_uri" : "https://reviews.llvm.org/"
+}
diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..ac573c4
--- /dev/null
@@ -0,0 +1,32 @@
+#==============================================================================#
+# This file specifies intentionally untracked files that git should ignore.
+# See: http://www.kernel.org/pub/software/scm/git/docs/gitignore.html
+#
+# This file is intentionally different from the output of `git svn show-ignore`,
+# as most of those are useless.
+#==============================================================================#
+
+#==============================================================================#
+# File extensions to be ignored anywhere in the tree.
+#==============================================================================#
+# Temp files created by most text editors.
+*~
+# Merge files created by git.
+*.orig
+# Byte compiled python modules.
+*.pyc
+# vim swap files
+.*.swp
+.sw?
+
+#==============================================================================#
+# Explicit files to ignore (only matches one).
+#==============================================================================#
+cscope.files
+cscope.out
+.clang_complete
+
+#==============================================================================#
+# Directories to ignore (do not add trailing '/'s, they skip symlinks).
+#==============================================================================#
+docs/_build
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644 (file)
index 0000000..ac8f16b
--- /dev/null
@@ -0,0 +1,29 @@
+add_subdirectory(clang-apply-replacements)
+add_subdirectory(clang-reorder-fields)
+add_subdirectory(modularize)
+if(CLANG_ENABLE_STATIC_ANALYZER)
+add_subdirectory(clang-tidy)
+add_subdirectory(clang-tidy-vs)
+endif()
+
+add_subdirectory(change-namespace)
+add_subdirectory(clang-query)
+add_subdirectory(clang-move)
+add_subdirectory(clangd)
+add_subdirectory(include-fixer)
+add_subdirectory(pp-trace)
+add_subdirectory(tool-template)
+
+# Add the common testsuite after all the tools.
+# TODO: Support tests with more granularity when features are off?
+if(CLANG_ENABLE_STATIC_ANALYZER AND CLANG_INCLUDE_TESTS)
+add_subdirectory(test)
+add_subdirectory(unittests)
+endif()
+
+option(CLANG_TOOLS_EXTRA_INCLUDE_DOCS "Generate build targets for the Clang Extra Tools docs."
+  ${LLVM_INCLUDE_DOCS})
+if( CLANG_TOOLS_EXTRA_INCLUDE_DOCS )
+  add_subdirectory(docs)
+endif()
+
diff --git a/CODE_OWNERS.TXT b/CODE_OWNERS.TXT
new file mode 100644 (file)
index 0000000..af8beb4
--- /dev/null
@@ -0,0 +1,21 @@
+This file is a list of the people responsible for ensuring that patches for a
+particular tool are reviewed, either by themself or by someone else. They are
+also the gatekeepers for their part of Clang, with the final word on what goes
+in or not.
+
+The list is sorted by surname and formatted to allow easy grepping and
+beautification by scripts.  The fields are: name (N), email (E), web-address
+(W), PGP key ID and fingerprint (P), description (D), and snail-mail address
+(S).
+
+N: Peter Collingbourne
+E: peter@pcc.me.uk
+D: clang-query
+
+N: Manuel Klimek
+E: klimek@google.com
+D: clang-rename, all parts of clang-tools-extra not covered by someone else
+
+N: Alexander Kornienko
+E: alexfh@google.com
+D: clang-tidy
diff --git a/LICENSE.TXT b/LICENSE.TXT
new file mode 100644 (file)
index 0000000..12fb67d
--- /dev/null
@@ -0,0 +1,63 @@
+==============================================================================
+LLVM Release License
+==============================================================================
+University of Illinois/NCSA
+Open Source License
+
+Copyright (c) 2007-2016 University of Illinois at Urbana-Champaign.
+All rights reserved.
+
+Developed by:
+
+    LLVM Team
+
+    University of Illinois at Urbana-Champaign
+
+    http://llvm.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal with
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+    * Redistributions of source code must retain the above copyright notice,
+      this list of conditions and the following disclaimers.
+
+    * Redistributions in binary form must reproduce the above copyright notice,
+      this list of conditions and the following disclaimers in the
+      documentation and/or other materials provided with the distribution.
+
+    * Neither the names of the LLVM Team, University of Illinois at
+      Urbana-Champaign, nor the names of its contributors may be used to
+      endorse or promote products derived from this Software without specific
+      prior written permission.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
+SOFTWARE.
+
+==============================================================================
+The LLVM software contains code written by third parties.  Such software will
+have its own individual LICENSE.TXT file in the directory in which it appears.
+This file will describe the copyrights, license, and restrictions which apply
+to that code.
+
+The disclaimer of warranty in the University of Illinois Open Source License
+applies to all code in the LLVM Distribution, and nothing in any of the
+other licenses gives permission to use the names of the LLVM Team or the
+University of Illinois to endorse or promote products derived from this
+Software.
+
+The following pieces of software have additional or alternate copyrights,
+licenses, and/or restrictions:
+
+Program             Directory
+-------             ---------
+clang-tidy          clang-tidy/cert
+clang-tidy          clang-tidy/hicpp
diff --git a/README.txt b/README.txt
new file mode 100644 (file)
index 0000000..9809cc3
--- /dev/null
@@ -0,0 +1,22 @@
+//===----------------------------------------------------------------------===//
+// Clang Tools repository
+//===----------------------------------------------------------------------===//
+
+Welcome to the repository of extra Clang Tools.  This repository holds tools
+that are developed as part of the LLVM compiler infrastructure project and the
+Clang frontend.  These tools are kept in a separate "extra" repository to
+allow lighter weight checkouts of the core Clang codebase.
+
+This repository is only intended to be checked out inside of a full LLVM+Clang
+tree, and in the 'tools/extra' subdirectory of the Clang checkout.
+
+All discussion regarding Clang, Clang-based tools, and code in this repository
+should be held using the standard Clang mailing lists:
+  http://lists.llvm.org/mailman/listinfo/cfe-dev
+
+Code review for this tree should take place on the standard Clang patch and
+commit lists:
+  http://lists.llvm.org/mailman/listinfo/cfe-commits
+
+If you find a bug in these tools, please file it in the LLVM bug tracker:
+  http://llvm.org/bugs/
diff --git a/change-namespace/CMakeLists.txt b/change-namespace/CMakeLists.txt
new file mode 100644 (file)
index 0000000..1c8aba9
--- /dev/null
@@ -0,0 +1,19 @@
+set(LLVM_LINK_COMPONENTS
+  support
+  )
+
+add_clang_library(clangChangeNamespace
+  ChangeNamespace.cpp
+
+  LINK_LIBS
+  clangAST
+  clangASTMatchers
+  clangBasic
+  clangFormat
+  clangFrontend
+  clangLex
+  clangTooling
+  clangToolingCore
+  )
+
+add_subdirectory(tool)
diff --git a/change-namespace/ChangeNamespace.cpp b/change-namespace/ChangeNamespace.cpp
new file mode 100644 (file)
index 0000000..0b90de4
--- /dev/null
@@ -0,0 +1,1009 @@
+//===-- ChangeNamespace.cpp - Change namespace implementation -------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+#include "ChangeNamespace.h"
+#include "clang/Format/Format.h"
+#include "clang/Lex/Lexer.h"
+#include "llvm/Support/ErrorHandling.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace change_namespace {
+
+namespace {
+
+inline std::string
+joinNamespaces(const llvm::SmallVectorImpl<StringRef> &Namespaces) {
+  if (Namespaces.empty())
+    return "";
+  std::string Result = Namespaces.front();
+  for (auto I = Namespaces.begin() + 1, E = Namespaces.end(); I != E; ++I)
+    Result += ("::" + *I).str();
+  return Result;
+}
+
+// Given "a::b::c", returns {"a", "b", "c"}.
+llvm::SmallVector<llvm::StringRef, 4> splitSymbolName(llvm::StringRef Name) {
+  llvm::SmallVector<llvm::StringRef, 4> Splitted;
+  Name.split(Splitted, "::", /*MaxSplit=*/-1,
+             /*KeepEmpty=*/false);
+  return Splitted;
+}
+
+SourceLocation startLocationForType(TypeLoc TLoc) {
+  // For elaborated types (e.g. `struct a::A`) we want the portion after the
+  // `struct` but including the namespace qualifier, `a::`.
+  if (TLoc.getTypeLocClass() == TypeLoc::Elaborated) {
+    NestedNameSpecifierLoc NestedNameSpecifier =
+        TLoc.castAs<ElaboratedTypeLoc>().getQualifierLoc();
+    if (NestedNameSpecifier.getNestedNameSpecifier())
+      return NestedNameSpecifier.getBeginLoc();
+    TLoc = TLoc.getNextTypeLoc();
+  }
+  return TLoc.getLocStart();
+}
+
+SourceLocation endLocationForType(TypeLoc TLoc) {
+  // Dig past any namespace or keyword qualifications.
+  while (TLoc.getTypeLocClass() == TypeLoc::Elaborated ||
+         TLoc.getTypeLocClass() == TypeLoc::Qualified)
+    TLoc = TLoc.getNextTypeLoc();
+
+  // The location for template specializations (e.g. Foo<int>) includes the
+  // templated types in its location range.  We want to restrict this to just
+  // before the `<` character.
+  if (TLoc.getTypeLocClass() == TypeLoc::TemplateSpecialization)
+    return TLoc.castAs<TemplateSpecializationTypeLoc>()
+        .getLAngleLoc()
+        .getLocWithOffset(-1);
+  return TLoc.getEndLoc();
+}
+
+// Returns the containing namespace of `InnerNs` by skipping `PartialNsName`.
+// If the `InnerNs` does not have `PartialNsName` as suffix, or `PartialNsName`
+// is empty, nullptr is returned.
+// For example, if `InnerNs` is "a::b::c" and `PartialNsName` is "b::c", then
+// the NamespaceDecl of namespace "a" will be returned.
+const NamespaceDecl *getOuterNamespace(const NamespaceDecl *InnerNs,
+                                       llvm::StringRef PartialNsName) {
+  if (!InnerNs || PartialNsName.empty())
+    return nullptr;
+  const auto *CurrentContext = llvm::cast<DeclContext>(InnerNs);
+  const auto *CurrentNs = InnerNs;
+  auto PartialNsNameSplitted = splitSymbolName(PartialNsName);
+  while (!PartialNsNameSplitted.empty()) {
+    // Get the inner-most namespace in CurrentContext.
+    while (CurrentContext && !llvm::isa<NamespaceDecl>(CurrentContext))
+      CurrentContext = CurrentContext->getParent();
+    if (!CurrentContext)
+      return nullptr;
+    CurrentNs = llvm::cast<NamespaceDecl>(CurrentContext);
+    if (PartialNsNameSplitted.back() != CurrentNs->getNameAsString())
+      return nullptr;
+    PartialNsNameSplitted.pop_back();
+    CurrentContext = CurrentContext->getParent();
+  }
+  return CurrentNs;
+}
+
+static std::unique_ptr<Lexer>
+getLexerStartingFromLoc(SourceLocation Loc, const SourceManager &SM,
+                        const LangOptions &LangOpts) {
+  if (Loc.isMacroID() &&
+      !Lexer::isAtEndOfMacroExpansion(Loc, SM, LangOpts, &Loc))
+    return nullptr;
+  // Break down the source location.
+  std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
+  // Try to load the file buffer.
+  bool InvalidTemp = false;
+  llvm::StringRef File = SM.getBufferData(LocInfo.first, &InvalidTemp);
+  if (InvalidTemp)
+    return nullptr;
+
+  const char *TokBegin = File.data() + LocInfo.second;
+  // Lex from the start of the given location.
+  return llvm::make_unique<Lexer>(SM.getLocForStartOfFile(LocInfo.first),
+                                  LangOpts, File.begin(), TokBegin, File.end());
+}
+
+// FIXME: get rid of this helper function if this is supported in clang-refactor
+// library.
+static SourceLocation getStartOfNextLine(SourceLocation Loc,
+                                         const SourceManager &SM,
+                                         const LangOptions &LangOpts) {
+  std::unique_ptr<Lexer> Lex = getLexerStartingFromLoc(Loc, SM, LangOpts);
+  if (!Lex.get())
+    return SourceLocation();
+  llvm::SmallVector<char, 16> Line;
+  // FIXME: this is a bit hacky to get ReadToEndOfLine work.
+  Lex->setParsingPreprocessorDirective(true);
+  Lex->ReadToEndOfLine(&Line);
+  auto End = Loc.getLocWithOffset(Line.size());
+  return SM.getLocForEndOfFile(SM.getDecomposedLoc(Loc).first) == End
+             ? End
+             : End.getLocWithOffset(1);
+}
+
+// Returns `R` with new range that refers to code after `Replaces` being
+// applied.
+tooling::Replacement
+getReplacementInChangedCode(const tooling::Replacements &Replaces,
+                            const tooling::Replacement &R) {
+  unsigned NewStart = Replaces.getShiftedCodePosition(R.getOffset());
+  unsigned NewEnd =
+      Replaces.getShiftedCodePosition(R.getOffset() + R.getLength());
+  return tooling::Replacement(R.getFilePath(), NewStart, NewEnd - NewStart,
+                              R.getReplacementText());
+}
+
+// Adds a replacement `R` into `Replaces` or merges it into `Replaces` by
+// applying all existing Replaces first if there is conflict.
+void addOrMergeReplacement(const tooling::Replacement &R,
+                           tooling::Replacements *Replaces) {
+  auto Err = Replaces->add(R);
+  if (Err) {
+    llvm::consumeError(std::move(Err));
+    auto Replace = getReplacementInChangedCode(*Replaces, R);
+    *Replaces = Replaces->merge(tooling::Replacements(Replace));
+  }
+}
+
+tooling::Replacement createReplacement(SourceLocation Start, SourceLocation End,
+                                       llvm::StringRef ReplacementText,
+                                       const SourceManager &SM) {
+  if (!Start.isValid() || !End.isValid()) {
+    llvm::errs() << "start or end location were invalid\n";
+    return tooling::Replacement();
+  }
+  if (SM.getDecomposedLoc(Start).first != SM.getDecomposedLoc(End).first) {
+    llvm::errs()
+        << "start or end location were in different macro expansions\n";
+    return tooling::Replacement();
+  }
+  Start = SM.getSpellingLoc(Start);
+  End = SM.getSpellingLoc(End);
+  if (SM.getFileID(Start) != SM.getFileID(End)) {
+    llvm::errs() << "start or end location were in different files\n";
+    return tooling::Replacement();
+  }
+  return tooling::Replacement(
+      SM, CharSourceRange::getTokenRange(SM.getSpellingLoc(Start),
+                                         SM.getSpellingLoc(End)),
+      ReplacementText);
+}
+
+void addReplacementOrDie(
+    SourceLocation Start, SourceLocation End, llvm::StringRef ReplacementText,
+    const SourceManager &SM,
+    std::map<std::string, tooling::Replacements> *FileToReplacements) {
+  const auto R = createReplacement(Start, End, ReplacementText, SM);
+  auto Err = (*FileToReplacements)[R.getFilePath()].add(R);
+  if (Err)
+    llvm_unreachable(llvm::toString(std::move(Err)).c_str());
+}
+
+tooling::Replacement createInsertion(SourceLocation Loc,
+                                     llvm::StringRef InsertText,
+                                     const SourceManager &SM) {
+  if (Loc.isInvalid()) {
+    llvm::errs() << "insert Location is invalid.\n";
+    return tooling::Replacement();
+  }
+  Loc = SM.getSpellingLoc(Loc);
+  return tooling::Replacement(SM, Loc, 0, InsertText);
+}
+
+// Returns the shortest qualified name for declaration `DeclName` in the
+// namespace `NsName`. For example, if `DeclName` is "a::b::X" and `NsName`
+// is "a::c::d", then "b::X" will be returned.
+// Note that if `DeclName` is `::b::X` and `NsName` is `::a::b`, this returns
+// "::b::X" instead of "b::X" since there will be a name conflict otherwise.
+// \param DeclName A fully qualified name, "::a::b::X" or "a::b::X".
+// \param NsName A fully qualified name, "::a::b" or "a::b". Global namespace
+//        will have empty name.
+std::string getShortestQualifiedNameInNamespace(llvm::StringRef DeclName,
+                                                llvm::StringRef NsName) {
+  DeclName = DeclName.ltrim(':');
+  NsName = NsName.ltrim(':');
+  if (DeclName.find(':') == llvm::StringRef::npos)
+    return DeclName;
+
+  auto NsNameSplitted = splitSymbolName(NsName);
+  auto DeclNsSplitted = splitSymbolName(DeclName);
+  llvm::StringRef UnqualifiedDeclName = DeclNsSplitted.pop_back_val();
+  // If the Decl is in global namespace, there is no need to shorten it.
+  if (DeclNsSplitted.empty())
+    return UnqualifiedDeclName;
+  // If NsName is the global namespace, we can simply use the DeclName sans
+  // leading "::".
+  if (NsNameSplitted.empty())
+    return DeclName;
+
+  if (NsNameSplitted.front() != DeclNsSplitted.front()) {
+    // The DeclName must be fully-qualified, but we still need to decide if a
+    // leading "::" is necessary. For example, if `NsName` is "a::b::c" and the
+    // `DeclName` is "b::X", then the reference must be qualified as "::b::X"
+    // to avoid conflict.
+    if (llvm::is_contained(NsNameSplitted, DeclNsSplitted.front()))
+      return ("::" + DeclName).str();
+    return DeclName;
+  }
+  // Since there is already an overlap namespace, we know that `DeclName` can be
+  // shortened, so we reduce the longest common prefix.
+  auto DeclI = DeclNsSplitted.begin();
+  auto DeclE = DeclNsSplitted.end();
+  auto NsI = NsNameSplitted.begin();
+  auto NsE = NsNameSplitted.end();
+  for (; DeclI != DeclE && NsI != NsE && *DeclI == *NsI; ++DeclI, ++NsI) {
+  }
+  return (DeclI == DeclE)
+             ? UnqualifiedDeclName.str()
+             : (llvm::join(DeclI, DeclE, "::") + "::" + UnqualifiedDeclName)
+                   .str();
+}
+
+std::string wrapCodeInNamespace(StringRef NestedNs, std::string Code) {
+  if (Code.back() != '\n')
+    Code += "\n";
+  auto NsSplitted = splitSymbolName(NestedNs);
+  while (!NsSplitted.empty()) {
+    // FIXME: consider code style for comments.
+    Code = ("namespace " + NsSplitted.back() + " {\n" + Code +
+            "} // namespace " + NsSplitted.back() + "\n")
+               .str();
+    NsSplitted.pop_back();
+  }
+  return Code;
+}
+
+// Returns true if \p D is a nested DeclContext in \p Context
+bool isNestedDeclContext(const DeclContext *D, const DeclContext *Context) {
+  while (D) {
+    if (D == Context)
+      return true;
+    D = D->getParent();
+  }
+  return false;
+}
+
+// Returns true if \p D is visible at \p Loc with DeclContext \p DeclCtx.
+bool isDeclVisibleAtLocation(const SourceManager &SM, const Decl *D,
+                             const DeclContext *DeclCtx, SourceLocation Loc) {
+  SourceLocation DeclLoc = SM.getSpellingLoc(D->getLocStart());
+  Loc = SM.getSpellingLoc(Loc);
+  return SM.isBeforeInTranslationUnit(DeclLoc, Loc) &&
+         (SM.getFileID(DeclLoc) == SM.getFileID(Loc) &&
+          isNestedDeclContext(DeclCtx, D->getDeclContext()));
+}
+
+// Given a qualified symbol name, returns true if the symbol will be
+// incorrectly qualified without leading "::".
+bool conflictInNamespace(llvm::StringRef QualifiedSymbol,
+                         llvm::StringRef Namespace) {
+  auto SymbolSplitted = splitSymbolName(QualifiedSymbol.trim(":"));
+  assert(!SymbolSplitted.empty());
+  SymbolSplitted.pop_back();  // We are only interested in namespaces.
+
+  if (SymbolSplitted.size() > 1 && !Namespace.empty()) {
+    auto NsSplitted = splitSymbolName(Namespace.trim(":"));
+    assert(!NsSplitted.empty());
+    // We do not check the outermost namespace since it would not be a conflict
+    // if it equals to the symbol's outermost namespace and the symbol name
+    // would have been shortened.
+    for (auto I = NsSplitted.begin() + 1, E = NsSplitted.end(); I != E; ++I) {
+      if (*I == SymbolSplitted.front())
+        return true;
+    }
+  }
+  return false;
+}
+
+AST_MATCHER(EnumDecl, isScoped) {
+    return Node.isScoped();
+}
+
+bool isTemplateParameter(TypeLoc Type) {
+  while (!Type.isNull()) {
+    if (Type.getTypeLocClass() == TypeLoc::SubstTemplateTypeParm)
+      return true;
+    Type = Type.getNextTypeLoc();
+  }
+  return false;
+}
+
+} // anonymous namespace
+
+ChangeNamespaceTool::ChangeNamespaceTool(
+    llvm::StringRef OldNs, llvm::StringRef NewNs, llvm::StringRef FilePattern,
+    llvm::ArrayRef<std::string> WhiteListedSymbolPatterns,
+    std::map<std::string, tooling::Replacements> *FileToReplacements,
+    llvm::StringRef FallbackStyle)
+    : FallbackStyle(FallbackStyle), FileToReplacements(*FileToReplacements),
+      OldNamespace(OldNs.ltrim(':')), NewNamespace(NewNs.ltrim(':')),
+      FilePattern(FilePattern), FilePatternRE(FilePattern) {
+  FileToReplacements->clear();
+  auto OldNsSplitted = splitSymbolName(OldNamespace);
+  auto NewNsSplitted = splitSymbolName(NewNamespace);
+  // Calculates `DiffOldNamespace` and `DiffNewNamespace`.
+  while (!OldNsSplitted.empty() && !NewNsSplitted.empty() &&
+         OldNsSplitted.front() == NewNsSplitted.front()) {
+    OldNsSplitted.erase(OldNsSplitted.begin());
+    NewNsSplitted.erase(NewNsSplitted.begin());
+  }
+  DiffOldNamespace = joinNamespaces(OldNsSplitted);
+  DiffNewNamespace = joinNamespaces(NewNsSplitted);
+
+  for (const auto &Pattern : WhiteListedSymbolPatterns)
+    WhiteListedSymbolRegexes.emplace_back(Pattern);
+}
+
+void ChangeNamespaceTool::registerMatchers(ast_matchers::MatchFinder *Finder) {
+  std::string FullOldNs = "::" + OldNamespace;
+  // Prefix is the outer-most namespace in DiffOldNamespace. For example, if the
+  // OldNamespace is "a::b::c" and DiffOldNamespace is "b::c", then Prefix will
+  // be "a::b". Declarations in this namespace will not be visible in the new
+  // namespace. If DiffOldNamespace is empty, Prefix will be a invalid name "-".
+  llvm::SmallVector<llvm::StringRef, 4> DiffOldNsSplitted;
+  llvm::StringRef(DiffOldNamespace)
+      .split(DiffOldNsSplitted, "::", /*MaxSplit=*/-1,
+             /*KeepEmpty=*/false);
+  std::string Prefix = "-";
+  if (!DiffOldNsSplitted.empty())
+    Prefix = (StringRef(FullOldNs).drop_back(DiffOldNamespace.size()) +
+              DiffOldNsSplitted.front())
+                 .str();
+  auto IsInMovedNs =
+      allOf(hasAncestor(namespaceDecl(hasName(FullOldNs)).bind("ns_decl")),
+            isExpansionInFileMatching(FilePattern));
+  auto IsVisibleInNewNs = anyOf(
+      IsInMovedNs, unless(hasAncestor(namespaceDecl(hasName(Prefix)))));
+  // Match using declarations.
+  Finder->addMatcher(
+      usingDecl(isExpansionInFileMatching(FilePattern), IsVisibleInNewNs)
+          .bind("using"),
+      this);
+  // Match using namespace declarations.
+  Finder->addMatcher(usingDirectiveDecl(isExpansionInFileMatching(FilePattern),
+                                        IsVisibleInNewNs)
+                         .bind("using_namespace"),
+                     this);
+  // Match namespace alias declarations.
+  Finder->addMatcher(namespaceAliasDecl(isExpansionInFileMatching(FilePattern),
+                                        IsVisibleInNewNs)
+                         .bind("namespace_alias"),
+                     this);
+
+  // Match old namespace blocks.
+  Finder->addMatcher(
+      namespaceDecl(hasName(FullOldNs), isExpansionInFileMatching(FilePattern))
+          .bind("old_ns"),
+      this);
+
+  // Match class forward-declarations in the old namespace.
+  // Note that forward-declarations in classes are not matched.
+  Finder->addMatcher(cxxRecordDecl(unless(anyOf(isImplicit(), isDefinition())),
+                                   IsInMovedNs, hasParent(namespaceDecl()))
+                         .bind("class_fwd_decl"),
+                     this);
+
+  // Match template class forward-declarations in the old namespace.
+  Finder->addMatcher(
+      classTemplateDecl(unless(hasDescendant(cxxRecordDecl(isDefinition()))),
+                        IsInMovedNs, hasParent(namespaceDecl()))
+          .bind("template_class_fwd_decl"),
+      this);
+
+  // Match references to types that are not defined in the old namespace.
+  // Forward-declarations in the old namespace are also matched since they will
+  // be moved back to the old namespace.
+  auto DeclMatcher = namedDecl(
+      hasAncestor(namespaceDecl()),
+      unless(anyOf(
+          isImplicit(), hasAncestor(namespaceDecl(isAnonymous())),
+          hasAncestor(cxxRecordDecl()),
+          allOf(IsInMovedNs, unless(cxxRecordDecl(unless(isDefinition())))))));
+
+  // Using shadow declarations in classes always refers to base class, which
+  // does not need to be qualified since it can be inferred from inheritance.
+  // Note that this does not match using alias declarations.
+  auto UsingShadowDeclInClass =
+      usingDecl(hasAnyUsingShadowDecl(decl()), hasParent(cxxRecordDecl()));
+
+  // Match TypeLocs on the declaration. Carefully match only the outermost
+  // TypeLoc and template specialization arguments (which are not outermost)
+  // that are directly linked to types matching `DeclMatcher`. Nested name
+  // specifier locs are handled separately below.
+  Finder->addMatcher(
+      typeLoc(IsInMovedNs,
+              loc(qualType(hasDeclaration(DeclMatcher.bind("from_decl")))),
+              unless(anyOf(hasParent(typeLoc(loc(qualType(
+                               allOf(hasDeclaration(DeclMatcher),
+                                     unless(templateSpecializationType())))))),
+                           hasParent(nestedNameSpecifierLoc()),
+                           hasAncestor(isImplicit()),
+                           hasAncestor(UsingShadowDeclInClass))),
+              hasAncestor(decl().bind("dc")))
+          .bind("type"),
+      this);
+
+  // Types in `UsingShadowDecl` is not matched by `typeLoc` above, so we need to
+  // special case it.
+  // Since using declarations inside classes must have the base class in the
+  // nested name specifier, we leave it to the nested name specifier matcher.
+  Finder->addMatcher(usingDecl(IsInMovedNs, hasAnyUsingShadowDecl(decl()),
+                               unless(UsingShadowDeclInClass))
+                         .bind("using_with_shadow"),
+                     this);
+
+  // Handle types in nested name specifier. Specifiers that are in a TypeLoc
+  // matched above are not matched, e.g. "A::" in "A::A" is not matched since
+  // "A::A" would have already been fixed.
+  Finder->addMatcher(
+      nestedNameSpecifierLoc(
+          hasAncestor(decl(IsInMovedNs).bind("dc")),
+          loc(nestedNameSpecifier(
+              specifiesType(hasDeclaration(DeclMatcher.bind("from_decl"))))),
+          unless(anyOf(hasAncestor(isImplicit()),
+                       hasAncestor(UsingShadowDeclInClass),
+                       hasAncestor(typeLoc(loc(qualType(hasDeclaration(
+                           decl(equalsBoundNode("from_decl"))))))))))
+          .bind("nested_specifier_loc"),
+      this);
+
+  // Matches base class initializers in constructors. TypeLocs of base class
+  // initializers do not need to be fixed. For example,
+  //    class X : public a::b::Y {
+  //      public:
+  //        X() : Y::Y() {} // Y::Y do not need namespace specifier.
+  //    };
+  Finder->addMatcher(
+      cxxCtorInitializer(isBaseInitializer()).bind("base_initializer"), this);
+
+  // Handle function.
+  // Only handle functions that are defined in a namespace excluding member
+  // function, static methods (qualified by nested specifier), and functions
+  // defined in the global namespace.
+  // Note that the matcher does not exclude calls to out-of-line static method
+  // definitions, so we need to exclude them in the callback handler.
+  auto FuncMatcher =
+      functionDecl(unless(anyOf(cxxMethodDecl(), IsInMovedNs,
+                                hasAncestor(namespaceDecl(isAnonymous())),
+                                hasAncestor(cxxRecordDecl()))),
+                   hasParent(namespaceDecl()));
+  Finder->addMatcher(decl(forEachDescendant(expr(anyOf(
+                              callExpr(callee(FuncMatcher)).bind("call"),
+                              declRefExpr(to(FuncMatcher.bind("func_decl")))
+                                  .bind("func_ref")))),
+                          IsInMovedNs, unless(isImplicit()))
+                         .bind("dc"),
+                     this);
+
+  auto GlobalVarMatcher = varDecl(
+      hasGlobalStorage(), hasParent(namespaceDecl()),
+      unless(anyOf(IsInMovedNs, hasAncestor(namespaceDecl(isAnonymous())))));
+  Finder->addMatcher(declRefExpr(IsInMovedNs, hasAncestor(decl().bind("dc")),
+                                 to(GlobalVarMatcher.bind("var_decl")))
+                         .bind("var_ref"),
+                     this);
+
+  // Handle unscoped enum constant.
+  auto UnscopedEnumMatcher = enumConstantDecl(hasParent(enumDecl(
+      hasParent(namespaceDecl()),
+      unless(anyOf(isScoped(), IsInMovedNs, hasAncestor(cxxRecordDecl()),
+                   hasAncestor(namespaceDecl(isAnonymous())))))));
+  Finder->addMatcher(
+      declRefExpr(IsInMovedNs, hasAncestor(decl().bind("dc")),
+                  to(UnscopedEnumMatcher.bind("enum_const_decl")))
+          .bind("enum_const_ref"),
+      this);
+}
+
+void ChangeNamespaceTool::run(
+    const ast_matchers::MatchFinder::MatchResult &Result) {
+  if (const auto *Using = Result.Nodes.getNodeAs<UsingDecl>("using")) {
+    UsingDecls.insert(Using);
+  } else if (const auto *UsingNamespace =
+                 Result.Nodes.getNodeAs<UsingDirectiveDecl>(
+                     "using_namespace")) {
+    UsingNamespaceDecls.insert(UsingNamespace);
+  } else if (const auto *NamespaceAlias =
+                 Result.Nodes.getNodeAs<NamespaceAliasDecl>(
+                     "namespace_alias")) {
+    NamespaceAliasDecls.insert(NamespaceAlias);
+  } else if (const auto *NsDecl =
+                 Result.Nodes.getNodeAs<NamespaceDecl>("old_ns")) {
+    moveOldNamespace(Result, NsDecl);
+  } else if (const auto *FwdDecl =
+                 Result.Nodes.getNodeAs<CXXRecordDecl>("class_fwd_decl")) {
+    moveClassForwardDeclaration(Result, cast<NamedDecl>(FwdDecl));
+  } else if (const auto *TemplateFwdDecl =
+                 Result.Nodes.getNodeAs<ClassTemplateDecl>(
+                     "template_class_fwd_decl")) {
+    moveClassForwardDeclaration(Result, cast<NamedDecl>(TemplateFwdDecl));
+  } else if (const auto *UsingWithShadow =
+                 Result.Nodes.getNodeAs<UsingDecl>("using_with_shadow")) {
+    fixUsingShadowDecl(Result, UsingWithShadow);
+  } else if (const auto *Specifier =
+                 Result.Nodes.getNodeAs<NestedNameSpecifierLoc>(
+                     "nested_specifier_loc")) {
+    SourceLocation Start = Specifier->getBeginLoc();
+    SourceLocation End = endLocationForType(Specifier->getTypeLoc());
+    fixTypeLoc(Result, Start, End, Specifier->getTypeLoc());
+  } else if (const auto *BaseInitializer =
+                 Result.Nodes.getNodeAs<CXXCtorInitializer>(
+                     "base_initializer")) {
+    BaseCtorInitializerTypeLocs.push_back(
+        BaseInitializer->getTypeSourceInfo()->getTypeLoc());
+  } else if (const auto *TLoc = Result.Nodes.getNodeAs<TypeLoc>("type")) {
+    // This avoids fixing types with record types as qualifier, which is not
+    // filtered by matchers in some cases, e.g. the type is templated. We should
+    // handle the record type qualifier instead.
+    TypeLoc Loc = *TLoc;
+    while (Loc.getTypeLocClass() == TypeLoc::Qualified)
+      Loc = Loc.getNextTypeLoc();
+    if (Loc.getTypeLocClass() == TypeLoc::Elaborated) {
+      NestedNameSpecifierLoc NestedNameSpecifier =
+          Loc.castAs<ElaboratedTypeLoc>().getQualifierLoc();
+      const Type *SpecifierType =
+          NestedNameSpecifier.getNestedNameSpecifier()->getAsType();
+      if (SpecifierType && SpecifierType->isRecordType())
+        return;
+    }
+    fixTypeLoc(Result, startLocationForType(Loc), endLocationForType(Loc), Loc);
+  } else if (const auto *VarRef =
+                 Result.Nodes.getNodeAs<DeclRefExpr>("var_ref")) {
+    const auto *Var = Result.Nodes.getNodeAs<VarDecl>("var_decl");
+    assert(Var);
+    if (Var->getCanonicalDecl()->isStaticDataMember())
+      return;
+    const auto *Context = Result.Nodes.getNodeAs<Decl>("dc");
+    assert(Context && "Empty decl context.");
+    fixDeclRefExpr(Result, Context->getDeclContext(),
+                   llvm::cast<NamedDecl>(Var), VarRef);
+  } else if (const auto *EnumConstRef =
+                 Result.Nodes.getNodeAs<DeclRefExpr>("enum_const_ref")) {
+    // Do not rename the reference if it is already scoped by the EnumDecl name.
+    if (EnumConstRef->hasQualifier() &&
+        EnumConstRef->getQualifier()->getKind() ==
+            NestedNameSpecifier::SpecifierKind::TypeSpec &&
+        EnumConstRef->getQualifier()->getAsType()->isEnumeralType())
+      return;
+    const auto *EnumConstDecl =
+        Result.Nodes.getNodeAs<EnumConstantDecl>("enum_const_decl");
+    assert(EnumConstDecl);
+    const auto *Context = Result.Nodes.getNodeAs<Decl>("dc");
+    assert(Context && "Empty decl context.");
+    // FIXME: this would qualify "ns::VALUE" as "ns::EnumValue::VALUE". Fix it
+    // if it turns out to be an issue.
+    fixDeclRefExpr(Result, Context->getDeclContext(),
+                   llvm::cast<NamedDecl>(EnumConstDecl), EnumConstRef);
+  } else if (const auto *FuncRef =
+                 Result.Nodes.getNodeAs<DeclRefExpr>("func_ref")) {
+    // If this reference has been processed as a function call, we do not
+    // process it again.
+    if (ProcessedFuncRefs.count(FuncRef))
+      return;
+    ProcessedFuncRefs.insert(FuncRef);
+    const auto *Func = Result.Nodes.getNodeAs<FunctionDecl>("func_decl");
+    assert(Func);
+    const auto *Context = Result.Nodes.getNodeAs<Decl>("dc");
+    assert(Context && "Empty decl context.");
+    fixDeclRefExpr(Result, Context->getDeclContext(),
+                   llvm::cast<NamedDecl>(Func), FuncRef);
+  } else {
+    const auto *Call = Result.Nodes.getNodeAs<CallExpr>("call");
+    assert(Call != nullptr && "Expecting callback for CallExpr.");
+    const auto *CalleeFuncRef =
+        llvm::cast<DeclRefExpr>(Call->getCallee()->IgnoreImplicit());
+    ProcessedFuncRefs.insert(CalleeFuncRef);
+    const FunctionDecl *Func = Call->getDirectCallee();
+    assert(Func != nullptr);
+    // FIXME: ignore overloaded operators. This would miss cases where operators
+    // are called by qualified names (i.e. "ns::operator <"). Ignore such
+    // cases for now.
+    if (Func->isOverloadedOperator())
+      return;
+    // Ignore out-of-line static methods since they will be handled by nested
+    // name specifiers.
+    if (Func->getCanonicalDecl()->getStorageClass() ==
+            StorageClass::SC_Static &&
+        Func->isOutOfLine())
+      return;
+    const auto *Context = Result.Nodes.getNodeAs<Decl>("dc");
+    assert(Context && "Empty decl context.");
+    SourceRange CalleeRange = Call->getCallee()->getSourceRange();
+    replaceQualifiedSymbolInDeclContext(
+        Result, Context->getDeclContext(), CalleeRange.getBegin(),
+        CalleeRange.getEnd(), llvm::cast<NamedDecl>(Func));
+  }
+}
+
+static SourceLocation getLocAfterNamespaceLBrace(const NamespaceDecl *NsDecl,
+                                                 const SourceManager &SM,
+                                                 const LangOptions &LangOpts) {
+  std::unique_ptr<Lexer> Lex =
+      getLexerStartingFromLoc(NsDecl->getLocStart(), SM, LangOpts);
+  assert(Lex.get() &&
+         "Failed to create lexer from the beginning of namespace.");
+  if (!Lex.get())
+    return SourceLocation();
+  Token Tok;
+  while (!Lex->LexFromRawLexer(Tok) && Tok.isNot(tok::TokenKind::l_brace)) {
+  }
+  return Tok.isNot(tok::TokenKind::l_brace)
+             ? SourceLocation()
+             : Tok.getEndLoc().getLocWithOffset(1);
+}
+
+// Stores information about a moved namespace in `MoveNamespaces` and leaves
+// the actual movement to `onEndOfTranslationUnit()`.
+void ChangeNamespaceTool::moveOldNamespace(
+    const ast_matchers::MatchFinder::MatchResult &Result,
+    const NamespaceDecl *NsDecl) {
+  // If the namespace is empty, do nothing.
+  if (Decl::castToDeclContext(NsDecl)->decls_empty())
+    return;
+
+  const SourceManager &SM = *Result.SourceManager;
+  // Get the range of the code in the old namespace.
+  SourceLocation Start =
+      getLocAfterNamespaceLBrace(NsDecl, SM, Result.Context->getLangOpts());
+  assert(Start.isValid() && "Can't find l_brace for namespace.");
+  MoveNamespace MoveNs;
+  MoveNs.Offset = SM.getFileOffset(Start);
+  // The range of the moved namespace is from the location just past the left
+  // brace to the location right before the right brace.
+  MoveNs.Length = SM.getFileOffset(NsDecl->getRBraceLoc()) - MoveNs.Offset;
+
+  // Insert the new namespace after `DiffOldNamespace`. For example, if
+  // `OldNamespace` is "a::b::c" and `NewNamespace` is `a::x::y`, then
+  // "x::y" will be inserted inside the existing namespace "a" and after "a::b".
+  // `OuterNs` is the first namespace in `DiffOldNamespace`, e.g. "namespace b"
+  // in the above example.
+  // If there is no outer namespace (i.e. DiffOldNamespace is empty), the new
+  // namespace will be a nested namespace in the old namespace.
+  const NamespaceDecl *OuterNs = getOuterNamespace(NsDecl, DiffOldNamespace);
+  SourceLocation InsertionLoc = Start;
+  if (OuterNs) {
+    SourceLocation LocAfterNs = getStartOfNextLine(
+        OuterNs->getRBraceLoc(), SM, Result.Context->getLangOpts());
+    assert(LocAfterNs.isValid() &&
+           "Failed to get location after DiffOldNamespace");
+    InsertionLoc = LocAfterNs;
+  }
+  MoveNs.InsertionOffset = SM.getFileOffset(SM.getSpellingLoc(InsertionLoc));
+  MoveNs.FID = SM.getFileID(Start);
+  MoveNs.SourceMgr = Result.SourceManager;
+  MoveNamespaces[SM.getFilename(Start)].push_back(MoveNs);
+}
+
+// Removes a class forward declaration from the code in the moved namespace and
+// creates an `InsertForwardDeclaration` to insert the forward declaration back
+// into the old namespace after moving code from the old namespace to the new
+// namespace.
+// For example, changing "a" to "x":
+// Old code:
+//   namespace a {
+//   class FWD;
+//   class A { FWD *fwd; }
+//   }  // a
+// New code:
+//   namespace a {
+//   class FWD;
+//   }  // a
+//   namespace x {
+//   class A { a::FWD *fwd; }
+//   }  // x
+void ChangeNamespaceTool::moveClassForwardDeclaration(
+    const ast_matchers::MatchFinder::MatchResult &Result,
+    const NamedDecl *FwdDecl) {
+  SourceLocation Start = FwdDecl->getLocStart();
+  SourceLocation End = FwdDecl->getLocEnd();
+  const SourceManager &SM = *Result.SourceManager;
+  SourceLocation AfterSemi = Lexer::findLocationAfterToken(
+      End, tok::semi, SM, Result.Context->getLangOpts(),
+      /*SkipTrailingWhitespaceAndNewLine=*/true);
+  if (AfterSemi.isValid())
+    End = AfterSemi.getLocWithOffset(-1);
+  // Delete the forward declaration from the code to be moved.
+  addReplacementOrDie(Start, End, "", SM, &FileToReplacements);
+  llvm::StringRef Code = Lexer::getSourceText(
+      CharSourceRange::getTokenRange(SM.getSpellingLoc(Start),
+                                     SM.getSpellingLoc(End)),
+      SM, Result.Context->getLangOpts());
+  // Insert the forward declaration back into the old namespace after moving the
+  // code from old namespace to new namespace.
+  // Insertion information is stored in `InsertFwdDecls` and actual
+  // insertion will be performed in `onEndOfTranslationUnit`.
+  // Get the (old) namespace that contains the forward declaration.
+  const auto *NsDecl = Result.Nodes.getNodeAs<NamespaceDecl>("ns_decl");
+  // The namespace contains the forward declaration, so it must not be empty.
+  assert(!NsDecl->decls_empty());
+  const auto Insertion = createInsertion(
+      getLocAfterNamespaceLBrace(NsDecl, SM, Result.Context->getLangOpts()),
+      Code, SM);
+  InsertForwardDeclaration InsertFwd;
+  InsertFwd.InsertionOffset = Insertion.getOffset();
+  InsertFwd.ForwardDeclText = Insertion.getReplacementText().str();
+  InsertFwdDecls[Insertion.getFilePath()].push_back(InsertFwd);
+}
+
+// Replaces a qualified symbol (in \p DeclCtx) that refers to a declaration \p
+// FromDecl with the shortest qualified name possible when the reference is in
+// `NewNamespace`.
+void ChangeNamespaceTool::replaceQualifiedSymbolInDeclContext(
+    const ast_matchers::MatchFinder::MatchResult &Result,
+    const DeclContext *DeclCtx, SourceLocation Start, SourceLocation End,
+    const NamedDecl *FromDecl) {
+  const auto *NsDeclContext = DeclCtx->getEnclosingNamespaceContext();
+  if (llvm::isa<TranslationUnitDecl>(NsDeclContext)) {
+    // This should not happen in usual unless the TypeLoc is in function type
+    // parameters, e.g `std::function<void(T)>`. In this case, DeclContext of
+    // `T` will be the translation unit. We simply use fully-qualified name
+    // here.
+    // Note that `FromDecl` must not be defined in the old namespace (according
+    // to `DeclMatcher`), so its fully-qualified name will not change after
+    // changing the namespace.
+    addReplacementOrDie(Start, End, FromDecl->getQualifiedNameAsString(),
+                        *Result.SourceManager, &FileToReplacements);
+    return;
+  }
+  const auto *NsDecl = llvm::cast<NamespaceDecl>(NsDeclContext);
+  // Calculate the name of the `NsDecl` after it is moved to new namespace.
+  std::string OldNs = NsDecl->getQualifiedNameAsString();
+  llvm::StringRef Postfix = OldNs;
+  bool Consumed = Postfix.consume_front(OldNamespace);
+  assert(Consumed && "Expect OldNS to start with OldNamespace.");
+  (void)Consumed;
+  const std::string NewNs = (NewNamespace + Postfix).str();
+
+  llvm::StringRef NestedName = Lexer::getSourceText(
+      CharSourceRange::getTokenRange(
+          Result.SourceManager->getSpellingLoc(Start),
+          Result.SourceManager->getSpellingLoc(End)),
+      *Result.SourceManager, Result.Context->getLangOpts());
+  std::string FromDeclName = FromDecl->getQualifiedNameAsString();
+  for (llvm::Regex &RE : WhiteListedSymbolRegexes)
+    if (RE.match(FromDeclName))
+      return;
+  std::string ReplaceName =
+      getShortestQualifiedNameInNamespace(FromDeclName, NewNs);
+  // Checks if there is any using namespace declarations that can shorten the
+  // qualified name.
+  for (const auto *UsingNamespace : UsingNamespaceDecls) {
+    if (!isDeclVisibleAtLocation(*Result.SourceManager, UsingNamespace, DeclCtx,
+                                 Start))
+      continue;
+    StringRef FromDeclNameRef = FromDeclName;
+    if (FromDeclNameRef.consume_front(UsingNamespace->getNominatedNamespace()
+                                          ->getQualifiedNameAsString())) {
+      FromDeclNameRef = FromDeclNameRef.drop_front(2);
+      if (FromDeclNameRef.size() < ReplaceName.size())
+        ReplaceName = FromDeclNameRef;
+    }
+  }
+  // Checks if there is any namespace alias declarations that can shorten the
+  // qualified name.
+  for (const auto *NamespaceAlias : NamespaceAliasDecls) {
+    if (!isDeclVisibleAtLocation(*Result.SourceManager, NamespaceAlias, DeclCtx,
+                                 Start))
+      continue;
+    StringRef FromDeclNameRef = FromDeclName;
+    if (FromDeclNameRef.consume_front(
+            NamespaceAlias->getNamespace()->getQualifiedNameAsString() +
+            "::")) {
+      std::string AliasName = NamespaceAlias->getNameAsString();
+      std::string AliasQualifiedName =
+          NamespaceAlias->getQualifiedNameAsString();
+      // We only consider namespace aliases define in the global namepspace or
+      // in namespaces that are directly visible from the reference, i.e.
+      // ancestor of the `OldNs`. Note that declarations in ancestor namespaces
+      // but not visible in the new namespace is filtered out by
+      // "IsVisibleInNewNs" matcher.
+      if (AliasQualifiedName != AliasName) {
+        // The alias is defined in some namespace.
+        assert(StringRef(AliasQualifiedName).endswith("::" + AliasName));
+        llvm::StringRef AliasNs =
+            StringRef(AliasQualifiedName).drop_back(AliasName.size() + 2);
+        if (!llvm::StringRef(OldNs).startswith(AliasNs))
+          continue;
+      }
+      std::string NameWithAliasNamespace =
+          (AliasName + "::" + FromDeclNameRef).str();
+      if (NameWithAliasNamespace.size() < ReplaceName.size())
+        ReplaceName = NameWithAliasNamespace;
+    }
+  }
+  // Checks if there is any using shadow declarations that can shorten the
+  // qualified name.
+  bool Matched = false;
+  for (const UsingDecl *Using : UsingDecls) {
+    if (Matched)
+      break;
+    if (isDeclVisibleAtLocation(*Result.SourceManager, Using, DeclCtx, Start)) {
+      for (const auto *UsingShadow : Using->shadows()) {
+        const auto *TargetDecl = UsingShadow->getTargetDecl();
+        if (TargetDecl->getQualifiedNameAsString() ==
+            FromDecl->getQualifiedNameAsString()) {
+          ReplaceName = FromDecl->getNameAsString();
+          Matched = true;
+          break;
+        }
+      }
+    }
+  }
+  // If the new nested name in the new namespace is the same as it was in the
+  // old namespace, we don't create replacement.
+  if (NestedName == ReplaceName ||
+      (NestedName.startswith("::") && NestedName.drop_front(2) == ReplaceName))
+    return;
+  // If the reference need to be fully-qualified, add a leading "::" unless
+  // NewNamespace is the global namespace.
+  if (ReplaceName == FromDeclName && !NewNamespace.empty() &&
+      conflictInNamespace(ReplaceName, NewNamespace))
+    ReplaceName = "::" + ReplaceName;
+  addReplacementOrDie(Start, End, ReplaceName, *Result.SourceManager,
+                      &FileToReplacements);
+}
+
+// Replace the [Start, End] of `Type` with the shortest qualified name when the
+// `Type` is in `NewNamespace`.
+void ChangeNamespaceTool::fixTypeLoc(
+    const ast_matchers::MatchFinder::MatchResult &Result, SourceLocation Start,
+    SourceLocation End, TypeLoc Type) {
+  // FIXME: do not rename template parameter.
+  if (Start.isInvalid() || End.isInvalid())
+    return;
+  // Types of CXXCtorInitializers do not need to be fixed.
+  if (llvm::is_contained(BaseCtorInitializerTypeLocs, Type))
+    return;
+  if (isTemplateParameter(Type))
+    return;
+  // The declaration which this TypeLoc refers to.
+  const auto *FromDecl = Result.Nodes.getNodeAs<NamedDecl>("from_decl");
+  // `hasDeclaration` gives underlying declaration, but if the type is
+  // a typedef type, we need to use the typedef type instead.
+  auto IsInMovedNs = [&](const NamedDecl *D) {
+    if (!llvm::StringRef(D->getQualifiedNameAsString())
+             .startswith(OldNamespace + "::"))
+      return false;
+    auto ExpansionLoc = Result.SourceManager->getExpansionLoc(D->getLocStart());
+    if (ExpansionLoc.isInvalid())
+      return false;
+    llvm::StringRef Filename = Result.SourceManager->getFilename(ExpansionLoc);
+    return FilePatternRE.match(Filename);
+  };
+  // Make `FromDecl` the immediate declaration that `Type` refers to, i.e. if
+  // `Type` is an alias type, we make `FromDecl` the type alias declaration.
+  // Also, don't fix the \p Type if it refers to a type alias decl in the moved
+  // namespace since the alias decl will be moved along with the type reference.
+  if (auto *Typedef = Type.getType()->getAs<TypedefType>()) {
+    FromDecl = Typedef->getDecl();
+    if (IsInMovedNs(FromDecl))
+      return;
+  } else if (auto *TemplateType =
+                 Type.getType()->getAs<TemplateSpecializationType>()) {
+    if (TemplateType->isTypeAlias()) {
+      FromDecl = TemplateType->getTemplateName().getAsTemplateDecl();
+      if (IsInMovedNs(FromDecl))
+        return;
+    }
+  }
+  const auto *DeclCtx = Result.Nodes.getNodeAs<Decl>("dc");
+  assert(DeclCtx && "Empty decl context.");
+  replaceQualifiedSymbolInDeclContext(Result, DeclCtx->getDeclContext(), Start,
+                                      End, FromDecl);
+}
+
+void ChangeNamespaceTool::fixUsingShadowDecl(
+    const ast_matchers::MatchFinder::MatchResult &Result,
+    const UsingDecl *UsingDeclaration) {
+  SourceLocation Start = UsingDeclaration->getLocStart();
+  SourceLocation End = UsingDeclaration->getLocEnd();
+  if (Start.isInvalid() || End.isInvalid())
+    return;
+
+  assert(UsingDeclaration->shadow_size() > 0);
+  // FIXME: it might not be always accurate to use the first using-decl.
+  const NamedDecl *TargetDecl =
+      UsingDeclaration->shadow_begin()->getTargetDecl();
+  std::string TargetDeclName = TargetDecl->getQualifiedNameAsString();
+  // FIXME: check if target_decl_name is in moved ns, which doesn't make much
+  // sense. If this happens, we need to use name with the new namespace.
+  // Use fully qualified name in UsingDecl for now.
+  addReplacementOrDie(Start, End, "using ::" + TargetDeclName,
+                      *Result.SourceManager, &FileToReplacements);
+}
+
+void ChangeNamespaceTool::fixDeclRefExpr(
+    const ast_matchers::MatchFinder::MatchResult &Result,
+    const DeclContext *UseContext, const NamedDecl *From,
+    const DeclRefExpr *Ref) {
+  SourceRange RefRange = Ref->getSourceRange();
+  replaceQualifiedSymbolInDeclContext(Result, UseContext, RefRange.getBegin(),
+                                      RefRange.getEnd(), From);
+}
+
+void ChangeNamespaceTool::onEndOfTranslationUnit() {
+  // Move namespace blocks and insert forward declaration to old namespace.
+  for (const auto &FileAndNsMoves : MoveNamespaces) {
+    auto &NsMoves = FileAndNsMoves.second;
+    if (NsMoves.empty())
+      continue;
+    const std::string &FilePath = FileAndNsMoves.first;
+    auto &Replaces = FileToReplacements[FilePath];
+    auto &SM = *NsMoves.begin()->SourceMgr;
+    llvm::StringRef Code = SM.getBufferData(NsMoves.begin()->FID);
+    auto ChangedCode = tooling::applyAllReplacements(Code, Replaces);
+    if (!ChangedCode) {
+      llvm::errs() << llvm::toString(ChangedCode.takeError()) << "\n";
+      continue;
+    }
+    // Replacements on the changed code for moving namespaces and inserting
+    // forward declarations to old namespaces.
+    tooling::Replacements NewReplacements;
+    // Cut the changed code from the old namespace and paste the code in the new
+    // namespace.
+    for (const auto &NsMove : NsMoves) {
+      // Calculate the range of the old namespace block in the changed
+      // code.
+      const unsigned NewOffset = Replaces.getShiftedCodePosition(NsMove.Offset);
+      const unsigned NewLength =
+          Replaces.getShiftedCodePosition(NsMove.Offset + NsMove.Length) -
+          NewOffset;
+      tooling::Replacement Deletion(FilePath, NewOffset, NewLength, "");
+      std::string MovedCode = ChangedCode->substr(NewOffset, NewLength);
+      std::string MovedCodeWrappedInNewNs =
+          wrapCodeInNamespace(DiffNewNamespace, MovedCode);
+      // Calculate the new offset at which the code will be inserted in the
+      // changed code.
+      unsigned NewInsertionOffset =
+          Replaces.getShiftedCodePosition(NsMove.InsertionOffset);
+      tooling::Replacement Insertion(FilePath, NewInsertionOffset, 0,
+                                     MovedCodeWrappedInNewNs);
+      addOrMergeReplacement(Deletion, &NewReplacements);
+      addOrMergeReplacement(Insertion, &NewReplacements);
+    }
+    // After moving namespaces, insert forward declarations back to old
+    // namespaces.
+    const auto &FwdDeclInsertions = InsertFwdDecls[FilePath];
+    for (const auto &FwdDeclInsertion : FwdDeclInsertions) {
+      unsigned NewInsertionOffset =
+          Replaces.getShiftedCodePosition(FwdDeclInsertion.InsertionOffset);
+      tooling::Replacement Insertion(FilePath, NewInsertionOffset, 0,
+                                     FwdDeclInsertion.ForwardDeclText);
+      addOrMergeReplacement(Insertion, &NewReplacements);
+    }
+    // Add replacements referring to the changed code to existing replacements,
+    // which refers to the original code.
+    Replaces = Replaces.merge(NewReplacements);
+    auto Style = format::getStyle("file", FilePath, FallbackStyle);
+    if (!Style) {
+      llvm::errs() << llvm::toString(Style.takeError()) << "\n";
+      continue;
+    }
+    // Clean up old namespaces if there is nothing in it after moving.
+    auto CleanReplacements =
+        format::cleanupAroundReplacements(Code, Replaces, *Style);
+    if (!CleanReplacements) {
+      llvm::errs() << llvm::toString(CleanReplacements.takeError()) << "\n";
+      continue;
+    }
+    FileToReplacements[FilePath] = *CleanReplacements;
+  }
+
+  // Make sure we don't generate replacements for files that do not match
+  // FilePattern.
+  for (auto &Entry : FileToReplacements)
+    if (!FilePatternRE.match(Entry.first))
+      Entry.second.clear();
+}
+
+} // namespace change_namespace
+} // namespace clang
diff --git a/change-namespace/ChangeNamespace.h b/change-namespace/ChangeNamespace.h
new file mode 100644 (file)
index 0000000..cfe3cbc
--- /dev/null
@@ -0,0 +1,176 @@
+//===-- ChangeNamespace.h -- Change namespace  ------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CHANGE_NAMESPACE_CHANGENAMESPACE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CHANGE_NAMESPACE_CHANGENAMESPACE_H
+
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Format/Format.h"
+#include "clang/Tooling/Core/Replacement.h"
+#include "llvm/Support/Regex.h"
+#include <string>
+
+namespace clang {
+namespace change_namespace {
+
+// This tool can be used to change the surrounding namespaces of class/function
+// definitions. Classes/functions in the moved namespace will have new
+// namespaces while references to symbols (e.g. types, functions) which are not
+// defined in the changed namespace will be correctly qualified by prepending
+// namespace specifiers before them.
+// This will try to add shortest namespace specifiers possible. When a symbol
+// reference needs to be fully-qualified, this adds a "::" prefix to the
+// namespace specifiers unless the new namespace is the global namespace.
+// For classes, only classes that are declared/defined in the given namespace in
+// speficifed files will be moved: forward declarations will remain in the old
+// namespace.
+// For example, changing "a" to "x":
+// Old code:
+//   namespace a {
+//   class FWD;
+//   class A { FWD *fwd; }
+//   }  // a
+// New code:
+//   namespace a {
+//   class FWD;
+//   }  // a
+//   namespace x {
+//   class A { ::a::FWD *fwd; }
+//   }  // x
+// FIXME: support moving typedef, enums across namespaces.
+class ChangeNamespaceTool : public ast_matchers::MatchFinder::MatchCallback {
+public:
+  // Moves code in the old namespace `OldNs` to the new namespace `NewNs` in
+  // files matching `FilePattern`.
+  ChangeNamespaceTool(
+      llvm::StringRef OldNs, llvm::StringRef NewNs, llvm::StringRef FilePattern,
+      llvm::ArrayRef<std::string> WhiteListedSymbolPatterns,
+      std::map<std::string, tooling::Replacements> *FileToReplacements,
+      llvm::StringRef FallbackStyle = "LLVM");
+
+  void registerMatchers(ast_matchers::MatchFinder *Finder);
+
+  void run(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+  // Moves the changed code in old namespaces but leaves class forward
+  // declarations behind.
+  void onEndOfTranslationUnit() override;
+
+private:
+  void moveOldNamespace(const ast_matchers::MatchFinder::MatchResult &Result,
+                        const NamespaceDecl *NsDecl);
+
+  void moveClassForwardDeclaration(
+      const ast_matchers::MatchFinder::MatchResult &Result,
+      const NamedDecl *FwdDecl);
+
+  void replaceQualifiedSymbolInDeclContext(
+      const ast_matchers::MatchFinder::MatchResult &Result,
+      const DeclContext *DeclContext, SourceLocation Start, SourceLocation End,
+      const NamedDecl *FromDecl);
+
+  void fixTypeLoc(const ast_matchers::MatchFinder::MatchResult &Result,
+                  SourceLocation Start, SourceLocation End, TypeLoc Type);
+
+  void fixUsingShadowDecl(const ast_matchers::MatchFinder::MatchResult &Result,
+                          const UsingDecl *UsingDeclaration);
+
+  void fixDeclRefExpr(const ast_matchers::MatchFinder::MatchResult &Result,
+                      const DeclContext *UseContext, const NamedDecl *From,
+                      const DeclRefExpr *Ref);
+
+  // Information about moving an old namespace.
+  struct MoveNamespace {
+    // The start offset of the namespace block being moved in the original
+    // code.
+    unsigned Offset;
+    // The length of the namespace block in the original code.
+    unsigned Length;
+    // The offset at which the new namespace block will be inserted in the
+    // original code.
+    unsigned InsertionOffset;
+    // The file in which the namespace is declared.
+    FileID FID;
+    SourceManager *SourceMgr;
+  };
+
+  // Information about inserting a class forward declaration.
+  struct InsertForwardDeclaration {
+    // The offset at while the forward declaration will be inserted in the
+    // original code.
+    unsigned InsertionOffset;
+    // The code to be inserted.
+    std::string ForwardDeclText;
+  };
+
+  std::string FallbackStyle;
+  // In match callbacks, this contains replacements for replacing `typeLoc`s in
+  // and deleting forward declarations in the moved namespace blocks.
+  // In `onEndOfTranslationUnit` callback, the previous added replacements are
+  // applied (on the moved namespace blocks), and then changed code in old
+  // namespaces re moved to new namespaces, and previously deleted forward
+  // declarations are inserted back to old namespaces, from which they are
+  // deleted.
+  std::map<std::string, tooling::Replacements> &FileToReplacements;
+  // A fully qualified name of the old namespace without "::" prefix, e.g.
+  // "a::b::c".
+  std::string OldNamespace;
+  // A fully qualified name of the new namespace without "::" prefix, e.g.
+  // "x::y::z".
+  std::string NewNamespace;
+  // The longest suffix in the old namespace that does not overlap the new
+  // namespace.
+  // For example, if `OldNamespace` is "a::b::c" and `NewNamespace` is
+  // "a::x::y", then `DiffOldNamespace` will be "b::c".
+  std::string DiffOldNamespace;
+  // The longest suffix in the new namespace that does not overlap the old
+  // namespace.
+  // For example, if `OldNamespace` is "a::b::c" and `NewNamespace` is
+  // "a::x::y", then `DiffNewNamespace` will be "x::y".
+  std::string DiffNewNamespace;
+  // A regex pattern that matches files to be processed.
+  std::string FilePattern;
+  llvm::Regex FilePatternRE;
+  // Information about moved namespaces grouped by file.
+  // Since we are modifying code in old namespaces (e.g. add namespace
+  // spedifiers) as well as moving them, we store information about namespaces
+  // to be moved and only move them after all modifications are finished (i.e.
+  // in `onEndOfTranslationUnit`).
+  std::map<std::string, std::vector<MoveNamespace>> MoveNamespaces;
+  // Information about forward declaration insertions grouped by files.
+  // A class forward declaration is not moved, so it will be deleted from the
+  // moved code block and inserted back into the old namespace. The insertion
+  // will be done after removing the code from the old namespace and before
+  // inserting it to the new namespace.
+  std::map<std::string, std::vector<InsertForwardDeclaration>> InsertFwdDecls;
+  // Records all using declarations, which can be used to shorten namespace
+  // specifiers.
+  llvm::SmallPtrSet<const UsingDecl *, 8> UsingDecls;
+  // Records all using namespace declarations, which can be used to shorten
+  // namespace specifiers.
+  llvm::SmallPtrSet<const UsingDirectiveDecl *, 8> UsingNamespaceDecls;
+  // Records all namespace alias declarations, which can be used to shorten
+  // namespace specifiers.
+  llvm::SmallPtrSet<const NamespaceAliasDecl *, 8> NamespaceAliasDecls;
+  // TypeLocs of CXXCtorInitializer. Types of CXXCtorInitializers do not need to
+  // be fixed.
+  llvm::SmallVector<TypeLoc, 8> BaseCtorInitializerTypeLocs;
+  // Since a DeclRefExpr for a function call can be matched twice (one as
+  // CallExpr and one as DeclRefExpr), we record all DeclRefExpr's that have
+  // been processed so that we don't handle them twice.
+  llvm::SmallPtrSet<const clang::DeclRefExpr*, 16> ProcessedFuncRefs;
+  // Patterns of symbol names whose references are not expected to be updated
+  // when changing namespaces around them.
+  std::vector<llvm::Regex> WhiteListedSymbolRegexes;
+};
+
+} // namespace change_namespace
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CHANGE_NAMESPACE_CHANGENAMESPACE_H
diff --git a/change-namespace/tool/CMakeLists.txt b/change-namespace/tool/CMakeLists.txt
new file mode 100644 (file)
index 0000000..62c412a
--- /dev/null
@@ -0,0 +1,23 @@
+include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..)
+
+set(LLVM_LINK_COMPONENTS
+  Support
+  )
+
+add_clang_executable(clang-change-namespace
+  ClangChangeNamespace.cpp
+  )
+target_link_libraries(clang-change-namespace
+  clangAST
+  clangASTMatchers
+  clangBasic
+  clangChangeNamespace
+  clangFormat
+  clangFrontend
+  clangRewrite
+  clangTooling
+  clangToolingCore
+  )
+
+install(TARGETS clang-change-namespace
+  RUNTIME DESTINATION bin)
diff --git a/change-namespace/tool/ClangChangeNamespace.cpp b/change-namespace/tool/ClangChangeNamespace.cpp
new file mode 100644 (file)
index 0000000..180e8c3
--- /dev/null
@@ -0,0 +1,178 @@
+//===-- ClangIncludeFixer.cpp - Standalone change namespace ---------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+// This tool can be used to change the surrounding namespaces of class/function
+// definitions.
+//
+// Example: test.cc
+//    namespace na {
+//    class X {};
+//    namespace nb {
+//    class Y { X x; };
+//    } // namespace nb
+//    } // namespace na
+// To move the definition of class Y from namespace "na::nb" to "x::y", run:
+//    clang-change-namespace --old_namespace "na::nb" \
+//      --new_namespace "x::y" --file_pattern "test.cc" test.cc --
+// Output:
+//    namespace na {
+//    class X {};
+//    } // namespace na
+//    namespace x {
+//    namespace y {
+//    class Y { na::X x; };
+//    } // namespace y
+//    } // namespace x
+
+#include "ChangeNamespace.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Frontend/FrontendActions.h"
+#include "clang/Frontend/TextDiagnosticPrinter.h"
+#include "clang/Rewrite/Core/Rewriter.h"
+#include "clang/Tooling/CommonOptionsParser.h"
+#include "clang/Tooling/Refactoring.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Signals.h"
+#include "llvm/Support/YAMLTraits.h"
+
+using namespace clang;
+using namespace llvm;
+
+namespace {
+
+cl::OptionCategory ChangeNamespaceCategory("Change namespace.");
+
+cl::opt<std::string> OldNamespace("old_namespace", cl::Required,
+                                  cl::desc("Old namespace."),
+                                  cl::cat(ChangeNamespaceCategory));
+
+cl::opt<std::string> NewNamespace("new_namespace", cl::Required,
+                                  cl::desc("New namespace."),
+                                  cl::cat(ChangeNamespaceCategory));
+
+cl::opt<std::string> FilePattern(
+    "file_pattern", cl::Required,
+    cl::desc("Only rename namespaces in files that match the given pattern."),
+    cl::cat(ChangeNamespaceCategory));
+
+cl::opt<bool> Inplace("i", cl::desc("Inplace edit <file>s, if specified."),
+                      cl::cat(ChangeNamespaceCategory));
+
+cl::opt<bool>
+    DumpYAML("dump_result",
+         cl::desc("Dump new file contents in YAML, if specified."),
+         cl::cat(ChangeNamespaceCategory));
+
+cl::opt<std::string> Style("style",
+                           cl::desc("The style name used for reformatting."),
+                           cl::init("LLVM"), cl::cat(ChangeNamespaceCategory));
+
+cl::opt<std::string> WhiteListFile(
+    "whitelist_file",
+    cl::desc("A file containing regexes of symbol names that are not expected "
+             "to be updated when changing namespaces around them."),
+    cl::init(""), cl::cat(ChangeNamespaceCategory));
+
+llvm::ErrorOr<std::vector<std::string>> GetWhiteListedSymbolPatterns() {
+  std::vector<std::string> Patterns;
+  if (WhiteListFile.empty())
+    return Patterns;
+
+  llvm::SmallVector<StringRef, 8> Lines;
+  llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> File =
+      llvm::MemoryBuffer::getFile(WhiteListFile);
+  if (!File)
+    return File.getError();
+  llvm::StringRef Content = File.get()->getBuffer();
+  Content.split(Lines, '\n', /*MaxSplit=*/-1, /*KeepEmpty=*/false);
+  for (auto Line : Lines)
+    Patterns.push_back(Line.trim());
+  return Patterns;
+}
+
+} // anonymous namespace
+
+int main(int argc, const char **argv) {
+  llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
+  tooling::CommonOptionsParser OptionsParser(argc, argv,
+                                             ChangeNamespaceCategory);
+  const auto &Files = OptionsParser.getSourcePathList();
+  tooling::RefactoringTool Tool(OptionsParser.getCompilations(), Files);
+  llvm::ErrorOr<std::vector<std::string>> WhiteListPatterns =
+      GetWhiteListedSymbolPatterns();
+  if (!WhiteListPatterns) {
+    llvm::errs() << "Failed to open whitelist file " << WhiteListFile << ". "
+                 << WhiteListPatterns.getError().message() << "\n";
+    return 1;
+  }
+  change_namespace::ChangeNamespaceTool NamespaceTool(
+      OldNamespace, NewNamespace, FilePattern, *WhiteListPatterns,
+      &Tool.getReplacements(), Style);
+  ast_matchers::MatchFinder Finder;
+  NamespaceTool.registerMatchers(&Finder);
+  std::unique_ptr<tooling::FrontendActionFactory> Factory =
+      tooling::newFrontendActionFactory(&Finder);
+
+  if (int Result = Tool.run(Factory.get()))
+    return Result;
+  LangOptions DefaultLangOptions;
+  IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
+  clang::TextDiagnosticPrinter DiagnosticPrinter(errs(), &*DiagOpts);
+  DiagnosticsEngine Diagnostics(
+      IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), &*DiagOpts,
+      &DiagnosticPrinter, false);
+  auto &FileMgr = Tool.getFiles();
+  SourceManager Sources(Diagnostics, FileMgr);
+  Rewriter Rewrite(Sources, DefaultLangOptions);
+
+  if (!formatAndApplyAllReplacements(Tool.getReplacements(), Rewrite, Style)) {
+    llvm::errs() << "Failed applying all replacements.\n";
+    return 1;
+  }
+  if (Inplace)
+    return Rewrite.overwriteChangedFiles();
+
+  std::set<llvm::StringRef> ChangedFiles;
+  for (const auto &it : Tool.getReplacements())
+    ChangedFiles.insert(it.first);
+
+  if (DumpYAML) {
+    auto WriteToYAML = [&](llvm::raw_ostream &OS) {
+      OS << "[\n";
+      for (auto I = ChangedFiles.begin(), E = ChangedFiles.end(); I != E; ++I) {
+        OS << "  {\n";
+        OS << "    \"FilePath\": \"" << *I << "\",\n";
+        const auto *Entry = FileMgr.getFile(*I);
+        auto ID = Sources.getOrCreateFileID(Entry, SrcMgr::C_User);
+        std::string Content;
+        llvm::raw_string_ostream ContentStream(Content);
+        Rewrite.getEditBuffer(ID).write(ContentStream);
+        OS << "    \"SourceText\": \""
+           << llvm::yaml::escape(ContentStream.str()) << "\"\n";
+        OS << "  }";
+        if (I != std::prev(E))
+          OS << ",\n";
+      }
+      OS << "\n]\n";
+    };
+    WriteToYAML(llvm::outs());
+    return 0;
+  }
+
+  for (const auto &File : ChangedFiles) {
+    const auto *Entry = FileMgr.getFile(File);
+
+    auto ID = Sources.getOrCreateFileID(Entry, SrcMgr::C_User);
+    outs() << "============== " << File << " ==============\n";
+    Rewrite.getEditBuffer(ID).write(llvm::outs());
+    outs() << "\n============================================\n";
+  }
+
+  return 0;
+}
diff --git a/clang-apply-replacements/CMakeLists.txt b/clang-apply-replacements/CMakeLists.txt
new file mode 100644 (file)
index 0000000..5366e02
--- /dev/null
@@ -0,0 +1,19 @@
+set(LLVM_LINK_COMPONENTS
+  Support
+  )
+
+add_clang_library(clangApplyReplacements
+  lib/Tooling/ApplyReplacements.cpp
+
+  LINK_LIBS
+  clangAST
+  clangBasic
+  clangRewrite
+  clangToolingCore
+  )
+
+include_directories(
+  ${CMAKE_CURRENT_SOURCE_DIR}
+  include
+  )
+add_subdirectory(tool)
diff --git a/clang-apply-replacements/include/clang-apply-replacements/Tooling/ApplyReplacements.h b/clang-apply-replacements/include/clang-apply-replacements/Tooling/ApplyReplacements.h
new file mode 100644 (file)
index 0000000..5e0ff48
--- /dev/null
@@ -0,0 +1,155 @@
+//===-- ApplyReplacements.h - Deduplicate and apply replacements -- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief This file provides the interface for deduplicating, detecting
+/// conflicts in, and applying collections of Replacements.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_APPLYREPLACEMENTS_H
+#define LLVM_CLANG_APPLYREPLACEMENTS_H
+
+#include "clang/Tooling/Core/Diagnostic.h"
+#include "clang/Tooling/Refactoring.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/StringRef.h"
+#include <string>
+#include <system_error>
+#include <vector>
+
+namespace clang {
+
+class DiagnosticsEngine;
+class Rewriter;
+
+namespace format {
+struct FormatStyle;
+} // end namespace format
+
+namespace replace {
+
+/// \brief Collection of source ranges.
+typedef std::vector<clang::tooling::Range> RangeVector;
+
+/// \brief Collection of TranslationUnitReplacements.
+typedef std::vector<clang::tooling::TranslationUnitReplacements> TUReplacements;
+
+/// \brief Collection of TranslationUnitReplacement files.
+typedef std::vector<std::string> TUReplacementFiles;
+
+/// \brief Collection of TranslationUniDiagnostics.
+typedef std::vector<clang::tooling::TranslationUnitDiagnostics> TUDiagnostics;
+
+/// \brief Map mapping file name to Replacements targeting that file.
+typedef llvm::DenseMap<const clang::FileEntry *,
+                       std::vector<clang::tooling::Replacement>>
+    FileToReplacementsMap;
+
+/// \brief Recursively descends through a directory structure rooted at \p
+/// Directory and attempts to deserialize *.yaml files as
+/// TranslationUnitReplacements. All docs that successfully deserialize are
+/// added to \p TUs.
+///
+/// Directories starting with '.' are ignored during traversal.
+///
+/// \param[in] Directory Directory to begin search for serialized
+/// TranslationUnitReplacements.
+/// \param[out] TUs Collection of all found and deserialized
+/// TranslationUnitReplacements or TranslationUnitDiagnostics.
+/// \param[out] TUFiles Collection of all TranslationUnitReplacement files
+/// found in \c Directory.
+/// \param[in] Diagnostics DiagnosticsEngine used for error output.
+///
+/// \returns An error_code indicating success or failure in navigating the
+/// directory structure.
+std::error_code collectReplacementsFromDirectory(
+    const llvm::StringRef Directory, TUReplacements &TUs,
+    TUReplacementFiles &TUFiles, clang::DiagnosticsEngine &Diagnostics);
+
+std::error_code collectReplacementsFromDirectory(
+    const llvm::StringRef Directory, TUDiagnostics &TUs,
+    TUReplacementFiles &TUFiles, clang::DiagnosticsEngine &Diagnostics);
+
+/// \brief Deduplicate, check for conflicts, and apply all Replacements stored
+/// in \c TUs. If conflicts occur, no Replacements are applied.
+///
+/// \post For all (key,value) in GroupedReplacements, value[i].getOffset() <=
+/// value[i+1].getOffset().
+///
+/// \param[in] TUs Collection of TranslationUnitReplacements or
+/// TranslationUnitDiagnostics to merge,
+/// deduplicate, and test for conflicts.
+/// \param[out] GroupedReplacements Container grouping all Replacements by the
+/// file they target.
+/// \param[in] SM SourceManager required for conflict reporting.
+///
+/// \returns \parblock
+///          \li true If all changes were applied successfully.
+///          \li false If there were conflicts.
+bool mergeAndDeduplicate(const TUReplacements &TUs,
+                         FileToReplacementsMap &GroupedReplacements,
+                         clang::SourceManager &SM);
+
+bool mergeAndDeduplicate(const TUDiagnostics &TUs,
+                         FileToReplacementsMap &GroupedReplacements,
+                         clang::SourceManager &SM);
+
+// FIXME: Remove this function after changing clang-apply-replacements to use
+// Replacements class.
+bool applyAllReplacements(const std::vector<tooling::Replacement> &Replaces,
+                          Rewriter &Rewrite);
+
+/// \brief Apply all replacements in \c GroupedReplacements.
+///
+/// \param[in] GroupedReplacements Deduplicated and conflict free Replacements
+/// to apply.
+/// \param[out] Rewrites The results of applying replacements will be applied
+/// to this Rewriter.
+///
+/// \returns \parblock
+///          \li true If all changes were applied successfully.
+///          \li false If a replacement failed to apply.
+bool applyReplacements(const FileToReplacementsMap &GroupedReplacements,
+                       clang::Rewriter &Rewrites);
+
+/// \brief Given a collection of Replacements for a single file, produces a list
+/// of source ranges that enclose those Replacements.
+///
+/// \pre Replacements[i].getOffset() <= Replacements[i+1].getOffset().
+///
+/// \param[in] Replacements Replacements from a single file.
+///
+/// \returns Collection of source ranges that enclose all given Replacements.
+/// One range is created for each replacement.
+RangeVector calculateChangedRanges(
+    const std::vector<clang::tooling::Replacement> &Replacements);
+
+/// \brief Write the contents of \c FileContents to disk. Keys of the map are
+/// filenames and values are the new contents for those files.
+///
+/// \param[in] Rewrites Rewriter containing written files to write to disk.
+bool writeFiles(const clang::Rewriter &Rewrites);
+
+/// \brief Delete the replacement files.
+///
+/// \param[in] Files Replacement files to delete.
+/// \param[in] Diagnostics DiagnosticsEngine used for error output.
+///
+/// \returns \parblock
+///          \li true If all files have been deleted successfully.
+///          \li false If at least one or more failures occur when deleting
+/// files.
+bool deleteReplacementFiles(const TUReplacementFiles &Files,
+                            clang::DiagnosticsEngine &Diagnostics);
+
+} // end namespace replace
+} // end namespace clang
+
+#endif // LLVM_CLANG_APPLYREPLACEMENTS_H
diff --git a/clang-apply-replacements/lib/Tooling/ApplyReplacements.cpp b/clang-apply-replacements/lib/Tooling/ApplyReplacements.cpp
new file mode 100644 (file)
index 0000000..52234fe
--- /dev/null
@@ -0,0 +1,402 @@
+//===-- ApplyReplacements.cpp - Apply and deduplicate replacements --------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief This file provides the implementation for deduplicating, detecting
+/// conflicts in, and applying collections of Replacements.
+///
+/// FIXME: Use Diagnostics for output instead of llvm::errs().
+///
+//===----------------------------------------------------------------------===//
+#include "clang-apply-replacements/Tooling/ApplyReplacements.h"
+#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Format/Format.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Rewrite/Core/Rewriter.h"
+#include "clang/Tooling/DiagnosticsYaml.h"
+#include "clang/Tooling/ReplacementsYaml.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace llvm;
+using namespace clang;
+
+static void eatDiagnostics(const SMDiagnostic &, void *) {}
+
+namespace clang {
+namespace replace {
+
+std::error_code collectReplacementsFromDirectory(
+    const llvm::StringRef Directory, TUReplacements &TUs,
+    TUReplacementFiles &TUFiles, clang::DiagnosticsEngine &Diagnostics) {
+  using namespace llvm::sys::fs;
+  using namespace llvm::sys::path;
+
+  std::error_code ErrorCode;
+
+  for (recursive_directory_iterator I(Directory, ErrorCode), E;
+       I != E && !ErrorCode; I.increment(ErrorCode)) {
+    if (filename(I->path())[0] == '.') {
+      // Indicate not to descend into directories beginning with '.'
+      I.no_push();
+      continue;
+    }
+
+    if (extension(I->path()) != ".yaml")
+      continue;
+
+    TUFiles.push_back(I->path());
+
+    ErrorOr<std::unique_ptr<MemoryBuffer>> Out =
+        MemoryBuffer::getFile(I->path());
+    if (std::error_code BufferError = Out.getError()) {
+      errs() << "Error reading " << I->path() << ": " << BufferError.message()
+             << "\n";
+      continue;
+    }
+
+    yaml::Input YIn(Out.get()->getBuffer(), nullptr, &eatDiagnostics);
+    tooling::TranslationUnitReplacements TU;
+    YIn >> TU;
+    if (YIn.error()) {
+      // File doesn't appear to be a header change description. Ignore it.
+      continue;
+    }
+
+    // Only keep files that properly parse.
+    TUs.push_back(TU);
+  }
+
+  return ErrorCode;
+}
+
+std::error_code
+collectReplacementsFromDirectory(const llvm::StringRef Directory,
+                                 TUDiagnostics &TUs, TUReplacementFiles &TUFiles,
+                                 clang::DiagnosticsEngine &Diagnostics) {
+  using namespace llvm::sys::fs;
+  using namespace llvm::sys::path;
+
+  std::error_code ErrorCode;
+
+  for (recursive_directory_iterator I(Directory, ErrorCode), E;
+       I != E && !ErrorCode; I.increment(ErrorCode)) {
+    if (filename(I->path())[0] == '.') {
+      // Indicate not to descend into directories beginning with '.'
+      I.no_push();
+      continue;
+    }
+
+    if (extension(I->path()) != ".yaml")
+      continue;
+
+    TUFiles.push_back(I->path());
+
+    ErrorOr<std::unique_ptr<MemoryBuffer>> Out =
+        MemoryBuffer::getFile(I->path());
+    if (std::error_code BufferError = Out.getError()) {
+      errs() << "Error reading " << I->path() << ": " << BufferError.message()
+             << "\n";
+      continue;
+    }
+
+    yaml::Input YIn(Out.get()->getBuffer(), nullptr, &eatDiagnostics);
+    tooling::TranslationUnitDiagnostics TU;
+    YIn >> TU;
+    if (YIn.error()) {
+      // File doesn't appear to be a header change description. Ignore it.
+      continue;
+    }
+
+    // Only keep files that properly parse.
+    TUs.push_back(TU);
+  }
+
+  return ErrorCode;
+}
+
+/// \brief Dumps information for a sequence of conflicting Replacements.
+///
+/// \param[in] File FileEntry for the file the conflicting Replacements are
+/// for.
+/// \param[in] ConflictingReplacements List of conflicting Replacements.
+/// \param[in] SM SourceManager used for reporting.
+static void reportConflict(
+    const FileEntry *File,
+    const llvm::ArrayRef<clang::tooling::Replacement> ConflictingReplacements,
+    SourceManager &SM) {
+  FileID FID = SM.translateFile(File);
+  if (FID.isInvalid())
+    FID = SM.createFileID(File, SourceLocation(), SrcMgr::C_User);
+
+  // FIXME: Output something a little more user-friendly (e.g. unified diff?)
+  errs() << "The following changes conflict:\n";
+  for (const tooling::Replacement &R : ConflictingReplacements) {
+    if (R.getLength() == 0) {
+      errs() << "  Insert at " << SM.getLineNumber(FID, R.getOffset()) << ":"
+             << SM.getColumnNumber(FID, R.getOffset()) << " "
+             << R.getReplacementText() << "\n";
+    } else {
+      if (R.getReplacementText().empty())
+        errs() << "  Remove ";
+      else
+        errs() << "  Replace ";
+
+      errs() << SM.getLineNumber(FID, R.getOffset()) << ":"
+             << SM.getColumnNumber(FID, R.getOffset()) << "-"
+             << SM.getLineNumber(FID, R.getOffset() + R.getLength() - 1) << ":"
+             << SM.getColumnNumber(FID, R.getOffset() + R.getLength() - 1);
+
+      if (R.getReplacementText().empty())
+        errs() << "\n";
+      else
+        errs() << " with \"" << R.getReplacementText() << "\"\n";
+    }
+  }
+}
+
+// FIXME: Remove this function after changing clang-apply-replacements to use
+// Replacements class.
+bool applyAllReplacements(const std::vector<tooling::Replacement> &Replaces,
+                          Rewriter &Rewrite) {
+  bool Result = true;
+  for (auto I = Replaces.begin(), E = Replaces.end(); I != E; ++I) {
+    if (I->isApplicable()) {
+      Result = I->apply(Rewrite) && Result;
+    } else {
+      Result = false;
+    }
+  }
+  return Result;
+}
+
+// FIXME: moved from libToolingCore. remove this when std::vector<Replacement>
+// is replaced with tooling::Replacements class.
+static void deduplicate(std::vector<tooling::Replacement> &Replaces,
+                        std::vector<tooling::Range> &Conflicts) {
+  if (Replaces.empty())
+    return;
+
+  auto LessNoPath = [](const tooling::Replacement &LHS,
+                       const tooling::Replacement &RHS) {
+    if (LHS.getOffset() != RHS.getOffset())
+      return LHS.getOffset() < RHS.getOffset();
+    if (LHS.getLength() != RHS.getLength())
+      return LHS.getLength() < RHS.getLength();
+    return LHS.getReplacementText() < RHS.getReplacementText();
+  };
+
+  auto EqualNoPath = [](const tooling::Replacement &LHS,
+                        const tooling::Replacement &RHS) {
+    return LHS.getOffset() == RHS.getOffset() &&
+           LHS.getLength() == RHS.getLength() &&
+           LHS.getReplacementText() == RHS.getReplacementText();
+  };
+
+  // Deduplicate. We don't want to deduplicate based on the path as we assume
+  // that all replacements refer to the same file (or are symlinks).
+  std::sort(Replaces.begin(), Replaces.end(), LessNoPath);
+  Replaces.erase(std::unique(Replaces.begin(), Replaces.end(), EqualNoPath),
+                 Replaces.end());
+
+  // Detect conflicts
+  tooling::Range ConflictRange(Replaces.front().getOffset(),
+                               Replaces.front().getLength());
+  unsigned ConflictStart = 0;
+  unsigned ConflictLength = 1;
+  for (unsigned i = 1; i < Replaces.size(); ++i) {
+    tooling::Range Current(Replaces[i].getOffset(), Replaces[i].getLength());
+    if (ConflictRange.overlapsWith(Current)) {
+      // Extend conflicted range
+      ConflictRange =
+          tooling::Range(ConflictRange.getOffset(),
+                         std::max(ConflictRange.getLength(),
+                                  Current.getOffset() + Current.getLength() -
+                                      ConflictRange.getOffset()));
+      ++ConflictLength;
+    } else {
+      if (ConflictLength > 1)
+        Conflicts.push_back(tooling::Range(ConflictStart, ConflictLength));
+      ConflictRange = Current;
+      ConflictStart = i;
+      ConflictLength = 1;
+    }
+  }
+
+  if (ConflictLength > 1)
+    Conflicts.push_back(tooling::Range(ConflictStart, ConflictLength));
+}
+
+/// \brief Deduplicates and tests for conflicts among the replacements for each
+/// file in \c Replacements. Any conflicts found are reported.
+///
+/// \post Replacements[i].getOffset() <= Replacements[i+1].getOffset().
+///
+/// \param[in,out] Replacements Container of all replacements grouped by file
+/// to be deduplicated and checked for conflicts.
+/// \param[in] SM SourceManager required for conflict reporting.
+///
+/// \returns \parblock
+///          \li true if conflicts were detected
+///          \li false if no conflicts were detected
+static bool deduplicateAndDetectConflicts(FileToReplacementsMap &Replacements,
+                                          SourceManager &SM) {
+  bool conflictsFound = false;
+
+  for (auto &FileAndReplacements : Replacements) {
+    const FileEntry *Entry = FileAndReplacements.first;
+    auto &Replacements = FileAndReplacements.second;
+    assert(Entry != nullptr && "No file entry!");
+
+    std::vector<tooling::Range> Conflicts;
+    deduplicate(FileAndReplacements.second, Conflicts);
+
+    if (Conflicts.empty())
+      continue;
+
+    conflictsFound = true;
+
+    errs() << "There are conflicting changes to " << Entry->getName() << ":\n";
+
+    for (const tooling::Range &Conflict : Conflicts) {
+      auto ConflictingReplacements = llvm::makeArrayRef(
+          &Replacements[Conflict.getOffset()], Conflict.getLength());
+      reportConflict(Entry, ConflictingReplacements, SM);
+    }
+  }
+
+  return conflictsFound;
+}
+
+bool mergeAndDeduplicate(const TUReplacements &TUs,
+                         FileToReplacementsMap &GroupedReplacements,
+                         clang::SourceManager &SM) {
+
+  // Group all replacements by target file.
+  std::set<StringRef> Warned;
+  for (const auto &TU : TUs) {
+    for (const tooling::Replacement &R : TU.Replacements) {
+      // Use the file manager to deduplicate paths. FileEntries are
+      // automatically canonicalized.
+      const FileEntry *Entry = SM.getFileManager().getFile(R.getFilePath());
+      if (!Entry && Warned.insert(R.getFilePath()).second) {
+        errs() << "Described file '" << R.getFilePath()
+               << "' doesn't exist. Ignoring...\n";
+        continue;
+      }
+      GroupedReplacements[Entry].push_back(R);
+    }
+  }
+
+  // Ask clang to deduplicate and report conflicts.
+  return !deduplicateAndDetectConflicts(GroupedReplacements, SM);
+}
+
+bool mergeAndDeduplicate(const TUDiagnostics &TUs,
+                         FileToReplacementsMap &GroupedReplacements,
+                         clang::SourceManager &SM) {
+
+  // Group all replacements by target file.
+  std::set<StringRef> Warned;
+  for (const auto &TU : TUs) {
+    for (const auto &D : TU.Diagnostics) {
+      for (const auto &Fix : D.Fix) {
+        for (const tooling::Replacement &R : Fix.second) {
+          // Use the file manager to deduplicate paths. FileEntries are
+          // automatically canonicalized.
+          const FileEntry *Entry = SM.getFileManager().getFile(R.getFilePath());
+          if (!Entry && Warned.insert(R.getFilePath()).second) {
+            errs() << "Described file '" << R.getFilePath()
+                   << "' doesn't exist. Ignoring...\n";
+            continue;
+          }
+          GroupedReplacements[Entry].push_back(R);
+        }
+      }
+    }
+  }
+
+  // Ask clang to deduplicate and report conflicts.
+  return !deduplicateAndDetectConflicts(GroupedReplacements, SM);
+}
+
+bool applyReplacements(const FileToReplacementsMap &GroupedReplacements,
+                       clang::Rewriter &Rewrites) {
+
+  // Apply all changes
+  //
+  // FIXME: No longer certain GroupedReplacements is really the best kind of
+  // data structure for applying replacements. Rewriter certainly doesn't care.
+  // However, until we nail down the design of ReplacementGroups, might as well
+  // leave this as is.
+  for (const auto &FileAndReplacements : GroupedReplacements) {
+    if (!applyAllReplacements(FileAndReplacements.second, Rewrites))
+      return false;
+  }
+
+  return true;
+}
+
+RangeVector calculateChangedRanges(
+    const std::vector<clang::tooling::Replacement> &Replaces) {
+  RangeVector ChangedRanges;
+
+  // Generate the new ranges from the replacements.
+  int Shift = 0;
+  for (const tooling::Replacement &R : Replaces) {
+    unsigned Offset = R.getOffset() + Shift;
+    unsigned Length = R.getReplacementText().size();
+    Shift += Length - R.getLength();
+    ChangedRanges.push_back(tooling::Range(Offset, Length));
+  }
+
+  return ChangedRanges;
+}
+
+bool writeFiles(const clang::Rewriter &Rewrites) {
+
+  for (auto BufferI = Rewrites.buffer_begin(), BufferE = Rewrites.buffer_end();
+       BufferI != BufferE; ++BufferI) {
+    StringRef FileName =
+        Rewrites.getSourceMgr().getFileEntryForID(BufferI->first)->getName();
+
+    std::error_code EC;
+    llvm::raw_fd_ostream FileStream(FileName, EC, llvm::sys::fs::F_Text);
+    if (EC) {
+      errs() << "Warning: Could not write to " << EC.message() << "\n";
+      continue;
+    }
+    BufferI->second.write(FileStream);
+  }
+
+  return true;
+}
+
+bool deleteReplacementFiles(const TUReplacementFiles &Files,
+                            clang::DiagnosticsEngine &Diagnostics) {
+  bool Success = true;
+  for (const auto &Filename : Files) {
+    std::error_code Error = llvm::sys::fs::remove(Filename);
+    if (Error) {
+      Success = false;
+      // FIXME: Use Diagnostics for outputting errors.
+      errs() << "Error deleting file: " << Filename << "\n";
+      errs() << Error.message() << "\n";
+      errs() << "Please delete the file manually\n";
+    }
+  }
+  return Success;
+}
+
+} // end namespace replace
+} // end namespace clang
diff --git a/clang-apply-replacements/tool/CMakeLists.txt b/clang-apply-replacements/tool/CMakeLists.txt
new file mode 100644 (file)
index 0000000..b5c159d
--- /dev/null
@@ -0,0 +1,17 @@
+set(LLVM_LINK_COMPONENTS
+  Support
+  )
+
+add_clang_executable(clang-apply-replacements
+  ClangApplyReplacementsMain.cpp
+  )
+target_link_libraries(clang-apply-replacements
+  clangApplyReplacements
+  clangBasic
+  clangFormat
+  clangRewrite
+  clangToolingCore
+  )
+
+install(TARGETS clang-apply-replacements
+  RUNTIME DESTINATION bin)
diff --git a/clang-apply-replacements/tool/ClangApplyReplacementsMain.cpp b/clang-apply-replacements/tool/ClangApplyReplacementsMain.cpp
new file mode 100644 (file)
index 0000000..3ca2680
--- /dev/null
@@ -0,0 +1,290 @@
+//===-- ClangApplyReplacementsMain.cpp - Main file for the tool -----------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief This file provides the main function for the
+/// clang-apply-replacements tool.
+///
+//===----------------------------------------------------------------------===//
+
+#include "clang-apply-replacements/Tooling/ApplyReplacements.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/DiagnosticOptions.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Basic/Version.h"
+#include "clang/Format/Format.h"
+#include "clang/Rewrite/Core/Rewriter.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringSet.h"
+#include "llvm/Support/CommandLine.h"
+
+using namespace llvm;
+using namespace clang;
+using namespace clang::replace;
+
+static cl::opt<std::string> Directory(cl::Positional, cl::Required,
+                                      cl::desc("<Search Root Directory>"));
+
+static cl::OptionCategory ReplacementCategory("Replacement Options");
+static cl::OptionCategory FormattingCategory("Formatting Options");
+
+const cl::OptionCategory *VisibleCategories[] = {&ReplacementCategory,
+                                                 &FormattingCategory};
+
+static cl::opt<bool> RemoveTUReplacementFiles(
+    "remove-change-desc-files",
+    cl::desc("Remove the change description files regardless of successful\n"
+             "merging/replacing."),
+    cl::init(false), cl::cat(ReplacementCategory));
+
+static cl::opt<bool> DoFormat(
+    "format",
+    cl::desc("Enable formatting of code changed by applying replacements.\n"
+             "Use -style to choose formatting style.\n"),
+    cl::cat(FormattingCategory));
+
+// FIXME: Consider making the default behaviour for finding a style
+// configuration file to start the search anew for every file being changed to
+// handle situations where the style is different for different parts of a
+// project.
+
+static cl::opt<std::string> FormatStyleConfig(
+    "style-config",
+    cl::desc("Path to a directory containing a .clang-format file\n"
+             "describing a formatting style to use for formatting\n"
+             "code when -style=file.\n"),
+    cl::init(""), cl::cat(FormattingCategory));
+
+static cl::opt<std::string>
+    FormatStyleOpt("style", cl::desc(format::StyleOptionHelpDescription),
+                   cl::init("LLVM"), cl::cat(FormattingCategory));
+
+namespace {
+// Helper object to remove the TUReplacement and TUDiagnostic (triggered by
+// "remove-change-desc-files" command line option) when exiting current scope.
+class ScopedFileRemover {
+public:
+  ScopedFileRemover(const TUReplacementFiles &Files,
+                    clang::DiagnosticsEngine &Diagnostics)
+      : TURFiles(Files), Diag(Diagnostics) {}
+
+  ~ScopedFileRemover() { deleteReplacementFiles(TURFiles, Diag); }
+
+private:
+  const TUReplacementFiles &TURFiles;
+  clang::DiagnosticsEngine &Diag;
+};
+} // namespace
+
+static void printVersion() {
+  outs() << "clang-apply-replacements version " CLANG_VERSION_STRING << "\n";
+}
+
+/// \brief Convenience function to get rewritten content for \c Filename from
+/// \c Rewrites.
+///
+/// \pre Replacements[i].getFilePath() == Replacements[i+1].getFilePath().
+/// \post Replacements.empty() -> Result.empty()
+///
+/// \param[in] Replacements Replacements to apply
+/// \param[in] Rewrites Rewriter to use to apply replacements.
+/// \param[out] Result Contents of the file after applying replacements if
+/// replacements were provided.
+///
+/// \returns \parblock
+///          \li true if all replacements were applied successfully.
+///          \li false if at least one replacement failed to apply.
+static bool
+getRewrittenData(const std::vector<tooling::Replacement> &Replacements,
+                 Rewriter &Rewrites, std::string &Result) {
+  if (Replacements.empty())
+    return true;
+
+  if (!applyAllReplacements(Replacements, Rewrites))
+    return false;
+
+  SourceManager &SM = Rewrites.getSourceMgr();
+  FileManager &Files = SM.getFileManager();
+
+  StringRef FileName = Replacements.begin()->getFilePath();
+  const clang::FileEntry *Entry = Files.getFile(FileName);
+  assert(Entry && "Expected an existing file");
+  FileID ID = SM.translateFile(Entry);
+  assert(ID.isValid() && "Expected a valid FileID");
+  const RewriteBuffer *Buffer = Rewrites.getRewriteBufferFor(ID);
+  Result = std::string(Buffer->begin(), Buffer->end());
+
+  return true;
+}
+
+/// \brief Apply \c Replacements and return the new file contents.
+///
+/// \pre Replacements[i].getFilePath() == Replacements[i+1].getFilePath().
+/// \post Replacements.empty() -> Result.empty()
+///
+/// \param[in] Replacements Replacements to apply.
+/// \param[out] Result Contents of the file after applying replacements if
+/// replacements were provided.
+/// \param[in] Diagnostics For diagnostic output.
+///
+/// \returns \parblock
+///          \li true if all replacements applied successfully.
+///          \li false if at least one replacement failed to apply.
+static bool
+applyReplacements(const std::vector<tooling::Replacement> &Replacements,
+                  std::string &Result, DiagnosticsEngine &Diagnostics) {
+  FileManager Files((FileSystemOptions()));
+  SourceManager SM(Diagnostics, Files);
+  Rewriter Rewrites(SM, LangOptions());
+
+  return getRewrittenData(Replacements, Rewrites, Result);
+}
+
+/// \brief Apply code formatting to all places where replacements were made.
+///
+/// \pre !Replacements.empty().
+/// \pre Replacements[i].getFilePath() == Replacements[i+1].getFilePath().
+/// \pre Replacements[i].getOffset() <= Replacements[i+1].getOffset().
+///
+/// \param[in] Replacements Replacements that were made to the file. Provided
+/// to indicate where changes were made.
+/// \param[in] FileData The contents of the file \b after \c Replacements have
+/// been applied.
+/// \param[out] FormattedFileData The contents of the file after reformatting.
+/// \param[in] FormatStyle Style to apply.
+/// \param[in] Diagnostics For diagnostic output.
+///
+/// \returns \parblock
+///          \li true if reformatting replacements were all successfully
+///          applied.
+///          \li false if at least one reformatting replacement failed to apply.
+static bool
+applyFormatting(const std::vector<tooling::Replacement> &Replacements,
+                const StringRef FileData, std::string &FormattedFileData,
+                const format::FormatStyle &FormatStyle,
+                DiagnosticsEngine &Diagnostics) {
+  assert(!Replacements.empty() && "Need at least one replacement");
+
+  RangeVector Ranges = calculateChangedRanges(Replacements);
+
+  StringRef FileName = Replacements.begin()->getFilePath();
+  tooling::Replacements R =
+      format::reformat(FormatStyle, FileData, Ranges, FileName);
+
+  // FIXME: Remove this copy when tooling::Replacements is implemented as a
+  // vector instead of a set.
+  std::vector<tooling::Replacement> FormattingReplacements;
+  std::copy(R.begin(), R.end(), back_inserter(FormattingReplacements));
+
+  if (FormattingReplacements.empty()) {
+    FormattedFileData = FileData;
+    return true;
+  }
+
+  FileManager Files((FileSystemOptions()));
+  SourceManager SM(Diagnostics, Files);
+  SM.overrideFileContents(Files.getFile(FileName),
+                          llvm::MemoryBuffer::getMemBufferCopy(FileData));
+  Rewriter Rewrites(SM, LangOptions());
+
+  return getRewrittenData(FormattingReplacements, Rewrites, FormattedFileData);
+}
+
+int main(int argc, char **argv) {
+  cl::HideUnrelatedOptions(makeArrayRef(VisibleCategories));
+
+  cl::SetVersionPrinter(&printVersion);
+  cl::ParseCommandLineOptions(argc, argv);
+
+  IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(new DiagnosticOptions());
+  DiagnosticsEngine Diagnostics(
+      IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), DiagOpts.get());
+
+  // Determine a formatting style from options.
+  format::FormatStyle FormatStyle;
+  if (DoFormat) {
+    auto FormatStyleOrError =
+        format::getStyle(FormatStyleOpt, FormatStyleConfig, "LLVM");
+    if (!FormatStyleOrError) {
+      llvm::errs() << llvm::toString(FormatStyleOrError.takeError()) << "\n";
+      return 1;
+    }
+    FormatStyle = *FormatStyleOrError;
+  }
+
+  TUReplacements TURs;
+  TUReplacementFiles TUFiles;
+
+  std::error_code ErrorCode =
+      collectReplacementsFromDirectory(Directory, TURs, TUFiles, Diagnostics);
+
+  TUDiagnostics TUDs;
+  TUFiles.clear();
+  ErrorCode =
+      collectReplacementsFromDirectory(Directory, TUDs, TUFiles, Diagnostics);
+
+  if (ErrorCode) {
+    errs() << "Trouble iterating over directory '" << Directory
+           << "': " << ErrorCode.message() << "\n";
+    return 1;
+  }
+
+  // Remove the TUReplacementFiles (triggered by "remove-change-desc-files"
+  // command line option) when exiting main().
+  std::unique_ptr<ScopedFileRemover> Remover;
+  if (RemoveTUReplacementFiles)
+    Remover.reset(new ScopedFileRemover(TUFiles, Diagnostics));
+
+  FileManager Files((FileSystemOptions()));
+  SourceManager SM(Diagnostics, Files);
+
+  FileToReplacementsMap GroupedReplacements;
+  if (!mergeAndDeduplicate(TURs, GroupedReplacements, SM))
+    return 1;
+  if (!mergeAndDeduplicate(TUDs, GroupedReplacements, SM))
+    return 1;
+
+  Rewriter ReplacementsRewriter(SM, LangOptions());
+
+  for (const auto &FileAndReplacements : GroupedReplacements) {
+    // This shouldn't happen but if a file somehow has no replacements skip to
+    // next file.
+    if (FileAndReplacements.second.empty())
+      continue;
+
+    std::string NewFileData;
+    StringRef FileName = FileAndReplacements.first->getName();
+    if (!applyReplacements(FileAndReplacements.second, NewFileData,
+                           Diagnostics)) {
+      errs() << "Failed to apply replacements to " << FileName << "\n";
+      continue;
+    }
+
+    // Apply formatting if requested.
+    if (DoFormat &&
+        !applyFormatting(FileAndReplacements.second, NewFileData, NewFileData,
+                         FormatStyle, Diagnostics)) {
+      errs() << "Failed to apply reformatting replacements for " << FileName
+             << "\n";
+      continue;
+    }
+
+    // Write new file to disk
+    std::error_code EC;
+    llvm::raw_fd_ostream FileStream(FileName, EC, llvm::sys::fs::F_None);
+    if (EC) {
+      llvm::errs() << "Could not open " << FileName << " for writing\n";
+      continue;
+    }
+
+    FileStream << NewFileData;
+  }
+
+  return 0;
+}
diff --git a/clang-move/CMakeLists.txt b/clang-move/CMakeLists.txt
new file mode 100644 (file)
index 0000000..84175fd
--- /dev/null
@@ -0,0 +1,21 @@
+set(LLVM_LINK_COMPONENTS
+  support
+  )
+
+add_clang_library(clangMove
+  ClangMove.cpp
+  HelperDeclRefGraph.cpp
+
+  LINK_LIBS
+  clangAnalysis
+  clangAST
+  clangASTMatchers
+  clangBasic
+  clangFormat
+  clangFrontend
+  clangLex
+  clangTooling
+  clangToolingCore
+  )
+
+add_subdirectory(tool)
diff --git a/clang-move/ClangMove.cpp b/clang-move/ClangMove.cpp
new file mode 100644 (file)
index 0000000..3a44aa3
--- /dev/null
@@ -0,0 +1,931 @@
+//===-- ClangMove.cpp - Implement ClangMove functationalities ---*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ClangMove.h"
+#include "HelperDeclRefGraph.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Format/Format.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Rewrite/Core/Rewriter.h"
+#include "clang/Tooling/Core/Replacement.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/Path.h"
+
+#define DEBUG_TYPE "clang-move"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace move {
+namespace {
+
+// FIXME: Move to ASTMatchers.
+AST_MATCHER(VarDecl, isStaticDataMember) { return Node.isStaticDataMember(); }
+
+AST_MATCHER(NamedDecl, notInMacro) { return !Node.getLocation().isMacroID(); }
+
+AST_MATCHER_P(Decl, hasOutermostEnclosingClass,
+              ast_matchers::internal::Matcher<Decl>, InnerMatcher) {
+  const auto *Context = Node.getDeclContext();
+  if (!Context)
+    return false;
+  while (const auto *NextContext = Context->getParent()) {
+    if (isa<NamespaceDecl>(NextContext) ||
+        isa<TranslationUnitDecl>(NextContext))
+      break;
+    Context = NextContext;
+  }
+  return InnerMatcher.matches(*Decl::castFromDeclContext(Context), Finder,
+                              Builder);
+}
+
+AST_MATCHER_P(CXXMethodDecl, ofOutermostEnclosingClass,
+              ast_matchers::internal::Matcher<CXXRecordDecl>, InnerMatcher) {
+  const CXXRecordDecl *Parent = Node.getParent();
+  if (!Parent)
+    return false;
+  while (const auto *NextParent =
+             dyn_cast<CXXRecordDecl>(Parent->getParent())) {
+    Parent = NextParent;
+  }
+
+  return InnerMatcher.matches(*Parent, Finder, Builder);
+}
+
+// Make the Path absolute using the CurrentDir if the Path is not an absolute
+// path. An empty Path will result in an empty string.
+std::string MakeAbsolutePath(StringRef CurrentDir, StringRef Path) {
+  if (Path.empty())
+    return "";
+  llvm::SmallString<128> InitialDirectory(CurrentDir);
+  llvm::SmallString<128> AbsolutePath(Path);
+  if (std::error_code EC =
+          llvm::sys::fs::make_absolute(InitialDirectory, AbsolutePath))
+    llvm::errs() << "Warning: could not make absolute file: '" << EC.message()
+                 << '\n';
+  llvm::sys::path::remove_dots(AbsolutePath, /*remove_dot_dot=*/true);
+  llvm::sys::path::native(AbsolutePath);
+  return AbsolutePath.str();
+}
+
+// Make the Path absolute using the current working directory of the given
+// SourceManager if the Path is not an absolute path.
+//
+// The Path can be a path relative to the build directory, or retrieved from
+// the SourceManager.
+std::string MakeAbsolutePath(const SourceManager &SM, StringRef Path) {
+  llvm::SmallString<128> AbsolutePath(Path);
+  if (std::error_code EC =
+          SM.getFileManager().getVirtualFileSystem()->makeAbsolute(
+              AbsolutePath))
+    llvm::errs() << "Warning: could not make absolute file: '" << EC.message()
+                 << '\n';
+  // Handle symbolic link path cases.
+  // We are trying to get the real file path of the symlink.
+  const DirectoryEntry *Dir = SM.getFileManager().getDirectory(
+      llvm::sys::path::parent_path(AbsolutePath.str()));
+  if (Dir) {
+    StringRef DirName = SM.getFileManager().getCanonicalName(Dir);
+    SmallVector<char, 128> AbsoluteFilename;
+    llvm::sys::path::append(AbsoluteFilename, DirName,
+                            llvm::sys::path::filename(AbsolutePath.str()));
+    return llvm::StringRef(AbsoluteFilename.data(), AbsoluteFilename.size())
+        .str();
+  }
+  return AbsolutePath.str();
+}
+
+// Matches AST nodes that are expanded within the given AbsoluteFilePath.
+AST_POLYMORPHIC_MATCHER_P(isExpansionInFile,
+                          AST_POLYMORPHIC_SUPPORTED_TYPES(Decl, Stmt, TypeLoc),
+                          std::string, AbsoluteFilePath) {
+  auto &SourceManager = Finder->getASTContext().getSourceManager();
+  auto ExpansionLoc = SourceManager.getExpansionLoc(Node.getLocStart());
+  if (ExpansionLoc.isInvalid())
+    return false;
+  auto FileEntry =
+      SourceManager.getFileEntryForID(SourceManager.getFileID(ExpansionLoc));
+  if (!FileEntry)
+    return false;
+  return MakeAbsolutePath(SourceManager, FileEntry->getName()) ==
+         AbsoluteFilePath;
+}
+
+class FindAllIncludes : public clang::PPCallbacks {
+public:
+  explicit FindAllIncludes(SourceManager *SM, ClangMoveTool *const MoveTool)
+      : SM(*SM), MoveTool(MoveTool) {}
+
+  void InclusionDirective(clang::SourceLocation HashLoc,
+                          const clang::Token & /*IncludeTok*/,
+                          StringRef FileName, bool IsAngled,
+                          clang::CharSourceRange FilenameRange,
+                          const clang::FileEntry * /*File*/,
+                          StringRef SearchPath, StringRef /*RelativePath*/,
+                          const clang::Module * /*Imported*/) override {
+    if (const auto *FileEntry = SM.getFileEntryForID(SM.getFileID(HashLoc)))
+      MoveTool->addIncludes(FileName, IsAngled, SearchPath,
+                            FileEntry->getName(), FilenameRange, SM);
+  }
+
+private:
+  const SourceManager &SM;
+  ClangMoveTool *const MoveTool;
+};
+
+/// Add a declatration being moved to new.h/cc. Note that the declaration will
+/// also be deleted in old.h/cc.
+void MoveDeclFromOldFileToNewFile(ClangMoveTool *MoveTool, const NamedDecl *D) {
+  MoveTool->getMovedDecls().push_back(D);
+  MoveTool->addRemovedDecl(D);
+  MoveTool->getUnremovedDeclsInOldHeader().erase(D);
+}
+
+class FunctionDeclarationMatch : public MatchFinder::MatchCallback {
+public:
+  explicit FunctionDeclarationMatch(ClangMoveTool *MoveTool)
+      : MoveTool(MoveTool) {}
+
+  void run(const MatchFinder::MatchResult &Result) override {
+    const auto *FD = Result.Nodes.getNodeAs<clang::FunctionDecl>("function");
+    assert(FD);
+    const clang::NamedDecl *D = FD;
+    if (const auto *FTD = FD->getDescribedFunctionTemplate())
+      D = FTD;
+    MoveDeclFromOldFileToNewFile(MoveTool, D);
+  }
+
+private:
+  ClangMoveTool *MoveTool;
+};
+
+class VarDeclarationMatch : public MatchFinder::MatchCallback {
+public:
+  explicit VarDeclarationMatch(ClangMoveTool *MoveTool)
+      : MoveTool(MoveTool) {}
+
+  void run(const MatchFinder::MatchResult &Result) override {
+    const auto *VD = Result.Nodes.getNodeAs<clang::VarDecl>("var");
+    assert(VD);
+    MoveDeclFromOldFileToNewFile(MoveTool, VD);
+  }
+
+private:
+  ClangMoveTool *MoveTool;
+};
+
+class TypeAliasMatch : public MatchFinder::MatchCallback {
+public:
+  explicit TypeAliasMatch(ClangMoveTool *MoveTool)
+      : MoveTool(MoveTool) {}
+
+  void run(const MatchFinder::MatchResult &Result) override {
+    if (const auto *TD = Result.Nodes.getNodeAs<clang::TypedefDecl>("typedef"))
+      MoveDeclFromOldFileToNewFile(MoveTool, TD);
+    else if (const auto *TAD =
+                 Result.Nodes.getNodeAs<clang::TypeAliasDecl>("type_alias")) {
+      const NamedDecl * D = TAD;
+      if (const auto * TD = TAD->getDescribedAliasTemplate())
+        D = TD;
+      MoveDeclFromOldFileToNewFile(MoveTool, D);
+    }
+  }
+
+private:
+  ClangMoveTool *MoveTool;
+};
+
+class EnumDeclarationMatch : public MatchFinder::MatchCallback {
+public:
+  explicit EnumDeclarationMatch(ClangMoveTool *MoveTool)
+      : MoveTool(MoveTool) {}
+
+  void run(const MatchFinder::MatchResult &Result) override {
+    const auto *ED = Result.Nodes.getNodeAs<clang::EnumDecl>("enum");
+    assert(ED);
+    MoveDeclFromOldFileToNewFile(MoveTool, ED);
+  }
+
+private:
+  ClangMoveTool *MoveTool;
+};
+
+class ClassDeclarationMatch : public MatchFinder::MatchCallback {
+public:
+  explicit ClassDeclarationMatch(ClangMoveTool *MoveTool)
+      : MoveTool(MoveTool) {}
+  void run(const MatchFinder::MatchResult &Result) override {
+    clang::SourceManager* SM = &Result.Context->getSourceManager();
+    if (const auto *CMD =
+            Result.Nodes.getNodeAs<clang::CXXMethodDecl>("class_method"))
+      MatchClassMethod(CMD, SM);
+    else if (const auto *VD = Result.Nodes.getNodeAs<clang::VarDecl>(
+                   "class_static_var_decl"))
+      MatchClassStaticVariable(VD, SM);
+    else if (const auto *CD = Result.Nodes.getNodeAs<clang::CXXRecordDecl>(
+                   "moved_class"))
+      MatchClassDeclaration(CD, SM);
+  }
+
+private:
+  void MatchClassMethod(const clang::CXXMethodDecl* CMD,
+                        clang::SourceManager* SM) {
+    // Skip inline class methods. isInline() ast matcher doesn't ignore this
+    // case.
+    if (!CMD->isInlined()) {
+      MoveTool->getMovedDecls().push_back(CMD);
+      MoveTool->addRemovedDecl(CMD);
+      // Get template class method from its method declaration as
+      // UnremovedDecls stores template class method.
+      if (const auto *FTD = CMD->getDescribedFunctionTemplate())
+        MoveTool->getUnremovedDeclsInOldHeader().erase(FTD);
+      else
+        MoveTool->getUnremovedDeclsInOldHeader().erase(CMD);
+    }
+  }
+
+  void MatchClassStaticVariable(const clang::NamedDecl *VD,
+                                clang::SourceManager* SM) {
+    MoveDeclFromOldFileToNewFile(MoveTool, VD);
+  }
+
+  void MatchClassDeclaration(const clang::CXXRecordDecl *CD,
+                             clang::SourceManager* SM) {
+    // Get class template from its class declaration as UnremovedDecls stores
+    // class template.
+    if (const auto *TC = CD->getDescribedClassTemplate())
+      MoveTool->getMovedDecls().push_back(TC);
+    else
+      MoveTool->getMovedDecls().push_back(CD);
+    MoveTool->addRemovedDecl(MoveTool->getMovedDecls().back());
+    MoveTool->getUnremovedDeclsInOldHeader().erase(
+        MoveTool->getMovedDecls().back());
+  }
+
+  ClangMoveTool *MoveTool;
+};
+
+// Expand to get the end location of the line where the EndLoc of the given
+// Decl.
+SourceLocation
+getLocForEndOfDecl(const clang::Decl *D,
+                   const LangOptions &LangOpts = clang::LangOptions()) {
+  const auto &SM = D->getASTContext().getSourceManager();
+  auto EndExpansionLoc = SM.getExpansionLoc(D->getLocEnd());
+  std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(EndExpansionLoc);
+  // Try to load the file buffer.
+  bool InvalidTemp = false;
+  llvm::StringRef File = SM.getBufferData(LocInfo.first, &InvalidTemp);
+  if (InvalidTemp)
+    return SourceLocation();
+
+  const char *TokBegin = File.data() + LocInfo.second;
+  // Lex from the start of the given location.
+  Lexer Lex(SM.getLocForStartOfFile(LocInfo.first), LangOpts, File.begin(),
+            TokBegin, File.end());
+
+  llvm::SmallVector<char, 16> Line;
+  // FIXME: this is a bit hacky to get ReadToEndOfLine work.
+  Lex.setParsingPreprocessorDirective(true);
+  Lex.ReadToEndOfLine(&Line);
+  SourceLocation EndLoc = EndExpansionLoc.getLocWithOffset(Line.size());
+  // If we already reach EOF, just return the EOF SourceLocation;
+  // otherwise, move 1 offset ahead to include the trailing newline character
+  // '\n'.
+  return SM.getLocForEndOfFile(LocInfo.first) == EndLoc
+             ? EndLoc
+             : EndLoc.getLocWithOffset(1);
+}
+
+// Get full range of a Decl including the comments associated with it.
+clang::CharSourceRange
+getFullRange(const clang::Decl *D,
+             const clang::LangOptions &options = clang::LangOptions()) {
+  const auto &SM = D->getASTContext().getSourceManager();
+  clang::SourceRange Full(SM.getExpansionLoc(D->getLocStart()),
+                          getLocForEndOfDecl(D));
+  // Expand to comments that are associated with the Decl.
+  if (const auto *Comment = D->getASTContext().getRawCommentForDeclNoCache(D)) {
+    if (SM.isBeforeInTranslationUnit(Full.getEnd(), Comment->getLocEnd()))
+      Full.setEnd(Comment->getLocEnd());
+    // FIXME: Don't delete a preceding comment, if there are no other entities
+    // it could refer to.
+    if (SM.isBeforeInTranslationUnit(Comment->getLocStart(), Full.getBegin()))
+      Full.setBegin(Comment->getLocStart());
+  }
+
+  return clang::CharSourceRange::getCharRange(Full);
+}
+
+std::string getDeclarationSourceText(const clang::Decl *D) {
+  const auto &SM = D->getASTContext().getSourceManager();
+  llvm::StringRef SourceText =
+      clang::Lexer::getSourceText(getFullRange(D), SM, clang::LangOptions());
+  return SourceText.str();
+}
+
+bool isInHeaderFile(const clang::Decl *D,
+                    llvm::StringRef OriginalRunningDirectory,
+                    llvm::StringRef OldHeader) {
+  const auto &SM = D->getASTContext().getSourceManager();
+  if (OldHeader.empty())
+    return false;
+  auto ExpansionLoc = SM.getExpansionLoc(D->getLocStart());
+  if (ExpansionLoc.isInvalid())
+    return false;
+
+  if (const auto *FE = SM.getFileEntryForID(SM.getFileID(ExpansionLoc))) {
+    return MakeAbsolutePath(SM, FE->getName()) ==
+           MakeAbsolutePath(OriginalRunningDirectory, OldHeader);
+  }
+
+  return false;
+}
+
+std::vector<std::string> getNamespaces(const clang::Decl *D) {
+  std::vector<std::string> Namespaces;
+  for (const auto *Context = D->getDeclContext(); Context;
+       Context = Context->getParent()) {
+    if (llvm::isa<clang::TranslationUnitDecl>(Context) ||
+        llvm::isa<clang::LinkageSpecDecl>(Context))
+      break;
+
+    if (const auto *ND = llvm::dyn_cast<clang::NamespaceDecl>(Context))
+      Namespaces.push_back(ND->getName().str());
+  }
+  std::reverse(Namespaces.begin(), Namespaces.end());
+  return Namespaces;
+}
+
+clang::tooling::Replacements
+createInsertedReplacements(const std::vector<std::string> &Includes,
+                           const std::vector<const NamedDecl *> &Decls,
+                           llvm::StringRef FileName, bool IsHeader = false,
+                           StringRef OldHeaderInclude = "") {
+  std::string NewCode;
+  std::string GuardName(FileName);
+  if (IsHeader) {
+    for (size_t i = 0; i < GuardName.size(); ++i) {
+      if (!isAlphanumeric(GuardName[i]))
+        GuardName[i] = '_';
+    }
+    GuardName = StringRef(GuardName).upper();
+    NewCode += "#ifndef " + GuardName + "\n";
+    NewCode += "#define " + GuardName + "\n\n";
+  }
+
+  NewCode += OldHeaderInclude;
+  // Add #Includes.
+  for (const auto &Include : Includes)
+    NewCode += Include;
+
+  if (!Includes.empty())
+    NewCode += "\n";
+
+  // Add moved class definition and its related declarations. All declarations
+  // in same namespace are grouped together.
+  //
+  // Record namespaces where the current position is in.
+  std::vector<std::string> CurrentNamespaces;
+  for (const auto *MovedDecl : Decls) {
+    // The namespaces of the declaration being moved.
+    std::vector<std::string> DeclNamespaces = getNamespaces(MovedDecl);
+    auto CurrentIt = CurrentNamespaces.begin();
+    auto DeclIt = DeclNamespaces.begin();
+    // Skip the common prefix.
+    while (CurrentIt != CurrentNamespaces.end() &&
+           DeclIt != DeclNamespaces.end()) {
+      if (*CurrentIt != *DeclIt)
+        break;
+      ++CurrentIt;
+      ++DeclIt;
+    }
+    // Calculate the new namespaces after adding MovedDecl in CurrentNamespace,
+    // which is used for next iteration of this loop.
+    std::vector<std::string> NextNamespaces(CurrentNamespaces.begin(),
+                                            CurrentIt);
+    NextNamespaces.insert(NextNamespaces.end(), DeclIt, DeclNamespaces.end());
+
+
+    // End with CurrentNamespace.
+    bool HasEndCurrentNamespace = false;
+    auto RemainingSize = CurrentNamespaces.end() - CurrentIt;
+    for (auto It = CurrentNamespaces.rbegin(); RemainingSize > 0;
+         --RemainingSize, ++It) {
+      assert(It < CurrentNamespaces.rend());
+      NewCode += "} // namespace " + *It + "\n";
+      HasEndCurrentNamespace = true;
+    }
+    // Add trailing '\n' after the nested namespace definition.
+    if (HasEndCurrentNamespace)
+      NewCode += "\n";
+
+    // If the moved declaration is not in CurrentNamespace, add extra namespace
+    // definitions.
+    bool IsInNewNamespace = false;
+    while (DeclIt != DeclNamespaces.end()) {
+      NewCode += "namespace " + *DeclIt + " {\n";
+      IsInNewNamespace = true;
+      ++DeclIt;
+    }
+    // If the moved declaration is in same namespace CurrentNamespace, add
+    // a preceeding `\n' before the moved declaration.
+    // FIXME: Don't add empty lines between using declarations.
+    if (!IsInNewNamespace)
+      NewCode += "\n";
+    NewCode += getDeclarationSourceText(MovedDecl);
+    CurrentNamespaces = std::move(NextNamespaces);
+  }
+  std::reverse(CurrentNamespaces.begin(), CurrentNamespaces.end());
+  for (const auto &NS : CurrentNamespaces)
+    NewCode += "} // namespace " + NS + "\n";
+
+  if (IsHeader)
+    NewCode += "\n#endif // " + GuardName + "\n";
+  return clang::tooling::Replacements(
+      clang::tooling::Replacement(FileName, 0, 0, NewCode));
+}
+
+// Return a set of all decls which are used/referenced by the given Decls.
+// Specically, given a class member declaration, this method will return all
+// decls which are used by the whole class.
+llvm::DenseSet<const Decl *>
+getUsedDecls(const HelperDeclRefGraph *RG,
+             const std::vector<const NamedDecl *> &Decls) {
+  assert(RG);
+  llvm::DenseSet<const CallGraphNode *> Nodes;
+  for (const auto *D : Decls) {
+    auto Result = RG->getReachableNodes(
+        HelperDeclRGBuilder::getOutmostClassOrFunDecl(D));
+    Nodes.insert(Result.begin(), Result.end());
+  }
+  llvm::DenseSet<const Decl *> Results;
+  for (const auto *Node : Nodes)
+    Results.insert(Node->getDecl());
+  return Results;
+}
+
+} // namespace
+
+std::unique_ptr<clang::ASTConsumer>
+ClangMoveAction::CreateASTConsumer(clang::CompilerInstance &Compiler,
+                                   StringRef /*InFile*/) {
+  Compiler.getPreprocessor().addPPCallbacks(llvm::make_unique<FindAllIncludes>(
+      &Compiler.getSourceManager(), &MoveTool));
+  return MatchFinder.newASTConsumer();
+}
+
+ClangMoveTool::ClangMoveTool(ClangMoveContext *const Context,
+                             DeclarationReporter *const Reporter)
+    : Context(Context), Reporter(Reporter) {
+  if (!Context->Spec.NewHeader.empty())
+    CCIncludes.push_back("#include \"" + Context->Spec.NewHeader + "\"\n");
+}
+
+void ClangMoveTool::addRemovedDecl(const NamedDecl *Decl) {
+  const auto &SM = Decl->getASTContext().getSourceManager();
+  auto Loc = Decl->getLocation();
+  StringRef FilePath = SM.getFilename(Loc);
+  FilePathToFileID[FilePath] = SM.getFileID(Loc);
+  RemovedDecls.push_back(Decl);
+}
+
+void ClangMoveTool::registerMatchers(ast_matchers::MatchFinder *Finder) {
+  auto InOldHeader =
+      isExpansionInFile(makeAbsolutePath(Context->Spec.OldHeader));
+  auto InOldCC = isExpansionInFile(makeAbsolutePath(Context->Spec.OldCC));
+  auto InOldFiles = anyOf(InOldHeader, InOldCC);
+  auto classTemplateForwardDecls =
+      classTemplateDecl(unless(has(cxxRecordDecl(isDefinition()))));
+  auto ForwardClassDecls = namedDecl(
+      anyOf(cxxRecordDecl(unless(anyOf(isImplicit(), isDefinition()))),
+            classTemplateForwardDecls));
+  auto TopLevelDecl =
+      hasDeclContext(anyOf(namespaceDecl(), translationUnitDecl()));
+
+  //============================================================================
+  // Matchers for old header
+  //============================================================================
+  // Match all top-level named declarations (e.g. function, variable, enum) in
+  // old header, exclude forward class declarations and namespace declarations.
+  //
+  // We consider declarations inside a class belongs to the class. So these
+  // declarations will be ignored.
+  auto AllDeclsInHeader = namedDecl(
+      unless(ForwardClassDecls), unless(namespaceDecl()),
+      unless(usingDirectiveDecl()), // using namespace decl.
+      InOldHeader,
+      hasParent(decl(anyOf(namespaceDecl(), translationUnitDecl()))),
+      hasDeclContext(decl(anyOf(namespaceDecl(), translationUnitDecl()))));
+  Finder->addMatcher(AllDeclsInHeader.bind("decls_in_header"), this);
+
+  // Don't register other matchers when dumping all declarations in header.
+  if (Context->DumpDeclarations)
+    return;
+
+  // Match forward declarations in old header.
+  Finder->addMatcher(namedDecl(ForwardClassDecls, InOldHeader).bind("fwd_decl"),
+                     this);
+
+  //============================================================================
+  // Matchers for old cc
+  //============================================================================
+  auto IsOldCCTopLevelDecl = allOf(
+      hasParent(decl(anyOf(namespaceDecl(), translationUnitDecl()))), InOldCC);
+  // Matching using decls/type alias decls which are in named/anonymous/global
+  // namespace, these decls are always copied to new.h/cc. Those in classes,
+  // functions are covered in other matchers.
+  Finder->addMatcher(namedDecl(anyOf(usingDecl(IsOldCCTopLevelDecl),
+                                     usingDirectiveDecl(IsOldCCTopLevelDecl),
+                                     typeAliasDecl(IsOldCCTopLevelDecl)),
+                               notInMacro())
+                         .bind("using_decl"),
+                     this);
+
+  // Match static functions/variable definitions which are defined in named
+  // namespaces.
+  Optional<ast_matchers::internal::Matcher<NamedDecl>> HasAnySymbolNames;
+  for (StringRef SymbolName : Context->Spec.Names) {
+    llvm::StringRef GlobalSymbolName = SymbolName.trim().ltrim(':');
+    const auto HasName = hasName(("::" + GlobalSymbolName).str());
+    HasAnySymbolNames =
+        HasAnySymbolNames ? anyOf(*HasAnySymbolNames, HasName) : HasName;
+  }
+
+  if (!HasAnySymbolNames) {
+    llvm::errs() << "No symbols being moved.\n";
+    return;
+  }
+  auto InMovedClass =
+      hasOutermostEnclosingClass(cxxRecordDecl(*HasAnySymbolNames));
+
+  // Matchers for helper declarations in old.cc.
+  auto InAnonymousNS = hasParent(namespaceDecl(isAnonymous()));
+  auto NotInMovedClass= allOf(unless(InMovedClass), InOldCC);
+  auto IsOldCCHelper =
+      allOf(NotInMovedClass, anyOf(isStaticStorageClass(), InAnonymousNS));
+  // Match helper classes separately with helper functions/variables since we
+  // want to reuse these matchers in finding helpers usage below.
+  //
+  // There could be forward declarations usage for helpers, especially for
+  // classes and functions. We need include these forward declarations.
+  //
+  // Forward declarations for variable helpers will be excluded as these
+  // declarations (with "extern") are not supposed in cpp file.
+   auto HelperFuncOrVar =
+      namedDecl(notInMacro(), anyOf(functionDecl(IsOldCCHelper),
+                                    varDecl(isDefinition(), IsOldCCHelper)));
+  auto HelperClasses =
+      cxxRecordDecl(notInMacro(), NotInMovedClass, InAnonymousNS);
+  // Save all helper declarations in old.cc.
+  Finder->addMatcher(
+      namedDecl(anyOf(HelperFuncOrVar, HelperClasses)).bind("helper_decls"),
+      this);
+
+  // Construct an AST-based call graph of helper declarations in old.cc.
+  // In the following matcheres, "dc" is a caller while "helper_decls" and
+  // "used_class" is a callee, so a new edge starting from caller to callee will
+  // be add in the graph.
+  //
+  // Find helper function/variable usages.
+  Finder->addMatcher(
+      declRefExpr(to(HelperFuncOrVar), hasAncestor(decl().bind("dc")))
+          .bind("func_ref"),
+      &RGBuilder);
+  // Find helper class usages.
+  Finder->addMatcher(
+      typeLoc(loc(recordType(hasDeclaration(HelperClasses.bind("used_class")))),
+              hasAncestor(decl().bind("dc"))),
+      &RGBuilder);
+
+  //============================================================================
+  // Matchers for old files, including old.h/old.cc
+  //============================================================================
+  // Create a MatchCallback for class declarations.
+  MatchCallbacks.push_back(llvm::make_unique<ClassDeclarationMatch>(this));
+  // Match moved class declarations.
+  auto MovedClass = cxxRecordDecl(InOldFiles, *HasAnySymbolNames,
+                                  isDefinition(), TopLevelDecl)
+                        .bind("moved_class");
+  Finder->addMatcher(MovedClass, MatchCallbacks.back().get());
+  // Match moved class methods (static methods included) which are defined
+  // outside moved class declaration.
+  Finder->addMatcher(
+      cxxMethodDecl(InOldFiles, ofOutermostEnclosingClass(*HasAnySymbolNames),
+                    isDefinition())
+          .bind("class_method"),
+      MatchCallbacks.back().get());
+  // Match static member variable definition of the moved class.
+  Finder->addMatcher(
+      varDecl(InMovedClass, InOldFiles, isDefinition(), isStaticDataMember())
+          .bind("class_static_var_decl"),
+      MatchCallbacks.back().get());
+
+  MatchCallbacks.push_back(llvm::make_unique<FunctionDeclarationMatch>(this));
+  Finder->addMatcher(functionDecl(InOldFiles, *HasAnySymbolNames, TopLevelDecl)
+                         .bind("function"),
+                     MatchCallbacks.back().get());
+
+  MatchCallbacks.push_back(llvm::make_unique<VarDeclarationMatch>(this));
+  Finder->addMatcher(
+      varDecl(InOldFiles, *HasAnySymbolNames, TopLevelDecl).bind("var"),
+      MatchCallbacks.back().get());
+
+  // Match enum definition in old.h. Enum helpers (which are defined in old.cc)
+  // will not be moved for now no matter whether they are used or not.
+  MatchCallbacks.push_back(llvm::make_unique<EnumDeclarationMatch>(this));
+  Finder->addMatcher(
+      enumDecl(InOldHeader, *HasAnySymbolNames, isDefinition(), TopLevelDecl)
+          .bind("enum"),
+      MatchCallbacks.back().get());
+
+  // Match type alias in old.h, this includes "typedef" and "using" type alias
+  // declarations. Type alias helpers (which are defined in old.cc) will not be
+  // moved for now no matter whether they are used or not.
+  MatchCallbacks.push_back(llvm::make_unique<TypeAliasMatch>(this));
+  Finder->addMatcher(namedDecl(anyOf(typedefDecl().bind("typedef"),
+                                     typeAliasDecl().bind("type_alias")),
+                               InOldHeader, *HasAnySymbolNames, TopLevelDecl),
+                     MatchCallbacks.back().get());
+}
+
+void ClangMoveTool::run(const ast_matchers::MatchFinder::MatchResult &Result) {
+  if (const auto *D =
+          Result.Nodes.getNodeAs<clang::NamedDecl>("decls_in_header")) {
+    UnremovedDeclsInOldHeader.insert(D);
+  } else if (const auto *FWD =
+                 Result.Nodes.getNodeAs<clang::CXXRecordDecl>("fwd_decl")) {
+    // Skip all forward declarations which appear after moved class declaration.
+    if (RemovedDecls.empty()) {
+      if (const auto *DCT = FWD->getDescribedClassTemplate())
+        MovedDecls.push_back(DCT);
+      else
+        MovedDecls.push_back(FWD);
+    }
+  } else if (const auto *ND =
+                 Result.Nodes.getNodeAs<clang::NamedDecl>("helper_decls")) {
+    MovedDecls.push_back(ND);
+    HelperDeclarations.push_back(ND);
+    DEBUG(llvm::dbgs() << "Add helper : "
+                       << ND->getNameAsString() << " (" << ND << ")\n");
+  } else if (const auto *UD =
+                 Result.Nodes.getNodeAs<clang::NamedDecl>("using_decl")) {
+    MovedDecls.push_back(UD);
+  }
+}
+
+std::string ClangMoveTool::makeAbsolutePath(StringRef Path) {
+  return MakeAbsolutePath(Context->OriginalRunningDirectory, Path);
+}
+
+void ClangMoveTool::addIncludes(llvm::StringRef IncludeHeader, bool IsAngled,
+                                llvm::StringRef SearchPath,
+                                llvm::StringRef FileName,
+                                clang::CharSourceRange IncludeFilenameRange,
+                                const SourceManager &SM) {
+  SmallVector<char, 128> HeaderWithSearchPath;
+  llvm::sys::path::append(HeaderWithSearchPath, SearchPath, IncludeHeader);
+  std::string AbsoluteOldHeader = makeAbsolutePath(Context->Spec.OldHeader);
+  if (AbsoluteOldHeader ==
+      MakeAbsolutePath(SM, llvm::StringRef(HeaderWithSearchPath.data(),
+                                           HeaderWithSearchPath.size()))) {
+    OldHeaderIncludeRange = IncludeFilenameRange;
+    return;
+  }
+
+  std::string IncludeLine =
+      IsAngled ? ("#include <" + IncludeHeader + ">\n").str()
+               : ("#include \"" + IncludeHeader + "\"\n").str();
+
+  std::string AbsoluteCurrentFile = MakeAbsolutePath(SM, FileName);
+  if (AbsoluteOldHeader == AbsoluteCurrentFile) {
+    HeaderIncludes.push_back(IncludeLine);
+  } else if (makeAbsolutePath(Context->Spec.OldCC) == AbsoluteCurrentFile) {
+    CCIncludes.push_back(IncludeLine);
+  }
+}
+
+void ClangMoveTool::removeDeclsInOldFiles() {
+  if (RemovedDecls.empty()) return;
+
+  // If old_header is not specified (only move declarations from old.cc), remain
+  // all the helper function declarations in old.cc as UnremovedDeclsInOldHeader
+  // is empty in this case, there is no way to verify unused/used helpers.
+  if (!Context->Spec.OldHeader.empty()) {
+    std::vector<const NamedDecl *> UnremovedDecls;
+    for (const auto *D : UnremovedDeclsInOldHeader)
+      UnremovedDecls.push_back(D);
+
+    auto UsedDecls = getUsedDecls(RGBuilder.getGraph(), UnremovedDecls);
+
+    // We remove the helper declarations which are not used in the old.cc after
+    // moving the given declarations.
+    for (const auto *D : HelperDeclarations) {
+      DEBUG(llvm::dbgs() << "Check helper is used: "
+                         << D->getNameAsString() << " (" << D << ")\n");
+      if (!UsedDecls.count(HelperDeclRGBuilder::getOutmostClassOrFunDecl(
+              D->getCanonicalDecl()))) {
+        DEBUG(llvm::dbgs() << "Helper removed in old.cc: "
+                           << D->getNameAsString() << " (" << D << ")\n");
+        RemovedDecls.push_back(D);
+      }
+    }
+  }
+
+  for (const auto *RemovedDecl : RemovedDecls) {
+    const auto &SM = RemovedDecl->getASTContext().getSourceManager();
+    auto Range = getFullRange(RemovedDecl);
+    clang::tooling::Replacement RemoveReplacement(
+        SM,
+        clang::CharSourceRange::getCharRange(Range.getBegin(), Range.getEnd()),
+        "");
+    std::string FilePath = RemoveReplacement.getFilePath().str();
+    auto Err = Context->FileToReplacements[FilePath].add(RemoveReplacement);
+    if (Err)
+      llvm::errs() << llvm::toString(std::move(Err)) << "\n";
+  }
+  const auto &SM = RemovedDecls[0]->getASTContext().getSourceManager();
+
+  // Post process of cleanup around all the replacements.
+  for (auto &FileAndReplacements : Context->FileToReplacements) {
+    StringRef FilePath = FileAndReplacements.first;
+    // Add #include of new header to old header.
+    if (Context->Spec.OldDependOnNew &&
+        MakeAbsolutePath(SM, FilePath) ==
+            makeAbsolutePath(Context->Spec.OldHeader)) {
+      // FIXME: Minimize the include path like include-fixer.
+      std::string IncludeNewH =
+          "#include \"" + Context->Spec.NewHeader + "\"\n";
+      // This replacment for inserting header will be cleaned up at the end.
+      auto Err = FileAndReplacements.second.add(
+          tooling::Replacement(FilePath, UINT_MAX, 0, IncludeNewH));
+      if (Err)
+        llvm::errs() << llvm::toString(std::move(Err)) << "\n";
+    }
+
+    auto SI = FilePathToFileID.find(FilePath);
+    // Ignore replacements for new.h/cc.
+    if (SI == FilePathToFileID.end()) continue;
+    llvm::StringRef Code = SM.getBufferData(SI->second);
+    auto Style = format::getStyle("file", FilePath, Context->FallbackStyle);
+    if (!Style) {
+      llvm::errs() << llvm::toString(Style.takeError()) << "\n";
+      continue;
+    }
+    auto CleanReplacements = format::cleanupAroundReplacements(
+        Code, Context->FileToReplacements[FilePath], *Style);
+
+    if (!CleanReplacements) {
+      llvm::errs() << llvm::toString(CleanReplacements.takeError()) << "\n";
+      continue;
+    }
+    Context->FileToReplacements[FilePath] = *CleanReplacements;
+  }
+}
+
+void ClangMoveTool::moveDeclsToNewFiles() {
+  std::vector<const NamedDecl *> NewHeaderDecls;
+  std::vector<const NamedDecl *> NewCCDecls;
+  for (const auto *MovedDecl : MovedDecls) {
+    if (isInHeaderFile(MovedDecl, Context->OriginalRunningDirectory,
+                       Context->Spec.OldHeader))
+      NewHeaderDecls.push_back(MovedDecl);
+    else
+      NewCCDecls.push_back(MovedDecl);
+  }
+
+  auto UsedDecls = getUsedDecls(RGBuilder.getGraph(), RemovedDecls);
+  std::vector<const NamedDecl *> ActualNewCCDecls;
+
+  // Filter out all unused helpers in NewCCDecls.
+  // We only move the used helpers (including transively used helpers) and the
+  // given symbols being moved.
+  for (const auto *D : NewCCDecls) {
+    if (llvm::is_contained(HelperDeclarations, D) &&
+        !UsedDecls.count(HelperDeclRGBuilder::getOutmostClassOrFunDecl(
+            D->getCanonicalDecl())))
+      continue;
+
+    DEBUG(llvm::dbgs() << "Helper used in new.cc: " << D->getNameAsString()
+                       << " " << D << "\n");
+    ActualNewCCDecls.push_back(D);
+  }
+
+  if (!Context->Spec.NewHeader.empty()) {
+    std::string OldHeaderInclude =
+        Context->Spec.NewDependOnOld
+            ? "#include \"" + Context->Spec.OldHeader + "\"\n"
+            : "";
+    Context->FileToReplacements[Context->Spec.NewHeader] =
+        createInsertedReplacements(HeaderIncludes, NewHeaderDecls,
+                                   Context->Spec.NewHeader, /*IsHeader=*/true,
+                                   OldHeaderInclude);
+  }
+  if (!Context->Spec.NewCC.empty())
+    Context->FileToReplacements[Context->Spec.NewCC] =
+        createInsertedReplacements(CCIncludes, ActualNewCCDecls,
+                                   Context->Spec.NewCC);
+}
+
+// Move all contents from OldFile to NewFile.
+void ClangMoveTool::moveAll(SourceManager &SM, StringRef OldFile,
+                            StringRef NewFile) {
+  const FileEntry *FE = SM.getFileManager().getFile(makeAbsolutePath(OldFile));
+  if (!FE) {
+    llvm::errs() << "Failed to get file: " << OldFile << "\n";
+    return;
+  }
+  FileID ID = SM.getOrCreateFileID(FE, SrcMgr::C_User);
+  auto Begin = SM.getLocForStartOfFile(ID);
+  auto End = SM.getLocForEndOfFile(ID);
+  clang::tooling::Replacement RemoveAll (
+      SM, clang::CharSourceRange::getCharRange(Begin, End), "");
+  std::string FilePath = RemoveAll.getFilePath().str();
+  Context->FileToReplacements[FilePath] =
+      clang::tooling::Replacements(RemoveAll);
+
+  StringRef Code = SM.getBufferData(ID);
+  if (!NewFile.empty()) {
+    auto AllCode = clang::tooling::Replacements(
+        clang::tooling::Replacement(NewFile, 0, 0, Code));
+    // If we are moving from old.cc, an extra step is required: excluding
+    // the #include of "old.h", instead, we replace it with #include of "new.h".
+    if (Context->Spec.NewCC == NewFile && OldHeaderIncludeRange.isValid()) {
+      AllCode = AllCode.merge(
+          clang::tooling::Replacements(clang::tooling::Replacement(
+              SM, OldHeaderIncludeRange, '"' + Context->Spec.NewHeader + '"')));
+    }
+    Context->FileToReplacements[NewFile] = std::move(AllCode);
+  }
+}
+
+void ClangMoveTool::onEndOfTranslationUnit() {
+  if (Context->DumpDeclarations) {
+    assert(Reporter);
+    for (const auto *Decl : UnremovedDeclsInOldHeader) {
+      auto Kind = Decl->getKind();
+      const std::string QualifiedName = Decl->getQualifiedNameAsString();
+      if (Kind == Decl::Kind::Var)
+        Reporter->reportDeclaration(QualifiedName, "Variable");
+      else if (Kind == Decl::Kind::Function ||
+               Kind == Decl::Kind::FunctionTemplate)
+        Reporter->reportDeclaration(QualifiedName, "Function");
+      else if (Kind == Decl::Kind::ClassTemplate ||
+               Kind == Decl::Kind::CXXRecord)
+        Reporter->reportDeclaration(QualifiedName, "Class");
+      else if (Kind == Decl::Kind::Enum)
+        Reporter->reportDeclaration(QualifiedName, "Enum");
+      else if (Kind == Decl::Kind::Typedef ||
+               Kind == Decl::Kind::TypeAlias ||
+               Kind == Decl::Kind::TypeAliasTemplate)
+        Reporter->reportDeclaration(QualifiedName, "TypeAlias");
+    }
+    return;
+  }
+
+  if (RemovedDecls.empty())
+    return;
+  // Ignore symbols that are not supported (e.g. typedef and enum) when
+  // checking if there is unremoved symbol in old header. This makes sure that
+  // we always move old files to new files when all symbols produced from
+  // dump_decls are moved.
+  auto IsSupportedKind = [](const clang::NamedDecl *Decl) {
+    switch (Decl->getKind()) {
+    case Decl::Kind::Function:
+    case Decl::Kind::FunctionTemplate:
+    case Decl::Kind::ClassTemplate:
+    case Decl::Kind::CXXRecord:
+    case Decl::Kind::Enum:
+    case Decl::Kind::Typedef:
+    case Decl::Kind::TypeAlias:
+    case Decl::Kind::TypeAliasTemplate:
+    case Decl::Kind::Var:
+      return true;
+    default:
+      return false;
+    }
+  };
+  if (std::none_of(UnremovedDeclsInOldHeader.begin(),
+                   UnremovedDeclsInOldHeader.end(), IsSupportedKind) &&
+      !Context->Spec.OldHeader.empty()) {
+    auto &SM = RemovedDecls[0]->getASTContext().getSourceManager();
+    moveAll(SM, Context->Spec.OldHeader, Context->Spec.NewHeader);
+    moveAll(SM, Context->Spec.OldCC, Context->Spec.NewCC);
+    return;
+  }
+  DEBUG(RGBuilder.getGraph()->dump());
+  moveDeclsToNewFiles();
+  removeDeclsInOldFiles();
+}
+
+} // namespace move
+} // namespace clang
diff --git a/clang-move/ClangMove.h b/clang-move/ClangMove.h
new file mode 100644 (file)
index 0000000..2522f53
--- /dev/null
@@ -0,0 +1,229 @@
+//===-- ClangMove.h - Clang move  -----------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_MOVE_CLANGMOVE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_MOVE_CLANGMOVE_H
+
+#include "HelperDeclRefGraph.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Frontend/FrontendAction.h"
+#include "clang/Tooling/Core/Replacement.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/ADT/StringMap.h"
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace clang {
+namespace move {
+
+// A reporter which collects and reports declarations in old header.
+class DeclarationReporter {
+public:
+  DeclarationReporter() = default;
+  ~DeclarationReporter() = default;
+
+  void reportDeclaration(llvm::StringRef DeclarationName,
+                         llvm::StringRef Type) {
+    DeclarationList.emplace_back(DeclarationName, Type);
+  };
+
+  // A <DeclarationName, DeclarationKind> pair.
+  // The DeclarationName is a fully qualified name for the declaration, like
+  // A::B::Foo. The DeclarationKind is a string represents the kind of the
+  // declaration, currently only "Function" and "Class" are supported.
+  typedef std::pair<std::string, std::string> DeclarationPair;
+
+  const std::vector<DeclarationPair> getDeclarationList() const {
+    return DeclarationList;
+  }
+
+private:
+  std::vector<DeclarationPair> DeclarationList;
+};
+
+// Specify declarations being moved. It contains all information of the moved
+// declarations.
+struct MoveDefinitionSpec {
+  // The list of fully qualified names, e.g. Foo, a::Foo, b::Foo.
+  SmallVector<std::string, 4> Names;
+  // The file path of old header, can be relative path and absolute path.
+  std::string OldHeader;
+  // The file path of old cc, can be relative path and absolute path.
+  std::string OldCC;
+  // The file path of new header, can be relative path and absolute path.
+  std::string NewHeader;
+  // The file path of new cc, can be relative path and absolute path.
+  std::string NewCC;
+  // Whether old.h depends on new.h. If true, #include "new.h" will be added
+  // in old.h.
+  bool OldDependOnNew = false;
+  // Whether new.h depends on old.h. If true, #include "old.h" will be added
+  // in new.h.
+  bool NewDependOnOld = false;
+};
+
+// A Context which contains extra options which are used in ClangMoveTool.
+struct ClangMoveContext {
+  MoveDefinitionSpec Spec;
+  // The Key is file path, value is the replacements being applied to the file.
+  std::map<std::string, tooling::Replacements> &FileToReplacements;
+  // The original working directory where the local clang-move binary runs.
+  //
+  // clang-move will change its current working directory to the build
+  // directory when analyzing the source file. We save the original working
+  // directory in order to get the absolute file path for the fields in Spec.
+  std::string OriginalRunningDirectory;
+  // The name of a predefined code style.
+  std::string FallbackStyle;
+  // Whether dump all declarations in old header.
+  bool DumpDeclarations;
+};
+
+// This tool is used to move class/function definitions from the given source
+// files (old.h/cc) to new files (new.h/cc).
+// The goal of this tool is to make the new/old files as compilable as possible.
+//
+// When moving a symbol,all used helper declarations (e.g. static
+// functions/variables definitions in global/named namespace,
+// functions/variables/classes definitions in anonymous namespace) used by the
+// moved symbol in old.cc are moved to the new.cc. In addition, all
+// using-declarations in old.cc are also moved to new.cc; forward class
+// declarations in old.h are also moved to new.h.
+//
+// The remaining helper declarations which are unused by non-moved symbols in
+// old.cc will be removed.
+//
+// Note: When all declarations in old header are being moved, all code in
+// old.h/cc will be moved, which means old.h/cc are empty. This ignores symbols
+// that are not supported (e.g. typedef and enum) so that we always move old
+// files to new files when all symbols produced from dump_decls are moved.
+class ClangMoveTool : public ast_matchers::MatchFinder::MatchCallback {
+public:
+  ClangMoveTool(ClangMoveContext *const Context,
+                DeclarationReporter *const Reporter);
+
+  void registerMatchers(ast_matchers::MatchFinder *Finder);
+
+  void run(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+  void onEndOfTranslationUnit() override;
+
+  /// Add #includes from old.h/cc files.
+  ///
+  /// \param IncludeHeader The name of the file being included, as written in
+  /// the source code.
+  /// \param IsAngled Whether the file name was enclosed in angle brackets.
+  /// \param SearchPath The search path which was used to find the IncludeHeader
+  /// in the file system. It can be a relative path or an absolute path.
+  /// \param FileName The name of file where the IncludeHeader comes from.
+  /// \param IncludeFilenameRange The source range for the written file name in
+  /// #include (i.e. "old.h" for #include "old.h") in old.cc.
+  /// \param SM The SourceManager.
+  void addIncludes(llvm::StringRef IncludeHeader, bool IsAngled,
+                   llvm::StringRef SearchPath, llvm::StringRef FileName,
+                   clang::CharSourceRange IncludeFilenameRange,
+                   const SourceManager &SM);
+
+  std::vector<const NamedDecl *> &getMovedDecls() { return MovedDecls; }
+
+  /// Add declarations being removed from old.h/cc. For each declarations, the
+  /// method also records the mapping relationship between the corresponding
+  /// FilePath and its FileID.
+  void addRemovedDecl(const NamedDecl *Decl);
+
+  llvm::SmallPtrSet<const NamedDecl *, 8> &getUnremovedDeclsInOldHeader() {
+    return UnremovedDeclsInOldHeader;
+  }
+
+private:
+  // Make the Path absolute using the OrignalRunningDirectory if the Path is not
+  // an absolute path. An empty Path will result in an empty string.
+  std::string makeAbsolutePath(StringRef Path);
+
+  void removeDeclsInOldFiles();
+  void moveDeclsToNewFiles();
+  void moveAll(SourceManager& SM, StringRef OldFile, StringRef NewFile);
+
+  // Stores all MatchCallbacks created by this tool.
+  std::vector<std::unique_ptr<ast_matchers::MatchFinder::MatchCallback>>
+      MatchCallbacks;
+  // Store all potential declarations (decls being moved, forward decls) that
+  // might need to move to new.h/cc. It includes all helper declarations
+  // (include unused ones) by default. The unused ones will be filtered out in
+  // the last stage. Saving in an AST-visited order.
+  std::vector<const NamedDecl *> MovedDecls;
+  // The declarations that needs to be removed in old.cc/h.
+  std::vector<const NamedDecl *> RemovedDecls;
+  // The #includes in old_header.h.
+  std::vector<std::string> HeaderIncludes;
+  // The #includes in old_cc.cc.
+  std::vector<std::string> CCIncludes;
+  // Records all helper declarations (function/variable/class definitions in
+  // anonymous namespaces, static function/variable definitions in global/named
+  // namespaces) in old.cc. saving in an AST-visited order.
+  std::vector<const NamedDecl *> HelperDeclarations;
+  // The unmoved named declarations in old header.
+  llvm::SmallPtrSet<const NamedDecl*, 8> UnremovedDeclsInOldHeader;
+  /// The source range for the written file name in #include (i.e. "old.h" for
+  /// #include "old.h") in old.cc,  including the enclosing quotes or angle
+  /// brackets.
+  clang::CharSourceRange OldHeaderIncludeRange;
+  /// Mapping from FilePath to FileID, which can be used in post processes like
+  /// cleanup around replacements.
+  llvm::StringMap<FileID> FilePathToFileID;
+  /// A context contains all running options. It is not owned.
+  ClangMoveContext *const Context;
+  /// A reporter to report all declarations from old header. It is not owned.
+  DeclarationReporter *const Reporter;
+  /// Builder for helper declarations reference graph.
+  HelperDeclRGBuilder RGBuilder;
+};
+
+class ClangMoveAction : public clang::ASTFrontendAction {
+public:
+  ClangMoveAction(ClangMoveContext *const Context,
+                  DeclarationReporter *const Reporter)
+      : MoveTool(Context, Reporter) {
+    MoveTool.registerMatchers(&MatchFinder);
+  }
+
+  ~ClangMoveAction() override = default;
+
+  std::unique_ptr<clang::ASTConsumer>
+  CreateASTConsumer(clang::CompilerInstance &Compiler,
+                    llvm::StringRef InFile) override;
+
+private:
+  ast_matchers::MatchFinder MatchFinder;
+  ClangMoveTool MoveTool;
+};
+
+class ClangMoveActionFactory : public tooling::FrontendActionFactory {
+public:
+  ClangMoveActionFactory(ClangMoveContext *const Context,
+                         DeclarationReporter *const Reporter = nullptr)
+      : Context(Context), Reporter(Reporter) {}
+
+  clang::FrontendAction *create() override {
+    return new ClangMoveAction(Context, Reporter);
+  }
+
+private:
+  // Not owned.
+  ClangMoveContext *const Context;
+  DeclarationReporter *const Reporter;
+};
+
+} // namespace move
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_MOVE_CLANGMOVE_H
diff --git a/clang-move/HelperDeclRefGraph.cpp b/clang-move/HelperDeclRefGraph.cpp
new file mode 100644 (file)
index 0000000..00ab9ea
--- /dev/null
@@ -0,0 +1,137 @@
+//===-- UsedHelperDeclFinder.cpp - AST-based call graph for helper decls --===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "HelperDeclRefGraph.h"
+#include "ClangMove.h"
+#include "clang/AST/Decl.h"
+#include "llvm/Support/Debug.h"
+#include <vector>
+
+#define DEBUG_TYPE "clang-move"
+
+namespace clang {
+namespace move {
+
+void HelperDeclRefGraph::print(raw_ostream &OS) const {
+  OS << " --- Call graph Dump --- \n";
+  for (auto I = DeclMap.begin(); I != DeclMap.end(); ++I) {
+    const CallGraphNode *N = (I->second).get();
+
+    OS << "  Declarations: ";
+    N->print(OS);
+    OS << " (" << N << ") ";
+    OS << " calls: ";
+    for (auto CI = N->begin(), CE = N->end(); CI != CE; ++CI) {
+      (*CI)->print(OS);
+      OS << " (" << CI << ") ";
+    }
+    OS << '\n';
+  }
+  OS.flush();
+}
+
+void HelperDeclRefGraph::addEdge(const Decl *Caller, const Decl *Callee) {
+  assert(Caller);
+  assert(Callee);
+
+  // Ignore the case where Caller equals Callee. This happens in the static
+  // class member definitions in global namespace like "int CLASS::static_var =
+  // 1;", its DC is a VarDel whose outmost enclosing declaration is the "CLASS"
+  // CXXRecordDecl.
+  if (Caller == Callee) return;
+
+  // Allocate a new node, mark it as root, and process it's calls.
+  CallGraphNode *CallerNode = getOrInsertNode(const_cast<Decl *>(Caller));
+  CallGraphNode *CalleeNode = getOrInsertNode(const_cast<Decl *>(Callee));
+  CallerNode->addCallee(CalleeNode);
+}
+
+void HelperDeclRefGraph::dump() const { print(llvm::errs()); }
+
+CallGraphNode *HelperDeclRefGraph::getOrInsertNode(Decl *F) {
+  F = F->getCanonicalDecl();
+  std::unique_ptr<CallGraphNode> &Node = DeclMap[F];
+  if (Node)
+    return Node.get();
+
+  Node = llvm::make_unique<CallGraphNode>(F);
+  return Node.get();
+}
+
+CallGraphNode *HelperDeclRefGraph::getNode(const Decl *D) const {
+  auto I = DeclMap.find(D->getCanonicalDecl());
+  return I == DeclMap.end() ? nullptr : I->second.get();
+}
+
+llvm::DenseSet<const CallGraphNode *>
+HelperDeclRefGraph::getReachableNodes(const Decl *Root) const {
+  const auto *RootNode = getNode(Root);
+  if (!RootNode)
+    return {};
+  llvm::DenseSet<const CallGraphNode *> ConnectedNodes;
+  std::function<void(const CallGraphNode *)> VisitNode =
+      [&](const CallGraphNode *Node) {
+        if (ConnectedNodes.count(Node))
+          return;
+        ConnectedNodes.insert(Node);
+        for (auto It = Node->begin(), End = Node->end(); It != End; ++It)
+          VisitNode(*It);
+      };
+
+  VisitNode(RootNode);
+  return ConnectedNodes;
+}
+
+const Decl *HelperDeclRGBuilder::getOutmostClassOrFunDecl(const Decl *D) {
+  const auto *DC = D->getDeclContext();
+  const auto *Result = D;
+  while (DC) {
+    if (const auto *RD = dyn_cast<CXXRecordDecl>(DC))
+      Result = RD;
+    else if (const auto *FD = dyn_cast<FunctionDecl>(DC))
+      Result = FD;
+    DC = DC->getParent();
+  }
+  return Result;
+}
+
+void HelperDeclRGBuilder::run(
+    const ast_matchers::MatchFinder::MatchResult &Result) {
+  // Construct the graph by adding a directed edge from caller to callee.
+  //
+  // "dc" is the closest ancestor declaration of "func_ref" or "used_class", it
+  // might be not the targetted Caller Decl, we always use the outmost enclosing
+  // FunctionDecl/CXXRecordDecl of "dc". For example,
+  //
+  //   int MoveClass::F() { int a = helper(); return a; }
+  //
+  // The matched "dc" of "helper" DeclRefExpr is a VarDecl, we traverse up AST
+  // to find the outmost "MoveClass" CXXRecordDecl and use it as Caller.
+  if (const auto *FuncRef = Result.Nodes.getNodeAs<DeclRefExpr>("func_ref")) {
+    const auto *DC = Result.Nodes.getNodeAs<Decl>("dc");
+    assert(DC);
+    DEBUG(llvm::dbgs() << "Find helper function usage: "
+                       << FuncRef->getDecl()->getNameAsString() << " ("
+                       << FuncRef->getDecl() << ")\n");
+    RG->addEdge(
+        getOutmostClassOrFunDecl(DC->getCanonicalDecl()),
+        getOutmostClassOrFunDecl(FuncRef->getDecl()->getCanonicalDecl()));
+  } else if (const auto *UsedClass =
+                 Result.Nodes.getNodeAs<CXXRecordDecl>("used_class")) {
+    const auto *DC = Result.Nodes.getNodeAs<Decl>("dc");
+    assert(DC);
+    DEBUG(llvm::dbgs() << "Find helper class usage: "
+                       << UsedClass->getNameAsString() << " (" << UsedClass
+                       << ")\n");
+    RG->addEdge(getOutmostClassOrFunDecl(DC->getCanonicalDecl()), UsedClass);
+  }
+}
+
+} // namespace move
+} // namespace clang
diff --git a/clang-move/HelperDeclRefGraph.h b/clang-move/HelperDeclRefGraph.h
new file mode 100644 (file)
index 0000000..11b3c9c
--- /dev/null
@@ -0,0 +1,99 @@
+//===-- UsedHelperDeclFinder.h - AST-based call graph for helper decls ----===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_MOVE_USED_HELPER_DECL_FINDER_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_MOVE_USED_HELPER_DECL_FINDER_H
+
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Analysis/CallGraph.h"
+#include "llvm/ADT/DenseSet.h"
+#include <memory>
+#include <vector>
+
+namespace clang {
+namespace move {
+
+// A reference graph for finding used/unused helper declarations in a single
+// translation unit (e.g. old.cc). We don't reuse CallGraph in clang/Analysis
+// because that CallGraph only supports function declarations.
+//
+// Helper declarations include following types:
+//   * function/variable/class definitions in an anonymous namespace.
+//   * static function/variable definitions in a global/named namespace.
+//
+// The reference graph is a directed graph. Each node in the graph represents a
+// helper declaration in old.cc or a non-moved/moved declaration (e.g. class,
+// function) in old.h, which means each node is associated with a Decl.
+//
+// To construct the graph, we use AST matcher to find interesting Decls (usually
+// a pair of Caller and Callee), and add an edge from the Caller node to the
+// Callee node.
+//
+// Specially, for a class, it might have multiple declarations such methods
+// and member variables. We only use a single node to present this class, and
+// this node is associated with the class declaration (CXXRecordDecl).
+//
+// The graph has 3 types of edges:
+//   1. moved_decl => helper_decl
+//   2. non_moved_decl => helper_decl
+//   3. helper_decl => helper_decl
+class HelperDeclRefGraph {
+public:
+  HelperDeclRefGraph() = default;
+  ~HelperDeclRefGraph() = default;
+
+  // Add a directed edge from the caller node to the callee node.
+  // A new node will be created if the node for Caller/Callee doesn't exist.
+  //
+  // Note that, all class member declarations are represented by a single node
+  // in the graph. The corresponding Decl of this node is the class declaration.
+  void addEdge(const Decl *Caller, const Decl *Callee);
+  CallGraphNode *getNode(const Decl *D) const;
+
+  // Get all reachable nodes in the graph from the given declaration D's node,
+  // including D.
+  llvm::DenseSet<const CallGraphNode *> getReachableNodes(const Decl *D) const;
+
+  // Dump the call graph for debug purpose.
+  void dump() const;
+
+private:
+  void print(raw_ostream &OS) const;
+  // Lookup a node for the given declaration D. If not found, insert a new
+  // node into the graph.
+  CallGraphNode *getOrInsertNode(Decl *D);
+
+  typedef llvm::DenseMap<const Decl *, std::unique_ptr<CallGraphNode>>
+      DeclMapTy;
+
+  // DeclMap owns all CallGraphNodes.
+  DeclMapTy DeclMap;
+};
+
+// A builder helps to construct a call graph of helper declarations.
+class HelperDeclRGBuilder : public ast_matchers::MatchFinder::MatchCallback {
+public:
+  HelperDeclRGBuilder() : RG(new HelperDeclRefGraph) {}
+  void run(const ast_matchers::MatchFinder::MatchResult &Result) override;
+  const HelperDeclRefGraph *getGraph() const { return RG.get(); }
+
+  // Find out the outmost enclosing class/function declaration of a given D.
+  // For a CXXMethodDecl, get its CXXRecordDecl; For a VarDecl/FunctionDecl, get
+  // its outmost enclosing FunctionDecl or CXXRecordDecl.
+  // Return D if not found.
+  static const Decl *getOutmostClassOrFunDecl(const Decl *D);
+
+private:
+  std::unique_ptr<HelperDeclRefGraph> RG;
+};
+
+} // namespace move
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_MOVE_USED_HELPER_DECL_FINDER_H
diff --git a/clang-move/tool/CMakeLists.txt b/clang-move/tool/CMakeLists.txt
new file mode 100644 (file)
index 0000000..6840915
--- /dev/null
@@ -0,0 +1,17 @@
+include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..)
+
+add_clang_executable(clang-move
+  ClangMoveMain.cpp
+  )
+
+target_link_libraries(clang-move
+  clangAST
+  clangASTMatchers
+  clangBasic
+  clangFormat
+  clangFrontend
+  clangMove
+  clangRewrite
+  clangTooling
+  clangToolingCore
+  )
diff --git a/clang-move/tool/ClangMoveMain.cpp b/clang-move/tool/ClangMoveMain.cpp
new file mode 100644 (file)
index 0000000..2c89842
--- /dev/null
@@ -0,0 +1,210 @@
+//===-- ClangMoveMain.cpp - move defintion to new file ----------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ClangMove.h"
+#include "clang/Frontend/TextDiagnosticPrinter.h"
+#include "clang/Rewrite/Core/Rewriter.h"
+#include "clang/Tooling/ArgumentsAdjusters.h"
+#include "clang/Tooling/CommonOptionsParser.h"
+#include "clang/Tooling/Refactoring.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/Process.h"
+#include "llvm/Support/Signals.h"
+#include "llvm/Support/YAMLTraits.h"
+#include <set>
+#include <string>
+
+using namespace clang;
+using namespace llvm;
+
+namespace {
+
+std::error_code CreateNewFile(const llvm::Twine &path) {
+  int fd = 0;
+  if (std::error_code ec =
+          llvm::sys::fs::openFileForWrite(path, fd, llvm::sys::fs::F_Text))
+    return ec;
+
+  return llvm::sys::Process::SafelyCloseFileDescriptor(fd);
+}
+
+cl::OptionCategory ClangMoveCategory("clang-move options");
+
+cl::list<std::string> Names("names", cl::CommaSeparated,
+                            cl::desc("The list of the names of classes being "
+                                     "moved, e.g. \"Foo,a::Foo,b::Foo\"."),
+                            cl::cat(ClangMoveCategory));
+
+cl::opt<std::string>
+    OldHeader("old_header",
+              cl::desc("The relative/absolute file path of old header."),
+              cl::cat(ClangMoveCategory));
+
+cl::opt<std::string>
+    OldCC("old_cc", cl::desc("The relative/absolute file path of old cc."),
+          cl::cat(ClangMoveCategory));
+
+cl::opt<std::string>
+    NewHeader("new_header",
+              cl::desc("The relative/absolute file path of new header."),
+              cl::cat(ClangMoveCategory));
+
+cl::opt<std::string>
+    NewCC("new_cc", cl::desc("The relative/absolute file path of new cc."),
+          cl::cat(ClangMoveCategory));
+
+cl::opt<bool>
+    OldDependOnNew("old_depend_on_new",
+                   cl::desc("Whether old header will depend on new header. If "
+                            "true, clang-move will "
+                            "add #include of new header to old header."),
+                   cl::init(false), cl::cat(ClangMoveCategory));
+
+cl::opt<bool>
+    NewDependOnOld("new_depend_on_old",
+                   cl::desc("Whether new header will depend on old header. If "
+                            "true, clang-move will "
+                            "add #include of old header to new header."),
+                   cl::init(false), cl::cat(ClangMoveCategory));
+
+cl::opt<std::string>
+    Style("style",
+          cl::desc("The style name used for reformatting. Default is \"llvm\""),
+          cl::init("llvm"), cl::cat(ClangMoveCategory));
+
+cl::opt<bool> Dump("dump_result",
+                   cl::desc("Dump results in JSON format to stdout."),
+                   cl::cat(ClangMoveCategory));
+
+cl::opt<bool> DumpDecls(
+    "dump_decls",
+    cl::desc("Dump all declarations in old header (JSON format) to stdout. If "
+             "the option is specified, other command options will be ignored. "
+             "An empty JSON will be returned if old header isn't specified."),
+    cl::cat(ClangMoveCategory));
+
+} // namespace
+
+int main(int argc, const char **argv) {
+  llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
+  tooling::CommonOptionsParser OptionsParser(argc, argv, ClangMoveCategory);
+
+  if (OldDependOnNew && NewDependOnOld) {
+    llvm::errs() << "Provide either --old_depend_on_new or "
+                    "--new_depend_on_old. clang-move doesn't support these two "
+                    "options at same time (It will introduce include cycle).\n";
+    return 1;
+  }
+
+  tooling::RefactoringTool Tool(OptionsParser.getCompilations(),
+                                OptionsParser.getSourcePathList());
+  // Add "-fparse-all-comments" compile option to make clang parse all comments.
+  Tool.appendArgumentsAdjuster(tooling::getInsertArgumentAdjuster(
+      "-fparse-all-comments", tooling::ArgumentInsertPosition::BEGIN));
+  move::MoveDefinitionSpec Spec;
+  Spec.Names = {Names.begin(), Names.end()};
+  Spec.OldHeader = OldHeader;
+  Spec.NewHeader = NewHeader;
+  Spec.OldCC = OldCC;
+  Spec.NewCC = NewCC;
+  Spec.OldDependOnNew = OldDependOnNew;
+  Spec.NewDependOnOld = NewDependOnOld;
+
+  llvm::SmallString<128> InitialDirectory;
+  if (std::error_code EC = llvm::sys::fs::current_path(InitialDirectory))
+    llvm::report_fatal_error("Cannot detect current path: " +
+                             Twine(EC.message()));
+
+  move::ClangMoveContext Context{Spec, Tool.getReplacements(),
+                                 InitialDirectory.str(), Style, DumpDecls};
+  move::DeclarationReporter Reporter;
+  move::ClangMoveActionFactory Factory(&Context, &Reporter);
+  
+  int CodeStatus = Tool.run(&Factory);
+  if (CodeStatus)
+    return CodeStatus;
+
+  if (DumpDecls) {
+    llvm::outs() << "[\n";
+    const auto &Declarations = Reporter.getDeclarationList();
+    for (auto I = Declarations.begin(), E = Declarations.end(); I != E; ++I) {
+      llvm::outs() << "  {\n";
+      llvm::outs() << "    \"DeclarationName\": \"" << I->first << "\",\n";
+      llvm::outs() << "    \"DeclarationType\": \"" << I->second << "\"\n";
+      llvm::outs() << "  }";
+      // Don't print trailing "," at the end of last element.
+      if (I != std::prev(E))
+        llvm::outs() << ",\n";
+    }
+    llvm::outs() << "\n]\n";
+    return 0;
+  }
+
+  if (!NewCC.empty()) {
+    std::error_code EC = CreateNewFile(NewCC);
+    if (EC) {
+      llvm::errs() << "Failed to create " << NewCC << ": " << EC.message()
+                   << "\n";
+      return EC.value();
+    }
+  }
+  if (!NewHeader.empty()) {
+    std::error_code EC = CreateNewFile(NewHeader);
+    if (EC) {
+      llvm::errs() << "Failed to create " << NewHeader << ": " << EC.message()
+                   << "\n";
+      return EC.value();
+    }
+  }
+
+  IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(new DiagnosticOptions());
+  clang::TextDiagnosticPrinter DiagnosticPrinter(errs(), &*DiagOpts);
+  DiagnosticsEngine Diagnostics(
+      IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), &*DiagOpts,
+      &DiagnosticPrinter, false);
+  auto &FileMgr = Tool.getFiles();
+  SourceManager SM(Diagnostics, FileMgr);
+  Rewriter Rewrite(SM, LangOptions());
+
+  if (!formatAndApplyAllReplacements(Tool.getReplacements(), Rewrite, Style)) {
+    llvm::errs() << "Failed applying all replacements.\n";
+    return 1;
+  }
+
+  if (Dump) {
+    std::set<llvm::StringRef> Files;
+    for (const auto &it : Tool.getReplacements())
+      Files.insert(it.first);
+    auto WriteToJson = [&](llvm::raw_ostream &OS) {
+      OS << "[\n";
+      for (auto I = Files.begin(), E = Files.end(); I != E; ++I) {
+        OS << "  {\n";
+        OS << "    \"FilePath\": \"" << *I << "\",\n";
+        const auto *Entry = FileMgr.getFile(*I);
+        auto ID = SM.translateFile(Entry);
+        std::string Content;
+        llvm::raw_string_ostream ContentStream(Content);
+        Rewrite.getEditBuffer(ID).write(ContentStream);
+        OS << "    \"SourceText\": \""
+           << llvm::yaml::escape(ContentStream.str()) << "\"\n";
+        OS << "  }";
+        if (I != std::prev(E))
+          OS << ",\n";
+      }
+      OS << "\n]\n";
+    };
+    WriteToJson(llvm::outs());
+    return 0;
+  }
+
+  return Rewrite.overwriteChangedFiles();
+}
diff --git a/clang-query/CMakeLists.txt b/clang-query/CMakeLists.txt
new file mode 100644 (file)
index 0000000..46febd6
--- /dev/null
@@ -0,0 +1,18 @@
+set(LLVM_LINK_COMPONENTS
+  lineeditor
+  support
+  )
+
+add_clang_library(clangQuery
+  Query.cpp
+  QueryParser.cpp
+
+  LINK_LIBS
+  clangAST
+  clangASTMatchers
+  clangBasic
+  clangDynamicASTMatchers
+  clangFrontend
+  )
+
+add_subdirectory(tool)
diff --git a/clang-query/Query.cpp b/clang-query/Query.cpp
new file mode 100644 (file)
index 0000000..072ee6b
--- /dev/null
@@ -0,0 +1,146 @@
+//===---- Query.cpp - clang-query query -----------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Query.h"
+#include "QuerySession.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Frontend/ASTUnit.h"
+#include "clang/Frontend/TextDiagnostic.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang::ast_matchers;
+using namespace clang::ast_matchers::dynamic;
+
+namespace clang {
+namespace query {
+
+Query::~Query() {}
+
+bool InvalidQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
+  OS << ErrStr << "\n";
+  return false;
+}
+
+bool NoOpQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
+  return true;
+}
+
+bool HelpQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
+  OS << "Available commands:\n\n"
+        "  match MATCHER, m MATCHER          "
+        "Match the loaded ASTs against the given matcher.\n"
+        "  let NAME MATCHER, l NAME MATCHER  "
+        "Give a matcher expression a name, to be used later\n"
+        "                                    "
+        "as part of other expressions.\n"
+        "  set bind-root (true|false)        "
+        "Set whether to bind the root matcher to \"root\".\n"
+        "  set output (diag|print|dump)      "
+        "Set whether to print bindings as diagnostics,\n"
+        "                                    "
+        "AST pretty prints or AST dumps.\n"
+        "  quit                              "
+        "Terminates the query session.\n\n";
+  return true;
+}
+
+bool QuitQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
+  QS.Terminate = true;
+  return true;
+}
+
+namespace {
+
+struct CollectBoundNodes : MatchFinder::MatchCallback {
+  std::vector<BoundNodes> &Bindings;
+  CollectBoundNodes(std::vector<BoundNodes> &Bindings) : Bindings(Bindings) {}
+  void run(const MatchFinder::MatchResult &Result) override {
+    Bindings.push_back(Result.Nodes);
+  }
+};
+
+} // namespace
+
+bool MatchQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
+  unsigned MatchCount = 0;
+
+  for (auto &AST : QS.ASTs) {
+    MatchFinder Finder;
+    std::vector<BoundNodes> Matches;
+    DynTypedMatcher MaybeBoundMatcher = Matcher;
+    if (QS.BindRoot) {
+      llvm::Optional<DynTypedMatcher> M = Matcher.tryBind("root");
+      if (M)
+        MaybeBoundMatcher = *M;
+    }
+    CollectBoundNodes Collect(Matches);
+    if (!Finder.addDynamicMatcher(MaybeBoundMatcher, &Collect)) {
+      OS << "Not a valid top-level matcher.\n";
+      return false;
+    }
+    Finder.matchAST(AST->getASTContext());
+
+    for (auto MI = Matches.begin(), ME = Matches.end(); MI != ME; ++MI) {
+      OS << "\nMatch #" << ++MatchCount << ":\n\n";
+
+      for (auto BI = MI->getMap().begin(), BE = MI->getMap().end(); BI != BE;
+           ++BI) {
+        switch (QS.OutKind) {
+        case OK_Diag: {
+          clang::SourceRange R = BI->second.getSourceRange();
+          if (R.isValid()) {
+            TextDiagnostic TD(OS, AST->getASTContext().getLangOpts(),
+                              &AST->getDiagnostics().getDiagnosticOptions());
+            TD.emitDiagnostic(
+                FullSourceLoc(R.getBegin(), AST->getSourceManager()),
+                DiagnosticsEngine::Note, "\"" + BI->first + "\" binds here",
+                CharSourceRange::getTokenRange(R), None);
+          }
+          break;
+        }
+        case OK_Print: {
+          OS << "Binding for \"" << BI->first << "\":\n";
+          BI->second.print(OS, AST->getASTContext().getPrintingPolicy());
+          OS << "\n";
+          break;
+        }
+        case OK_Dump: {
+          OS << "Binding for \"" << BI->first << "\":\n";
+          BI->second.dump(OS, AST->getSourceManager());
+          OS << "\n";
+          break;
+        }
+        }
+      }
+
+      if (MI->getMap().empty())
+        OS << "No bindings.\n";
+    }
+  }
+
+  OS << MatchCount << (MatchCount == 1 ? " match.\n" : " matches.\n");
+  return true;
+}
+
+bool LetQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
+  if (Value) {
+    QS.NamedValues[Name] = Value;
+  } else {
+    QS.NamedValues.erase(Name);
+  }
+  return true;
+}
+
+#ifndef _MSC_VER
+const QueryKind SetQueryKind<bool>::value;
+const QueryKind SetQueryKind<OutputKind>::value;
+#endif
+
+} // namespace query
+} // namespace clang
diff --git a/clang-query/Query.h b/clang-query/Query.h
new file mode 100644 (file)
index 0000000..b8c59cb
--- /dev/null
@@ -0,0 +1,136 @@
+//===--- Query.h - clang-query ----------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_QUERY_QUERY_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_QUERY_QUERY_H
+
+#include "clang/ASTMatchers/Dynamic/VariantValue.h"
+#include "llvm/ADT/IntrusiveRefCntPtr.h"
+#include "llvm/ADT/Optional.h"
+#include <string>
+
+namespace clang {
+namespace query {
+
+enum OutputKind { OK_Diag, OK_Print, OK_Dump };
+
+enum QueryKind {
+  QK_Invalid,
+  QK_NoOp,
+  QK_Help,
+  QK_Let,
+  QK_Match,
+  QK_SetBool,
+  QK_SetOutputKind,
+  QK_Quit
+};
+
+class QuerySession;
+
+struct Query : llvm::RefCountedBase<Query> {
+  Query(QueryKind Kind) : Kind(Kind) {}
+  virtual ~Query();
+
+  /// Perform the query on \p QS and print output to \p OS.
+  ///
+  /// \return false if an error occurs, otherwise return true.
+  virtual bool run(llvm::raw_ostream &OS, QuerySession &QS) const = 0;
+
+  const QueryKind Kind;
+};
+
+typedef llvm::IntrusiveRefCntPtr<Query> QueryRef;
+
+/// Any query which resulted in a parse error.  The error message is in ErrStr.
+struct InvalidQuery : Query {
+  InvalidQuery(const Twine &ErrStr) : Query(QK_Invalid), ErrStr(ErrStr.str()) {}
+  bool run(llvm::raw_ostream &OS, QuerySession &QS) const override;
+
+  std::string ErrStr;
+
+  static bool classof(const Query *Q) { return Q->Kind == QK_Invalid; }
+};
+
+/// No-op query (i.e. a blank line).
+struct NoOpQuery : Query {
+  NoOpQuery() : Query(QK_NoOp) {}
+  bool run(llvm::raw_ostream &OS, QuerySession &QS) const override;
+
+  static bool classof(const Query *Q) { return Q->Kind == QK_NoOp; }
+};
+
+/// Query for "help".
+struct HelpQuery : Query {
+  HelpQuery() : Query(QK_Help) {}
+  bool run(llvm::raw_ostream &OS, QuerySession &QS) const override;
+
+  static bool classof(const Query *Q) { return Q->Kind == QK_Help; }
+};
+
+/// Query for "quit".
+struct QuitQuery : Query {
+  QuitQuery() : Query(QK_Quit) {}
+  bool run(llvm::raw_ostream &OS, QuerySession &QS) const override;
+
+  static bool classof(const Query *Q) { return Q->Kind == QK_Quit; }
+};
+
+/// Query for "match MATCHER".
+struct MatchQuery : Query {
+  MatchQuery(const ast_matchers::dynamic::DynTypedMatcher &Matcher)
+      : Query(QK_Match), Matcher(Matcher) {}
+  bool run(llvm::raw_ostream &OS, QuerySession &QS) const override;
+
+  ast_matchers::dynamic::DynTypedMatcher Matcher;
+
+  static bool classof(const Query *Q) { return Q->Kind == QK_Match; }
+};
+
+struct LetQuery : Query {
+  LetQuery(StringRef Name, const ast_matchers::dynamic::VariantValue &Value)
+      : Query(QK_Let), Name(Name), Value(Value) {}
+  bool run(llvm::raw_ostream &OS, QuerySession &QS) const override;
+
+  std::string Name;
+  ast_matchers::dynamic::VariantValue Value;
+
+  static bool classof(const Query *Q) { return Q->Kind == QK_Let; }
+};
+
+template <typename T> struct SetQueryKind {};
+
+template <> struct SetQueryKind<bool> {
+  static const QueryKind value = QK_SetBool;
+};
+
+template <> struct SetQueryKind<OutputKind> {
+  static const QueryKind value = QK_SetOutputKind;
+};
+
+/// Query for "set VAR VALUE".
+template <typename T> struct SetQuery : Query {
+  SetQuery(T QuerySession::*Var, T Value)
+      : Query(SetQueryKind<T>::value), Var(Var), Value(Value) {}
+  bool run(llvm::raw_ostream &OS, QuerySession &QS) const override {
+    QS.*Var = Value;
+    return true;
+  }
+
+  static bool classof(const Query *Q) {
+    return Q->Kind == SetQueryKind<T>::value;
+  }
+
+  T QuerySession::*Var;
+  T Value;
+};
+
+} // namespace query
+} // namespace clang
+
+#endif
diff --git a/clang-query/QueryParser.cpp b/clang-query/QueryParser.cpp
new file mode 100644 (file)
index 0000000..c64d810
--- /dev/null
@@ -0,0 +1,278 @@
+//===---- QueryParser.cpp - clang-query command parser --------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "QueryParser.h"
+#include "Query.h"
+#include "QuerySession.h"
+#include "clang/ASTMatchers/Dynamic/Parser.h"
+#include "clang/Basic/CharInfo.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringSwitch.h"
+#include <set>
+
+using namespace llvm;
+using namespace clang::ast_matchers::dynamic;
+
+namespace clang {
+namespace query {
+
+// Lex any amount of whitespace followed by a "word" (any sequence of
+// non-whitespace characters) from the start of region [Begin,End).  If no word
+// is found before End, return StringRef().  Begin is adjusted to exclude the
+// lexed region.
+StringRef QueryParser::lexWord() {
+  while (true) {
+    if (Begin == End)
+      return StringRef(Begin, 0);
+
+    if (!isWhitespace(*Begin))
+      break;
+
+    ++Begin;
+  }
+
+  const char *WordBegin = Begin;
+
+  while (true) {
+    ++Begin;
+
+    if (Begin == End || isWhitespace(*Begin))
+      return StringRef(WordBegin, Begin - WordBegin);
+  }
+}
+
+// This is the StringSwitch-alike used by lexOrCompleteWord below. See that
+// function for details.
+template <typename T> struct QueryParser::LexOrCompleteWord {
+  StringSwitch<T> Switch;
+
+  QueryParser *P;
+  StringRef Word;
+  // Set to the completion point offset in Word, or StringRef::npos if
+  // completion point not in Word.
+  size_t WordCompletionPos;
+
+  LexOrCompleteWord(QueryParser *P, StringRef Word, size_t WCP)
+      : Switch(Word), P(P), Word(Word), WordCompletionPos(WCP) {}
+
+  template <unsigned N>
+  LexOrCompleteWord &Case(const char (&S)[N], const T &Value,
+                          bool IsCompletion = true) {
+    StringRef CaseStr(S, N - 1);
+
+    if (WordCompletionPos == StringRef::npos)
+      Switch.Case(S, Value);
+    else if (N != 1 && IsCompletion && WordCompletionPos <= CaseStr.size() &&
+             CaseStr.substr(0, WordCompletionPos) ==
+                 Word.substr(0, WordCompletionPos))
+      P->Completions.push_back(LineEditor::Completion(
+          (CaseStr.substr(WordCompletionPos) + " ").str(), CaseStr));
+    return *this;
+  }
+
+  T Default(const T &Value) const { return Switch.Default(Value); }
+};
+
+// Lexes a word and stores it in Word. Returns a LexOrCompleteWord<T> object
+// that can be used like a llvm::StringSwitch<T>, but adds cases as possible
+// completions if the lexed word contains the completion point.
+template <typename T>
+QueryParser::LexOrCompleteWord<T>
+QueryParser::lexOrCompleteWord(StringRef &Word) {
+  Word = lexWord();
+  size_t WordCompletionPos = StringRef::npos;
+  if (CompletionPos && CompletionPos <= Word.data() + Word.size()) {
+    if (CompletionPos < Word.data())
+      WordCompletionPos = 0;
+    else
+      WordCompletionPos = CompletionPos - Word.data();
+  }
+  return LexOrCompleteWord<T>(this, Word, WordCompletionPos);
+}
+
+QueryRef QueryParser::parseSetBool(bool QuerySession::*Var) {
+  StringRef ValStr;
+  unsigned Value = lexOrCompleteWord<unsigned>(ValStr)
+                       .Case("false", 0)
+                       .Case("true", 1)
+                       .Default(~0u);
+  if (Value == ~0u) {
+    return new InvalidQuery("expected 'true' or 'false', got '" + ValStr + "'");
+  }
+  return new SetQuery<bool>(Var, Value);
+}
+
+QueryRef QueryParser::parseSetOutputKind() {
+  StringRef ValStr;
+  unsigned OutKind = lexOrCompleteWord<unsigned>(ValStr)
+                         .Case("diag", OK_Diag)
+                         .Case("print", OK_Print)
+                         .Case("dump", OK_Dump)
+                         .Default(~0u);
+  if (OutKind == ~0u) {
+    return new InvalidQuery("expected 'diag', 'print' or 'dump', got '" +
+                            ValStr + "'");
+  }
+  return new SetQuery<OutputKind>(&QuerySession::OutKind, OutputKind(OutKind));
+}
+
+QueryRef QueryParser::endQuery(QueryRef Q) {
+  const char *Extra = Begin;
+  if (!lexWord().empty())
+    return new InvalidQuery("unexpected extra input: '" +
+                            StringRef(Extra, End - Extra) + "'");
+  return Q;
+}
+
+namespace {
+
+enum ParsedQueryKind {
+  PQK_Invalid,
+  PQK_NoOp,
+  PQK_Help,
+  PQK_Let,
+  PQK_Match,
+  PQK_Set,
+  PQK_Unlet,
+  PQK_Quit
+};
+
+enum ParsedQueryVariable { PQV_Invalid, PQV_Output, PQV_BindRoot };
+
+QueryRef makeInvalidQueryFromDiagnostics(const Diagnostics &Diag) {
+  std::string ErrStr;
+  llvm::raw_string_ostream OS(ErrStr);
+  Diag.printToStreamFull(OS);
+  return new InvalidQuery(OS.str());
+}
+
+} // namespace
+
+QueryRef QueryParser::completeMatcherExpression() {
+  std::vector<MatcherCompletion> Comps = Parser::completeExpression(
+      StringRef(Begin, End - Begin), CompletionPos - Begin, nullptr,
+      &QS.NamedValues);
+  for (auto I = Comps.begin(), E = Comps.end(); I != E; ++I) {
+    Completions.push_back(LineEditor::Completion(I->TypedText, I->MatcherDecl));
+  }
+  return QueryRef();
+}
+
+QueryRef QueryParser::doParse() {
+  StringRef CommandStr;
+  ParsedQueryKind QKind = lexOrCompleteWord<ParsedQueryKind>(CommandStr)
+                              .Case("", PQK_NoOp)
+                              .Case("help", PQK_Help)
+                              .Case("m", PQK_Match, /*IsCompletion=*/false)
+                              .Case("let", PQK_Let)
+                              .Case("match", PQK_Match)
+                              .Case("set", PQK_Set)
+                              .Case("unlet", PQK_Unlet)
+                              .Case("quit", PQK_Quit)
+                              .Default(PQK_Invalid);
+
+  switch (QKind) {
+  case PQK_NoOp:
+    return new NoOpQuery;
+
+  case PQK_Help:
+    return endQuery(new HelpQuery);
+
+  case PQK_Quit:
+    return endQuery(new QuitQuery);
+
+  case PQK_Let: {
+    StringRef Name = lexWord();
+
+    if (Name.empty())
+      return new InvalidQuery("expected variable name");
+
+    if (CompletionPos)
+      return completeMatcherExpression();
+
+    Diagnostics Diag;
+    ast_matchers::dynamic::VariantValue Value;
+    if (!Parser::parseExpression(StringRef(Begin, End - Begin), nullptr,
+                                 &QS.NamedValues, &Value, &Diag)) {
+      return makeInvalidQueryFromDiagnostics(Diag);
+    }
+
+    return new LetQuery(Name, Value);
+  }
+
+  case PQK_Match: {
+    if (CompletionPos)
+      return completeMatcherExpression();
+
+    Diagnostics Diag;
+    Optional<DynTypedMatcher> Matcher = Parser::parseMatcherExpression(
+        StringRef(Begin, End - Begin), nullptr, &QS.NamedValues, &Diag);
+    if (!Matcher) {
+      return makeInvalidQueryFromDiagnostics(Diag);
+    }
+    return new MatchQuery(*Matcher);
+  }
+
+  case PQK_Set: {
+    StringRef VarStr;
+    ParsedQueryVariable Var = lexOrCompleteWord<ParsedQueryVariable>(VarStr)
+                                  .Case("output", PQV_Output)
+                                  .Case("bind-root", PQV_BindRoot)
+                                  .Default(PQV_Invalid);
+    if (VarStr.empty())
+      return new InvalidQuery("expected variable name");
+    if (Var == PQV_Invalid)
+      return new InvalidQuery("unknown variable: '" + VarStr + "'");
+
+    QueryRef Q;
+    switch (Var) {
+    case PQV_Output:
+      Q = parseSetOutputKind();
+      break;
+    case PQV_BindRoot:
+      Q = parseSetBool(&QuerySession::BindRoot);
+      break;
+    case PQV_Invalid:
+      llvm_unreachable("Invalid query kind");
+    }
+
+    return endQuery(Q);
+  }
+
+  case PQK_Unlet: {
+    StringRef Name = lexWord();
+
+    if (Name.empty())
+      return new InvalidQuery("expected variable name");
+
+    return endQuery(new LetQuery(Name, VariantValue()));
+  }
+
+  case PQK_Invalid:
+    return new InvalidQuery("unknown command: " + CommandStr);
+  }
+
+  llvm_unreachable("Invalid query kind");
+}
+
+QueryRef QueryParser::parse(StringRef Line, const QuerySession &QS) {
+  return QueryParser(Line, QS).doParse();
+}
+
+std::vector<LineEditor::Completion>
+QueryParser::complete(StringRef Line, size_t Pos, const QuerySession &QS) {
+  QueryParser P(Line, QS);
+  P.CompletionPos = Line.data() + Pos;
+
+  P.doParse();
+  return P.Completions;
+}
+
+} // namespace query
+} // namespace clang
diff --git a/clang-query/QueryParser.h b/clang-query/QueryParser.h
new file mode 100644 (file)
index 0000000..2059181
--- /dev/null
@@ -0,0 +1,71 @@
+//===--- QueryParser.h - clang-query ----------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_QUERY_QUERY_PARSER_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_QUERY_QUERY_PARSER_H
+
+#include "Query.h"
+#include "QuerySession.h"
+#include "llvm/LineEditor/LineEditor.h"
+#include <cstddef>
+
+namespace clang {
+namespace query {
+
+class QuerySession;
+
+class QueryParser {
+public:
+  /// Parse \a Line as a query.
+  ///
+  /// \return A QueryRef representing the query, which may be an InvalidQuery.
+  static QueryRef parse(StringRef Line, const QuerySession &QS);
+
+  /// Compute a list of completions for \a Line assuming a cursor at
+  /// \param Pos characters past the start of \a Line, ordered from most
+  /// likely to least likely.
+  ///
+  /// \return A vector of completions for \a Line.
+  static std::vector<llvm::LineEditor::Completion>
+  complete(StringRef Line, size_t Pos, const QuerySession &QS);
+
+private:
+  QueryParser(StringRef Line, const QuerySession &QS)
+      : Begin(Line.begin()), End(Line.end()), CompletionPos(nullptr), QS(QS) {}
+
+  StringRef lexWord();
+
+  template <typename T> struct LexOrCompleteWord;
+  template <typename T> LexOrCompleteWord<T> lexOrCompleteWord(StringRef &Str);
+
+  QueryRef parseSetBool(bool QuerySession::*Var);
+  QueryRef parseSetOutputKind();
+  QueryRef completeMatcherExpression();
+
+  QueryRef endQuery(QueryRef Q);
+
+  /// \brief Parse [\p Begin,\p End).
+  ///
+  /// \return A reference to the parsed query object, which may be an
+  /// \c InvalidQuery if a parse error occurs.
+  QueryRef doParse();
+
+  const char *Begin;
+  const char *End;
+
+  const char *CompletionPos;
+  std::vector<llvm::LineEditor::Completion> Completions;
+
+  const QuerySession &QS;
+};
+
+} // namespace query
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_QUERY_QUERY_PARSER_H
diff --git a/clang-query/QuerySession.h b/clang-query/QuerySession.h
new file mode 100644 (file)
index 0000000..162289f
--- /dev/null
@@ -0,0 +1,40 @@
+//===--- QuerySession.h - clang-query ---------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_QUERY_QUERY_SESSION_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_QUERY_QUERY_SESSION_H
+
+#include "Query.h"
+#include "clang/ASTMatchers/Dynamic/VariantValue.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringMap.h"
+
+namespace clang {
+
+class ASTUnit;
+
+namespace query {
+
+/// Represents the state for a particular clang-query session.
+class QuerySession {
+public:
+  QuerySession(llvm::ArrayRef<std::unique_ptr<ASTUnit>> ASTs)
+      : ASTs(ASTs), OutKind(OK_Diag), BindRoot(true), Terminate(false) {}
+
+  llvm::ArrayRef<std::unique_ptr<ASTUnit>> ASTs;
+  OutputKind OutKind;
+  bool BindRoot;
+  bool Terminate;
+  llvm::StringMap<ast_matchers::dynamic::VariantValue> NamedValues;
+};
+
+} // namespace query
+} // namespace clang
+
+#endif
diff --git a/clang-query/tool/CMakeLists.txt b/clang-query/tool/CMakeLists.txt
new file mode 100644 (file)
index 0000000..52af5c8
--- /dev/null
@@ -0,0 +1,14 @@
+include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..)
+
+add_clang_executable(clang-query ClangQuery.cpp)
+target_link_libraries(clang-query
+  clangAST
+  clangASTMatchers
+  clangBasic
+  clangDynamicASTMatchers
+  clangFrontend
+  clangQuery
+  clangTooling
+  )
+
+install(TARGETS clang-query RUNTIME DESTINATION bin)
diff --git a/clang-query/tool/ClangQuery.cpp b/clang-query/tool/ClangQuery.cpp
new file mode 100644 (file)
index 0000000..669dc40
--- /dev/null
@@ -0,0 +1,116 @@
+//===---- ClangQuery.cpp - clang-query tool -------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This tool is for interactive exploration of the Clang AST using AST matchers.
+// It currently allows the user to enter a matcher at an interactive prompt and
+// view the resulting bindings as diagnostics, AST pretty prints or AST dumps.
+// Example session:
+//
+// $ cat foo.c
+// void foo(void) {}
+// $ clang-query foo.c --
+// clang-query> match functionDecl()
+//
+// Match #1:
+//
+// foo.c:1:1: note: "root" binds here
+// void foo(void) {}
+// ^~~~~~~~~~~~~~~~~
+// 1 match.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Query.h"
+#include "QueryParser.h"
+#include "QuerySession.h"
+#include "clang/Frontend/ASTUnit.h"
+#include "clang/Tooling/CommonOptionsParser.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/LineEditor/LineEditor.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Signals.h"
+#include <fstream>
+#include <string>
+
+using namespace clang;
+using namespace clang::ast_matchers;
+using namespace clang::ast_matchers::dynamic;
+using namespace clang::query;
+using namespace clang::tooling;
+using namespace llvm;
+
+static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage);
+static cl::OptionCategory ClangQueryCategory("clang-query options");
+
+static cl::list<std::string> Commands("c", cl::desc("Specify command to run"),
+                                      cl::value_desc("command"),
+                                      cl::cat(ClangQueryCategory));
+
+static cl::list<std::string> CommandFiles("f",
+                                          cl::desc("Read commands from file"),
+                                          cl::value_desc("file"),
+                                          cl::cat(ClangQueryCategory));
+
+int main(int argc, const char **argv) {
+  llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
+
+  CommonOptionsParser OptionsParser(argc, argv, ClangQueryCategory);
+
+  if (!Commands.empty() && !CommandFiles.empty()) {
+    llvm::errs() << argv[0] << ": cannot specify both -c and -f\n";
+    return 1;
+  }
+
+  ClangTool Tool(OptionsParser.getCompilations(),
+                 OptionsParser.getSourcePathList());
+  std::vector<std::unique_ptr<ASTUnit>> ASTs;
+  if (Tool.buildASTs(ASTs) != 0)
+    return 1;
+
+  QuerySession QS(ASTs);
+
+  if (!Commands.empty()) {
+    for (auto I = Commands.begin(), E = Commands.end(); I != E; ++I) {
+      QueryRef Q = QueryParser::parse(*I, QS);
+      if (!Q->run(llvm::outs(), QS))
+        return 1;
+    }
+  } else if (!CommandFiles.empty()) {
+    for (auto I = CommandFiles.begin(), E = CommandFiles.end(); I != E; ++I) {
+      std::ifstream Input(I->c_str());
+      if (!Input.is_open()) {
+        llvm::errs() << argv[0] << ": cannot open " << *I << "\n";
+        return 1;
+      }
+      while (Input.good()) {
+        std::string Line;
+        std::getline(Input, Line);
+
+        QueryRef Q = QueryParser::parse(Line, QS);
+        if (!Q->run(llvm::outs(), QS))
+          return 1;
+      }
+    }
+  } else {
+    LineEditor LE("clang-query");
+    LE.setListCompleter([&QS](StringRef Line, size_t Pos) {
+      return QueryParser::complete(Line, Pos, QS);
+    });
+    while (llvm::Optional<std::string> Line = LE.readLine()) {
+      QueryRef Q = QueryParser::parse(*Line, QS);
+      Q->run(llvm::outs(), QS);
+      llvm::outs().flush();
+      if (QS.Terminate)
+        break;
+    }
+  }
+
+  return 0;
+}
diff --git a/clang-reorder-fields/CMakeLists.txt b/clang-reorder-fields/CMakeLists.txt
new file mode 100644 (file)
index 0000000..51cd4af
--- /dev/null
@@ -0,0 +1,15 @@
+set(LLVM_LINK_COMPONENTS support)
+
+add_clang_library(clangReorderFields
+  ReorderFieldsAction.cpp
+
+  LINK_LIBS
+  clangAST
+  clangASTMatchers
+  clangBasic
+  clangIndex
+  clangLex
+  clangToolingCore
+  )
+
+add_subdirectory(tool)
diff --git a/clang-reorder-fields/ReorderFieldsAction.cpp b/clang-reorder-fields/ReorderFieldsAction.cpp
new file mode 100644 (file)
index 0000000..1996380
--- /dev/null
@@ -0,0 +1,264 @@
+//===-- tools/extra/clang-reorder-fields/ReorderFieldsAction.cpp -*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains the definition of the
+/// ReorderFieldsAction::newASTConsumer method
+///
+//===----------------------------------------------------------------------===//
+
+#include "ReorderFieldsAction.h"
+#include "clang/AST/AST.h"
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Tooling/Refactoring.h"
+#include <algorithm>
+#include <string>
+
+namespace clang {
+namespace reorder_fields {
+using namespace clang::ast_matchers;
+
+/// \brief Finds the definition of a record by name.
+///
+/// \returns nullptr if the name is ambiguous or not found.
+static const CXXRecordDecl *findDefinition(StringRef RecordName,
+                                           ASTContext &Context) {
+  auto Results = match(
+      recordDecl(hasName(RecordName), isDefinition()).bind("cxxRecordDecl"),
+      Context);
+  if (Results.empty()) {
+    llvm::errs() << "Definition of " << RecordName << "  not found\n";
+    return nullptr;
+  }
+  if (Results.size() > 1) {
+    llvm::errs() << "The name " << RecordName
+                 << " is ambiguous, several definitions found\n";
+    return nullptr;
+  }
+  return selectFirst<CXXRecordDecl>("cxxRecordDecl", Results);
+}
+
+/// \brief Calculates the new order of fields.
+///
+/// \returns empty vector if the list of fields doesn't match the definition.
+static SmallVector<unsigned, 4>
+getNewFieldsOrder(const CXXRecordDecl *Definition,
+                  ArrayRef<std::string> DesiredFieldsOrder) {
+  assert(Definition && "Definition is null");
+
+  llvm::StringMap<unsigned> NameToIndex;
+  for (const auto *Field : Definition->fields())
+    NameToIndex[Field->getName()] = Field->getFieldIndex();
+
+  if (DesiredFieldsOrder.size() != NameToIndex.size()) {
+    llvm::errs() << "Number of provided fields doesn't match definition.\n";
+    return {};
+  }
+  SmallVector<unsigned, 4> NewFieldsOrder;
+  for (const auto &Name : DesiredFieldsOrder) {
+    if (!NameToIndex.count(Name)) {
+      llvm::errs() << "Field " << Name << " not found in definition.\n";
+      return {};
+    }
+    NewFieldsOrder.push_back(NameToIndex[Name]);
+  }
+  assert(NewFieldsOrder.size() == NameToIndex.size());
+  return NewFieldsOrder;
+}
+
+// FIXME: error-handling
+/// \brief Replaces one range of source code by another.
+static void
+addReplacement(SourceRange Old, SourceRange New, const ASTContext &Context,
+               std::map<std::string, tooling::Replacements> &Replacements) {
+  StringRef NewText =
+      Lexer::getSourceText(CharSourceRange::getTokenRange(New),
+                           Context.getSourceManager(), Context.getLangOpts());
+  tooling::Replacement R(Context.getSourceManager(),
+                         CharSourceRange::getTokenRange(Old), NewText,
+                         Context.getLangOpts());
+  consumeError(Replacements[R.getFilePath()].add(R));
+}
+
+/// \brief Reorders fields in the definition of a struct/class.
+///
+/// At the moment reodering of fields with
+/// different accesses (public/protected/private) is not supported.
+/// \returns true on success.
+static bool reorderFieldsInDefinition(
+    const CXXRecordDecl *Definition, ArrayRef<unsigned> NewFieldsOrder,
+    const ASTContext &Context,
+    std::map<std::string, tooling::Replacements> &Replacements) {
+  assert(Definition && "Definition is null");
+
+  SmallVector<const FieldDecl *, 10> Fields;
+  for (const auto *Field : Definition->fields())
+    Fields.push_back(Field);
+
+  // Check that the permutation of the fields doesn't change the accesses
+  for (const auto *Field : Definition->fields()) {
+    const auto FieldIndex = Field->getFieldIndex();
+    if (Field->getAccess() != Fields[NewFieldsOrder[FieldIndex]]->getAccess()) {
+      llvm::errs() << "Currently reodering of fields with different accesses "
+                      "is not supported\n";
+      return false;
+    }
+  }
+
+  for (const auto *Field : Definition->fields()) {
+    const auto FieldIndex = Field->getFieldIndex();
+    if (FieldIndex == NewFieldsOrder[FieldIndex])
+      continue;
+    addReplacement(Field->getSourceRange(),
+                   Fields[NewFieldsOrder[FieldIndex]]->getSourceRange(),
+                   Context, Replacements);
+  }
+  return true;
+}
+
+/// \brief Reorders initializers in a C++ struct/class constructor.
+///
+/// A constructor can have initializers for an arbitrary subset of the class's fields.
+/// Thus, we need to ensure that we reorder just the initializers that are present.
+static void reorderFieldsInConstructor(
+    const CXXConstructorDecl *CtorDecl, ArrayRef<unsigned> NewFieldsOrder,
+    const ASTContext &Context,
+    std::map<std::string, tooling::Replacements> &Replacements) {
+  assert(CtorDecl && "Constructor declaration is null");
+  if (CtorDecl->isImplicit() || CtorDecl->getNumCtorInitializers() <= 1)
+    return;
+
+  // The method FunctionDecl::isThisDeclarationADefinition returns false
+  // for a defaulted function unless that function has been implicitly defined.
+  // Thus this assert needs to be after the previous checks.
+  assert(CtorDecl->isThisDeclarationADefinition() && "Not a definition");
+
+  SmallVector<unsigned, 10> NewFieldsPositions(NewFieldsOrder.size());
+  for (unsigned i = 0, e = NewFieldsOrder.size(); i < e; ++i)
+    NewFieldsPositions[NewFieldsOrder[i]] = i;
+
+  SmallVector<const CXXCtorInitializer *, 10> OldWrittenInitializersOrder;
+  SmallVector<const CXXCtorInitializer *, 10> NewWrittenInitializersOrder;
+  for (const auto *Initializer : CtorDecl->inits()) {
+    if (!Initializer->isWritten())
+      continue;
+    OldWrittenInitializersOrder.push_back(Initializer);
+    NewWrittenInitializersOrder.push_back(Initializer);
+  }
+  auto ByFieldNewPosition = [&](const CXXCtorInitializer *LHS,
+                                const CXXCtorInitializer *RHS) {
+    assert(LHS && RHS);
+    return NewFieldsPositions[LHS->getMember()->getFieldIndex()] <
+           NewFieldsPositions[RHS->getMember()->getFieldIndex()];
+  };
+  std::sort(std::begin(NewWrittenInitializersOrder),
+            std::end(NewWrittenInitializersOrder), ByFieldNewPosition);
+  assert(OldWrittenInitializersOrder.size() ==
+         NewWrittenInitializersOrder.size());
+  for (unsigned i = 0, e = NewWrittenInitializersOrder.size(); i < e; ++i)
+    if (OldWrittenInitializersOrder[i] != NewWrittenInitializersOrder[i])
+      addReplacement(OldWrittenInitializersOrder[i]->getSourceRange(),
+                     NewWrittenInitializersOrder[i]->getSourceRange(), Context,
+                     Replacements);
+}
+
+/// \brief Reorders initializers in the brace initialization of an aggregate.
+///
+/// At the moment partial initialization is not supported.
+/// \returns true on success
+static bool reorderFieldsInInitListExpr(
+    const InitListExpr *InitListEx, ArrayRef<unsigned> NewFieldsOrder,
+    const ASTContext &Context,
+    std::map<std::string, tooling::Replacements> &Replacements) {
+  assert(InitListEx && "Init list expression is null");
+  // We care only about InitListExprs which originate from source code. 
+  // Implicit InitListExprs are created by the semantic analyzer.
+  if (!InitListEx->isExplicit())
+    return true;
+  // The method InitListExpr::getSyntacticForm may return nullptr indicating that
+  // the current initializer list also serves as its syntactic form.
+  if (const auto *SyntacticForm = InitListEx->getSyntacticForm())
+    InitListEx = SyntacticForm;
+  // If there are no initializers we do not need to change anything.
+  if (!InitListEx->getNumInits())
+    return true;
+  if (InitListEx->getNumInits() != NewFieldsOrder.size()) {
+    llvm::errs() << "Currently only full initialization is supported\n";
+    return false;
+  }
+  for (unsigned i = 0, e = InitListEx->getNumInits(); i < e; ++i)
+    if (i != NewFieldsOrder[i])
+      addReplacement(
+          InitListEx->getInit(i)->getSourceRange(),
+          InitListEx->getInit(NewFieldsOrder[i])->getSourceRange(), Context,
+          Replacements);
+  return true;
+}
+
+namespace {
+class ReorderingConsumer : public ASTConsumer {
+  StringRef RecordName;
+  ArrayRef<std::string> DesiredFieldsOrder;
+  std::map<std::string, tooling::Replacements> &Replacements;
+
+public:
+  ReorderingConsumer(StringRef RecordName,
+                     ArrayRef<std::string> DesiredFieldsOrder,
+                     std::map<std::string, tooling::Replacements> &Replacements)
+      : RecordName(RecordName), DesiredFieldsOrder(DesiredFieldsOrder),
+        Replacements(Replacements) {}
+
+  ReorderingConsumer(const ReorderingConsumer &) = delete;
+  ReorderingConsumer &operator=(const ReorderingConsumer &) = delete;
+
+  void HandleTranslationUnit(ASTContext &Context) override {
+    const CXXRecordDecl *RD = findDefinition(RecordName, Context);
+    if (!RD)
+      return;
+    SmallVector<unsigned, 4> NewFieldsOrder =
+        getNewFieldsOrder(RD, DesiredFieldsOrder);
+    if (NewFieldsOrder.empty())
+      return;
+    if (!reorderFieldsInDefinition(RD, NewFieldsOrder, Context, Replacements))
+      return;
+    for (const auto *C : RD->ctors())
+      if (const auto *D = dyn_cast<CXXConstructorDecl>(C->getDefinition()))
+        reorderFieldsInConstructor(cast<const CXXConstructorDecl>(D),
+                                   NewFieldsOrder, Context, Replacements);
+
+    // We only need to reorder init list expressions for aggregate types.
+    // For other types the order of constructor parameters is used,
+    // which we don't change at the moment.
+    // Now (v0) partial initialization is not supported.
+    if (RD->isAggregate())
+      for (auto Result :
+           match(initListExpr(hasType(equalsNode(RD))).bind("initListExpr"),
+                 Context))
+        if (!reorderFieldsInInitListExpr(
+                Result.getNodeAs<InitListExpr>("initListExpr"), NewFieldsOrder,
+                Context, Replacements)) {
+          Replacements.clear();
+          return;
+        }
+  }
+};
+} // end anonymous namespace
+
+std::unique_ptr<ASTConsumer> ReorderFieldsAction::newASTConsumer() {
+  return llvm::make_unique<ReorderingConsumer>(RecordName, DesiredFieldsOrder,
+                                               Replacements);
+}
+
+} // namespace reorder_fields
+} // namespace clang
diff --git a/clang-reorder-fields/ReorderFieldsAction.h b/clang-reorder-fields/ReorderFieldsAction.h
new file mode 100644 (file)
index 0000000..f08c632
--- /dev/null
@@ -0,0 +1,47 @@
+//===-- tools/extra/clang-reorder-fields/ReorderFieldsAction.h -*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains the declarations of the ReorderFieldsAction class and
+/// the FieldPosition struct.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_REORDER_FIELDS_ACTION_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_REORDER_FIELDS_ACTION_H
+
+#include "clang/Tooling/Refactoring.h"
+
+namespace clang {
+class ASTConsumer;
+
+namespace reorder_fields {
+
+class ReorderFieldsAction {
+  llvm::StringRef RecordName;
+  llvm::ArrayRef<std::string> DesiredFieldsOrder;
+  std::map<std::string, tooling::Replacements> &Replacements;
+
+public:
+  ReorderFieldsAction(
+      llvm::StringRef RecordName,
+      llvm::ArrayRef<std::string> DesiredFieldsOrder,
+      std::map<std::string, tooling::Replacements> &Replacements)
+      : RecordName(RecordName), DesiredFieldsOrder(DesiredFieldsOrder),
+        Replacements(Replacements) {}
+
+  ReorderFieldsAction(const ReorderFieldsAction &) = delete;
+  ReorderFieldsAction &operator=(const ReorderFieldsAction &) = delete;
+
+  std::unique_ptr<ASTConsumer> newASTConsumer();
+};
+} // namespace reorder_fields
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_REORDER_FIELDS_ACTION_H
diff --git a/clang-reorder-fields/tool/CMakeLists.txt b/clang-reorder-fields/tool/CMakeLists.txt
new file mode 100644 (file)
index 0000000..174e71a
--- /dev/null
@@ -0,0 +1,12 @@
+add_clang_executable(clang-reorder-fields ClangReorderFields.cpp)
+
+target_link_libraries(clang-reorder-fields
+  clangBasic
+  clangFrontend
+  clangReorderFields
+  clangRewrite
+  clangTooling
+  clangToolingCore
+  )
+
+install(TARGETS clang-reorder-fields RUNTIME DESTINATION bin)
diff --git a/clang-reorder-fields/tool/ClangReorderFields.cpp b/clang-reorder-fields/tool/ClangReorderFields.cpp
new file mode 100644 (file)
index 0000000..077e55e
--- /dev/null
@@ -0,0 +1,88 @@
+//===-- tools/extra/clang-reorder-fields/tool/ClangReorderFields.cpp -*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains the implementation of clang-reorder-fields tool
+///
+//===----------------------------------------------------------------------===//
+
+#include "../ReorderFieldsAction.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/DiagnosticOptions.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Frontend/TextDiagnosticPrinter.h"
+#include "clang/Rewrite/Core/Rewriter.h"
+#include "clang/Tooling/CommonOptionsParser.h"
+#include "clang/Tooling/Refactoring.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/IntrusiveRefCntPtr.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileSystem.h"
+#include <cstdlib>
+#include <string>
+#include <system_error>
+
+using namespace llvm;
+using namespace clang;
+
+cl::OptionCategory ClangReorderFieldsCategory("clang-reorder-fields options");
+
+static cl::opt<std::string>
+    RecordName("record-name", cl::Required,
+               cl::desc("The name of the struct/class."),
+               cl::cat(ClangReorderFieldsCategory));
+
+static cl::list<std::string> FieldsOrder("fields-order", cl::CommaSeparated,
+                                         cl::OneOrMore,
+                                         cl::desc("The desired fields order."),
+                                         cl::cat(ClangReorderFieldsCategory));
+
+static cl::opt<bool> Inplace("i", cl::desc("Overwrite edited files."),
+                             cl::cat(ClangReorderFieldsCategory));
+
+const char Usage[] = "A tool to reorder fields in C/C++ structs/classes.\n";
+
+int main(int argc, const char **argv) {
+  tooling::CommonOptionsParser OP(argc, argv, ClangReorderFieldsCategory,
+                                  Usage);
+
+  auto Files = OP.getSourcePathList();
+  tooling::RefactoringTool Tool(OP.getCompilations(), Files);
+
+  reorder_fields::ReorderFieldsAction Action(RecordName, FieldsOrder,
+                                             Tool.getReplacements());
+
+  auto Factory = tooling::newFrontendActionFactory(&Action);
+
+  if (Inplace)
+    return Tool.runAndSave(Factory.get());
+
+  int ExitCode = Tool.run(Factory.get());
+  LangOptions DefaultLangOptions;
+  IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(new DiagnosticOptions());
+  TextDiagnosticPrinter DiagnosticPrinter(errs(), &*DiagOpts);
+  DiagnosticsEngine Diagnostics(
+      IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), &*DiagOpts,
+      &DiagnosticPrinter, false);
+
+  auto &FileMgr = Tool.getFiles();
+  SourceManager Sources(Diagnostics, FileMgr);
+  Rewriter Rewrite(Sources, DefaultLangOptions);
+  Tool.applyAllReplacements(Rewrite);
+
+  for (const auto &File : Files) {
+    const auto *Entry = FileMgr.getFile(File);
+    const auto ID = Sources.getOrCreateFileID(Entry, SrcMgr::C_User);
+    Rewrite.getEditBuffer(ID).write(outs());
+  }
+
+  return ExitCode;
+}
diff --git a/clang-tidy-vs/.gitignore b/clang-tidy-vs/.gitignore
new file mode 100644 (file)
index 0000000..2b0f3e7
--- /dev/null
@@ -0,0 +1,7 @@
+obj/\r
+bin/\r
+.vs/\r
+Key.snk\r
+clang-tidy.exe\r
+packages/\r
+*.csproj.user\r
diff --git a/clang-tidy-vs/CMakeLists.txt b/clang-tidy-vs/CMakeLists.txt
new file mode 100644 (file)
index 0000000..96381bd
--- /dev/null
@@ -0,0 +1,28 @@
+option(BUILD_CLANG_TIDY_VS_PLUGIN "Build clang-tidy VS plugin" OFF)
+if (BUILD_CLANG_TIDY_VS_PLUGIN)
+  add_custom_target(clang_tidy_exe_for_vsix
+      ${CMAKE_COMMAND} -E copy_if_different
+      "${LLVM_TOOLS_BINARY_DIR}/clang-tidy.exe"
+      "${CMAKE_CURRENT_SOURCE_DIR}/ClangTidy/clang-tidy.exe"
+      DEPENDS clang-tidy)
+
+  add_custom_target(clang_tidy_license
+      ${CMAKE_COMMAND} -E copy_if_different
+      "${CLANG_SOURCE_DIR}/LICENSE.TXT"
+      "${CMAKE_CURRENT_SOURCE_DIR}/ClangTidy/license.txt")
+
+  if (NOT CLANG_TIDY_VS_VERSION)
+    set(CLANG_TIDY_VS_VERSION "${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR}.${LLVM_VERSION_PATCH}")
+  endif()
+
+  configure_file("source.extension.vsixmanifest.in"
+      "${CMAKE_CURRENT_SOURCE_DIR}/ClangTidy/source.extension.vsixmanifest")
+
+  add_custom_target(clang_tidy_vsix ALL
+      devenv "${CMAKE_CURRENT_SOURCE_DIR}/ClangTidy.sln" /Build Release
+      DEPENDS clang_tidy_exe_for_vsix "${CMAKE_CURRENT_SOURCE_DIR}/ClangTidy/source.extension.vsixmanifest"
+      COMMAND ${CMAKE_COMMAND} -E copy_if_different
+      "${CMAKE_CURRENT_SOURCE_DIR}/ClangTidy/bin/Release/ClangTidy.vsix"
+      "${LLVM_TOOLS_BINARY_DIR}/ClangTidy.vsix"
+      DEPENDS clang_tidy_exe_for_vsix clang_tidy_license)
+endif()
diff --git a/clang-tidy-vs/ClangTidy.sln b/clang-tidy-vs/ClangTidy.sln
new file mode 100644 (file)
index 0000000..345eb83
--- /dev/null
@@ -0,0 +1,22 @@
+\r
+Microsoft Visual Studio Solution File, Format Version 12.00\r
+# Visual Studio 14\r
+VisualStudioVersion = 14.0.25123.0\r
+MinimumVisualStudioVersion = 10.0.40219.1\r
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClangTidy", "ClangTidy\ClangTidy.csproj", "{BE261DA1-36C6-449A-95C5-4653A549170A}"\r
+EndProject\r
+Global\r
+       GlobalSection(SolutionConfigurationPlatforms) = preSolution\r
+               Debug|Any CPU = Debug|Any CPU\r
+               Release|Any CPU = Release|Any CPU\r
+       EndGlobalSection\r
+       GlobalSection(ProjectConfigurationPlatforms) = postSolution\r
+               {BE261DA1-36C6-449A-95C5-4653A549170A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\r
+               {BE261DA1-36C6-449A-95C5-4653A549170A}.Debug|Any CPU.Build.0 = Debug|Any CPU\r
+               {BE261DA1-36C6-449A-95C5-4653A549170A}.Release|Any CPU.ActiveCfg = Release|Any CPU\r
+               {BE261DA1-36C6-449A-95C5-4653A549170A}.Release|Any CPU.Build.0 = Release|Any CPU\r
+       EndGlobalSection\r
+       GlobalSection(SolutionProperties) = preSolution\r
+               HideSolutionNode = FALSE\r
+       EndGlobalSection\r
+EndGlobal\r
diff --git a/clang-tidy-vs/ClangTidy/CategoryVerb.cs b/clang-tidy-vs/ClangTidy/CategoryVerb.cs
new file mode 100644 (file)
index 0000000..ef07a89
--- /dev/null
@@ -0,0 +1,70 @@
+using System;\r
+using System.Collections.Generic;\r
+using System.ComponentModel;\r
+using System.Globalization;\r
+using System.Linq;\r
+using System.Text;\r
+using System.Threading.Tasks;\r
+\r
+namespace LLVM.ClangTidy\r
+{\r
+    /// <summary>\r
+    /// Allows entire categories of properties to be enabled, disabled, or inherited\r
+    /// in one fell swoop.  We add properties to each category with the value being\r
+    /// this enum, and when the value is selected, we use reflection to find all other\r
+    /// properties in the same category and perform the corresponding action.\r
+    /// </summary>\r
+    public enum CategoryVerb\r
+    {\r
+        None,\r
+        Disable,\r
+        Enable,\r
+        Inherit\r
+    }\r
+\r
+    public class CategoryVerbConverter : EnumConverter\r
+    {\r
+        public CategoryVerbConverter() : base(typeof(CategoryVerb))\r
+        {\r
+        }\r
+\r
+        public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)\r
+        {\r
+            if (value is string)\r
+            {\r
+                switch ((string)value)\r
+                {\r
+                    case "Disable Category":\r
+                        return CategoryVerb.Disable;\r
+                    case "Enable Category":\r
+                        return CategoryVerb.Enable;\r
+                    case "Inherit Category":\r
+                        return CategoryVerb.Inherit;\r
+                    case "":\r
+                        return CategoryVerb.None;\r
+                }\r
+            }\r
+            return base.ConvertFrom(context, culture, value);\r
+        }\r
+\r
+        public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)\r
+        {\r
+            if (value is CategoryVerb && destinationType == typeof(string))\r
+            {\r
+                switch ((CategoryVerb)value)\r
+                {\r
+                    case CategoryVerb.Disable:\r
+                        return "Disable Category";\r
+                    case CategoryVerb.Enable:\r
+                        return "Enable Category";\r
+                    case CategoryVerb.Inherit:\r
+                        return "Inherit Category";\r
+                    case CategoryVerb.None:\r
+                        return String.Empty;\r
+                }\r
+            }\r
+\r
+            return base.ConvertTo(context, culture, value, destinationType);\r
+        }\r
+    }\r
+}\r
diff --git a/clang-tidy-vs/ClangTidy/CheckDatabase.cs b/clang-tidy-vs/ClangTidy/CheckDatabase.cs
new file mode 100644 (file)
index 0000000..6b76688
--- /dev/null
@@ -0,0 +1,67 @@
+using System;\r
+using System.Collections.Generic;\r
+using System.IO;\r
+using System.Linq;\r
+using System.Text;\r
+using YamlDotNet.Serialization;\r
+using YamlDotNet.Serialization.NamingConventions;\r
+\r
+namespace LLVM.ClangTidy\r
+{\r
+    public class CheckInfo\r
+    {\r
+        [YamlAlias("Name")]\r
+        public string Name { get; set; }\r
+\r
+        [YamlAlias("Label")]\r
+        public string Label { get; set; }\r
+\r
+        [YamlAlias("Description")]\r
+        public string Desc { get; set; }\r
+\r
+        [YamlAlias("Category")]\r
+        public string Category { get; set; }\r
+    }\r
+\r
+    /// <summary>\r
+    /// Reads the list of checks from Yaml and builds a description of each one.\r
+    /// This list of checks is then used by the PropertyGrid to determine what\r
+    /// items to display.\r
+    /// </summary>\r
+    public static class CheckDatabase\r
+    {\r
+        static CheckInfo[] Checks_ = null;\r
+\r
+        class CheckRoot\r
+        {\r
+            [YamlAlias("Checks")]\r
+            public CheckInfo[] Checks { get; set; }\r
+        }\r
+\r
+        static CheckDatabase()\r
+        {\r
+            using (StringReader Reader = new StringReader(Resources.ClangTidyChecks))\r
+            {\r
+                Deserializer D = new Deserializer(namingConvention: new PascalCaseNamingConvention());\r
+                var Root = D.Deserialize<CheckRoot>(Reader);\r
+                Checks_ = Root.Checks;\r
+\r
+                HashSet<string> Names = new HashSet<string>();\r
+                foreach (var Check in Checks_)\r
+                {\r
+                    if (Names.Contains(Check.Name))\r
+                        continue;\r
+                    Names.Add(Check.Name);\r
+                }\r
+            }\r
+        }\r
+\r
+        public static IEnumerable<CheckInfo> Checks\r
+        {\r
+            get\r
+            {\r
+                return Checks_;\r
+            }\r
+        }\r
+    }\r
+}\r
diff --git a/clang-tidy-vs/ClangTidy/CheckTree.cs b/clang-tidy-vs/ClangTidy/CheckTree.cs
new file mode 100644 (file)
index 0000000..f3e2583
--- /dev/null
@@ -0,0 +1,273 @@
+using System;\r
+using System.Collections.Generic;\r
+using System.ComponentModel;\r
+using System.Linq;\r
+using System.Text;\r
+\r
+namespace LLVM.ClangTidy\r
+{\r
+    /// <summary>\r
+    /// CheckTree is used to group checks into categories and subcategories.  For\r
+    /// example, given the following list of checks:\r
+    /// \r
+    ///   llvm-include-order\r
+    ///   llvm-namespace-comment\r
+    ///   llvm-twine-local\r
+    ///   llvm-header-guard\r
+    ///   google-runtime-member-string-references\r
+    ///   google-runtime-int\r
+    ///   google-readability-namespace-comments\r
+    ///   \r
+    /// the corresponding CheckTree would look like this:\r
+    /// \r
+    ///   llvm\r
+    ///     include-order\r
+    ///     namespace-comment\r
+    ///     twine-local\r
+    ///     header-guard\r
+    ///   google\r
+    ///     runtime\r
+    ///       member-string-references\r
+    ///       int\r
+    ///     readability\r
+    ///       namespace-comments\r
+    ///       redundant-smartptr-get\r
+    ///       \r
+    /// This is useful when serializing a set of options out to a .clang-tidy file,\r
+    /// because we need to decide the most efficient way to serialize the sequence\r
+    /// of check commands, when to use wildcards, etc.  For example, if everything\r
+    /// under google is inherited, we can simply leave that entry out entirely from\r
+    /// the .clang-tidy file.  On the other hand, if anything is inherited, we *must\r
+    /// not* add or remove google-* by wildcard because that, by definition, means\r
+    /// the property is no longer inherited.  When we can categorize the checks into\r
+    /// groups and subgroups like this, it is possible to efficiently serialize to\r
+    /// a minimal representative .clang-tidy file.\r
+    /// </summary>\r
+\r
+    public abstract class CheckTreeNode\r
+    {\r
+        private string Name_;\r
+        private CheckTreeNode Parent_;\r
+\r
+        protected CheckTreeNode(string Name, CheckTreeNode Parent)\r
+        {\r
+            Name_ = Name;\r
+            Parent_ = Parent;\r
+        }\r
+\r
+        public string Path\r
+        {\r
+            get\r
+            {\r
+                if (Parent_ == null)\r
+                    return null;\r
+                string ParentPath = Parent_.Path;\r
+                if (ParentPath == null)\r
+                    return Name_;\r
+                return ParentPath + "-" + Name_;\r
+            }\r
+        }\r
+\r
+        public string Name\r
+        {\r
+            get\r
+            {\r
+                return Name_;\r
+            }\r
+        }\r
+\r
+\r
+        public abstract int CountChecks { get; }\r
+        public abstract int CountExplicitlyDisabledChecks { get; }\r
+        public abstract int CountExplicitlyEnabledChecks { get; }\r
+        public abstract int CountInheritedChecks { get; }\r
+    }\r
+\r
+    public class CheckTree : CheckTreeNode\r
+    {\r
+        private Dictionary<string, CheckTreeNode> Children_ = new Dictionary<string, CheckTreeNode>();\r
+        public CheckTree()\r
+            : base(null, null)\r
+        {\r
+\r
+        }\r
+\r
+        private CheckTree(string Name, CheckTree Parent)\r
+            : base(Name, Parent)\r
+        {\r
+        }\r
+\r
+        private void AddLeaf(string Name, DynamicPropertyDescriptor<bool> Property)\r
+        {\r
+            Children_[Name] = new CheckLeaf(Name, this, Property);\r
+        }\r
+\r
+        private CheckTree AddOrCreateSubgroup(string Name)\r
+        {\r
+            CheckTreeNode Subgroup = null;\r
+            if (Children_.TryGetValue(Name, out Subgroup))\r
+            {\r
+                System.Diagnostics.Debug.Assert(Subgroup is CheckTree);\r
+                return (CheckTree)Subgroup;\r
+            }\r
+\r
+            CheckTree SG = new CheckTree(Name, this);\r
+            Children_[Name] = SG;\r
+            return SG;\r
+        }\r
+\r
+        public static CheckTree Build(ClangTidyProperties Config)\r
+        {\r
+            // Since some check names contain dashes in them, it doesn't make sense to\r
+            // simply split all check names by dash and construct a huge tree.  For\r
+            // example, in the check called google-runtime-member-string-references,\r
+            // we don't need each of those to be a different subgroup.  So instead we\r
+            // explicitly specify the common breaking points at which a user might want\r
+            // to use a -* and everything else falls as a leaf under one of these\r
+            // categories.\r
+            // FIXME: This should be configurable without recompilation\r
+            CheckTree Root = new CheckTree();\r
+            string[][] Groups = new string[][] {\r
+                new string[] {"boost"},\r
+                new string[] {"cert"},\r
+                new string[] {"clang", "diagnostic"},\r
+                new string[] {"cppcoreguidelines", "interfaces"},\r
+                new string[] {"cppcoreguidelines", "pro", "bounds"},\r
+                new string[] {"cppcoreguidelines", "pro", "type"},\r
+                new string[] {"google", "build"},\r
+                new string[] {"google", "readability"},\r
+                new string[] {"google", "runtime"},\r
+                new string[] {"llvm"},\r
+                new string[] {"misc"},\r
+            };\r
+\r
+            foreach (string[] Group in Groups)\r
+            {\r
+                CheckTree Subgroup = Root;\r
+                foreach (string Component in Group)\r
+                    Subgroup = Subgroup.AddOrCreateSubgroup(Component);\r
+            }\r
+\r
+            var Props = Config.GetProperties()\r
+                              .Cast<PropertyDescriptor>()\r
+                              .OfType<DynamicPropertyDescriptor<bool>>()\r
+                              .Where(x => x.Attributes.OfType<ClangTidyCheckAttribute>().Count() > 0)\r
+                              .Select(x => new KeyValuePair<DynamicPropertyDescriptor<bool>, string>(\r
+                                            x, x.Attributes.OfType<ClangTidyCheckAttribute>().First().CheckName));\r
+            var PropArray = Props.ToArray();\r
+            foreach (var CheckInfo in PropArray)\r
+            {\r
+                string LeafName = null;\r
+                CheckTree Tree = Root.LocateCheckLeafGroup(CheckInfo.Value, out LeafName);\r
+                Tree.AddLeaf(LeafName, CheckInfo.Key);\r
+            }\r
+            return Root;\r
+        }\r
+\r
+        private CheckTree LocateCheckLeafGroup(string Check, out string LeafName)\r
+        {\r
+            string[] Components = Check.Split('-');\r
+            string FirstComponent = Components.FirstOrDefault();\r
+            if (FirstComponent == null)\r
+            {\r
+                LeafName = Check;\r
+                return this;\r
+            }\r
+\r
+            CheckTreeNode Subgroup = null;\r
+            if (!Children_.TryGetValue(FirstComponent, out Subgroup))\r
+            {\r
+                LeafName = Check;\r
+                return this;\r
+            }\r
+            System.Diagnostics.Debug.Assert(Subgroup is CheckTree);\r
+            CheckTree Child = (CheckTree)Subgroup;\r
+            string ChildName = Check.Substring(FirstComponent.Length + 1);\r
+            return Child.LocateCheckLeafGroup(ChildName, out LeafName);\r
+        }\r
+\r
+        public override int CountChecks\r
+        {\r
+            get\r
+            {\r
+                return Children_.Aggregate(0, (X, V) => { return X + V.Value.CountChecks; });\r
+            }\r
+        }\r
+\r
+        public override int CountExplicitlyDisabledChecks\r
+        {\r
+            get\r
+            {\r
+                return Children_.Aggregate(0, (X, V) => { return X + V.Value.CountExplicitlyDisabledChecks; });\r
+            }\r
+        }\r
+\r
+        public override int CountExplicitlyEnabledChecks\r
+        {\r
+            get\r
+            {\r
+                return Children_.Aggregate(0, (X, V) => { return X + V.Value.CountExplicitlyEnabledChecks; });\r
+            }\r
+        }\r
+        public override int CountInheritedChecks\r
+        {\r
+            get\r
+            {\r
+                return Children_.Aggregate(0, (X, V) => { return X + V.Value.CountInheritedChecks; });\r
+            }\r
+        }\r
+\r
+        public IDictionary<string, CheckTreeNode> Children\r
+        {\r
+            get { return Children_; }\r
+        }\r
+    }\r
+\r
+    public class CheckLeaf : CheckTreeNode\r
+    {\r
+        private DynamicPropertyDescriptor<bool> Property_;\r
+\r
+        public CheckLeaf(string Name, CheckTree Parent, DynamicPropertyDescriptor<bool> Property)\r
+            : base(Name, Parent)\r
+        {\r
+            Property_ = Property;\r
+        }\r
+\r
+        public override int CountChecks\r
+        {\r
+            get\r
+            {\r
+                return 1;\r
+            }\r
+        }\r
+\r
+        public override int CountExplicitlyDisabledChecks\r
+        {\r
+            get\r
+            {\r
+                if (Property_.IsInheriting)\r
+                    return 0;\r
+                return (bool)Property_.GetValue(null) ? 0 : 1;\r
+            }\r
+        }\r
+\r
+        public override int CountExplicitlyEnabledChecks\r
+        {\r
+            get\r
+            {\r
+                if (Property_.IsInheriting)\r
+                    return 0;\r
+                return (bool)Property_.GetValue(null) ? 1 : 0;\r
+            }\r
+        }\r
+\r
+        public override int CountInheritedChecks\r
+        {\r
+            get\r
+            {\r
+                return (Property_.IsInheriting) ? 1 : 0;\r
+            }\r
+        }\r
+\r
+    }\r
+}\r
diff --git a/clang-tidy-vs/ClangTidy/ClangTidy.csproj b/clang-tidy-vs/ClangTidy/ClangTidy.csproj
new file mode 100644 (file)
index 0000000..bf74717
--- /dev/null
@@ -0,0 +1,267 @@
+<?xml version="1.0" encoding="utf-8"?>\r
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">\r
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />\r
+  <PropertyGroup>\r
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>\r
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>\r
+    <SchemaVersion>2.0</SchemaVersion>\r
+    <ProjectGuid>{BE261DA1-36C6-449A-95C5-4653A549170A}</ProjectGuid>\r
+    <ProjectTypeGuids>{82b43b9b-a64c-4715-b499-d71e9ca2bd60};{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>\r
+    <OutputType>Library</OutputType>\r
+    <AppDesignerFolder>Properties</AppDesignerFolder>\r
+    <RootNamespace>LLVM.ClangTidy</RootNamespace>\r
+    <AssemblyName>ClangTidy</AssemblyName>\r
+    <SignAssembly>true</SignAssembly>\r
+    <AssemblyOriginatorKeyFile>Key.snk</AssemblyOriginatorKeyFile>\r
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>\r
+    <MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>\r
+    <FileUpgradeFlags>\r
+    </FileUpgradeFlags>\r
+    <UpgradeBackupLocation>\r
+    </UpgradeBackupLocation>\r
+    <OldToolsVersion>4.0</OldToolsVersion>\r
+    <PublishUrl>publish\</PublishUrl>\r
+    <Install>true</Install>\r
+    <InstallFrom>Disk</InstallFrom>\r
+    <UpdateEnabled>false</UpdateEnabled>\r
+    <UpdateMode>Foreground</UpdateMode>\r
+    <UpdateInterval>7</UpdateInterval>\r
+    <UpdateIntervalUnits>Days</UpdateIntervalUnits>\r
+    <UpdatePeriodically>false</UpdatePeriodically>\r
+    <UpdateRequired>false</UpdateRequired>\r
+    <MapFileExtensions>true</MapFileExtensions>\r
+    <ApplicationRevision>0</ApplicationRevision>\r
+    <ApplicationVersion>1.0.0.%2a</ApplicationVersion>\r
+    <IsWebBootstrapper>false</IsWebBootstrapper>\r
+    <UseApplicationTrust>false</UseApplicationTrust>\r
+    <BootstrapperEnabled>true</BootstrapperEnabled>\r
+    <TargetFrameworkProfile />\r
+  </PropertyGroup>\r
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">\r
+    <DebugSymbols>true</DebugSymbols>\r
+    <DebugType>full</DebugType>\r
+    <Optimize>false</Optimize>\r
+    <OutputPath>bin\Debug\</OutputPath>\r
+    <DefineConstants>DEBUG;TRACE</DefineConstants>\r
+    <ErrorReport>prompt</ErrorReport>\r
+    <WarningLevel>0</WarningLevel>\r
+    <Prefer32Bit>false</Prefer32Bit>\r
+    <PlatformTarget>AnyCPU</PlatformTarget>\r
+  </PropertyGroup>\r
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">\r
+    <DebugType>pdbonly</DebugType>\r
+    <Optimize>true</Optimize>\r
+    <OutputPath>bin\Release\</OutputPath>\r
+    <DefineConstants>TRACE</DefineConstants>\r
+    <ErrorReport>prompt</ErrorReport>\r
+    <WarningLevel>4</WarningLevel>\r
+    <RunCodeAnalysis>true</RunCodeAnalysis>\r
+    <Prefer32Bit>false</Prefer32Bit>\r
+  </PropertyGroup>\r
+  <ItemGroup>\r
+    <Reference Include="Microsoft.CSharp" />\r
+    <Reference Include="Microsoft.VisualStudio.CoreUtility, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />\r
+    <Reference Include="Microsoft.VisualStudio.Editor, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />\r
+    <Reference Include="Microsoft.VisualStudio.OLE.Interop" />\r
+    <Reference Include="Microsoft.VisualStudio.Settings.14.0, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=x86" />\r
+    <Reference Include="Microsoft.VisualStudio.Shell.14.0, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />\r
+    <Reference Include="Microsoft.VisualStudio.Shell.Immutable.10.0, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />\r
+    <Reference Include="Microsoft.VisualStudio.Shell.Immutable.14.0, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />\r
+    <Reference Include="Microsoft.VisualStudio.Shell.Interop" />\r
+    <Reference Include="Microsoft.VisualStudio.TextManager.Interop" />\r
+    <Reference Include="Microsoft.VisualStudio.TextManager.Interop" />\r
+    <Reference Include="Microsoft.VisualStudio.Utilities, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />\r
+    <Reference Include="System" />\r
+    <Reference Include="System.Core" />\r
+    <Reference Include="System.Data" />\r
+    <Reference Include="System.Design" />\r
+    <Reference Include="System.Drawing" />\r
+    <Reference Include="System.Windows.Forms" />\r
+    <Reference Include="System.Xml" />\r
+    <Reference Include="System.Xml.Linq" />\r
+    <Reference Include="YamlDotNet, Version=3.3.0.0, Culture=neutral, processorArchitecture=MSIL">\r
+      <HintPath>..\packages\YamlDotNet.3.3.0\lib\net35\YamlDotNet.dll</HintPath>\r
+      <Private>True</Private>\r
+    </Reference>\r
+    <Reference Include="YamlDotNet.Dynamic, Version=3.2.0.0, Culture=neutral, processorArchitecture=MSIL">\r
+      <HintPath>..\packages\YamlDotNet.Dynamic.3.2.3\lib\net40\YamlDotNet.Dynamic.dll</HintPath>\r
+      <Private>True</Private>\r
+    </Reference>\r
+  </ItemGroup>\r
+  <ItemGroup>\r
+    <COMReference Include="EnvDTE">\r
+      <Guid>{80CC9F66-E7D8-4DDD-85B6-D9E6CD0E93E2}</Guid>\r
+      <VersionMajor>8</VersionMajor>\r
+      <VersionMinor>0</VersionMinor>\r
+      <Lcid>0</Lcid>\r
+      <WrapperTool>primary</WrapperTool>\r
+      <Isolated>False</Isolated>\r
+      <EmbedInteropTypes>False</EmbedInteropTypes>\r
+    </COMReference>\r
+    <COMReference Include="EnvDTE100">\r
+      <Guid>{26AD1324-4B7C-44BC-84F8-B86AED45729F}</Guid>\r
+      <VersionMajor>10</VersionMajor>\r
+      <VersionMinor>0</VersionMinor>\r
+      <Lcid>0</Lcid>\r
+      <WrapperTool>primary</WrapperTool>\r
+      <Isolated>False</Isolated>\r
+      <EmbedInteropTypes>False</EmbedInteropTypes>\r
+    </COMReference>\r
+    <COMReference Include="EnvDTE80">\r
+      <Guid>{1A31287A-4D7D-413E-8E32-3B374931BD89}</Guid>\r
+      <VersionMajor>8</VersionMajor>\r
+      <VersionMinor>0</VersionMinor>\r
+      <Lcid>0</Lcid>\r
+      <WrapperTool>primary</WrapperTool>\r
+      <Isolated>False</Isolated>\r
+      <EmbedInteropTypes>False</EmbedInteropTypes>\r
+    </COMReference>\r
+    <COMReference Include="EnvDTE90">\r
+      <Guid>{2CE2370E-D744-4936-A090-3FFFE667B0E1}</Guid>\r
+      <VersionMajor>9</VersionMajor>\r
+      <VersionMinor>0</VersionMinor>\r
+      <Lcid>0</Lcid>\r
+      <WrapperTool>primary</WrapperTool>\r
+      <Isolated>False</Isolated>\r
+      <EmbedInteropTypes>False</EmbedInteropTypes>\r
+    </COMReference>\r
+    <COMReference Include="Microsoft.VisualStudio.CommandBars">\r
+      <Guid>{1CBA492E-7263-47BB-87FE-639000619B15}</Guid>\r
+      <VersionMajor>8</VersionMajor>\r
+      <VersionMinor>0</VersionMinor>\r
+      <Lcid>0</Lcid>\r
+      <WrapperTool>primary</WrapperTool>\r
+      <Isolated>False</Isolated>\r
+      <EmbedInteropTypes>False</EmbedInteropTypes>\r
+    </COMReference>\r
+    <COMReference Include="stdole">\r
+      <Guid>{00020430-0000-0000-C000-000000000046}</Guid>\r
+      <VersionMajor>2</VersionMajor>\r
+      <VersionMinor>0</VersionMinor>\r
+      <Lcid>0</Lcid>\r
+      <WrapperTool>primary</WrapperTool>\r
+      <Isolated>False</Isolated>\r
+      <EmbedInteropTypes>False</EmbedInteropTypes>\r
+    </COMReference>\r
+  </ItemGroup>\r
+  <ItemGroup>\r
+    <Compile Include="CategoryVerb.cs" />\r
+    <Compile Include="CheckDatabase.cs" />\r
+    <Compile Include="CheckTree.cs" />\r
+    <Compile Include="ClangTidyCheckAttribute.cs" />\r
+    <Compile Include="ClangTidyConfigurationPage.cs">\r
+      <SubType>Component</SubType>\r
+    </Compile>\r
+    <Compile Include="ClangTidyProperties.cs">\r
+      <SubType>Component</SubType>\r
+    </Compile>\r
+    <Compile Include="DynamicPropertyConverter.cs" />\r
+    <Compile Include="DynamicPropertyDescriptor.cs" />\r
+    <Compile Include="ForwardingPropertyDescriptor.cs" />\r
+    <Compile Include="Guids.cs" />\r
+    <Compile Include="DynamicPropertyComponent.cs">\r
+      <SubType>Component</SubType>\r
+    </Compile>\r
+    <Compile Include="DynamicPropertyComponent.Designer.cs" />\r
+    <Compile Include="ClangTidyConfigParser.cs" />\r
+    <Compile Include="Resources.Designer.cs">\r
+      <AutoGen>True</AutoGen>\r
+      <DesignTime>True</DesignTime>\r
+      <DependentUpon>Resources.resx</DependentUpon>\r
+    </Compile>\r
+    <Compile Include="GlobalSuppressions.cs" />\r
+    <Compile Include="ClangTidyPackage.cs" />\r
+    <Compile Include="Properties\AssemblyInfo.cs" />\r
+    <Compile Include="PkgCmdID.cs" />\r
+    <Compile Include="ClangTidyPropertyGrid.cs">\r
+      <SubType>UserControl</SubType>\r
+    </Compile>\r
+    <Compile Include="ClangTidyPropertyGrid.Designer.cs">\r
+      <DependentUpon>ClangTidyPropertyGrid.cs</DependentUpon>\r
+    </Compile>\r
+    <Compile Include="Utility.cs" />\r
+  </ItemGroup>\r
+  <ItemGroup>\r
+    <EmbeddedResource Include="Resources.resx">\r
+      <Generator>ResXFileCodeGenerator</Generator>\r
+      <LastGenOutput>Resources.Designer.cs</LastGenOutput>\r
+      <SubType>Designer</SubType>\r
+    </EmbeddedResource>\r
+    <EmbeddedResource Include="ClangTidyPropertyGrid.resx">\r
+      <DependentUpon>ClangTidyPropertyGrid.cs</DependentUpon>\r
+    </EmbeddedResource>\r
+    <EmbeddedResource Include="VSPackage.resx">\r
+      <MergeWithCTO>true</MergeWithCTO>\r
+      <ManifestResourceName>VSPackage</ManifestResourceName>\r
+    </EmbeddedResource>\r
+  </ItemGroup>\r
+  <ItemGroup>\r
+    <None Include="Key.snk" />\r
+    <None Include="packages.config" />\r
+    <None Include="Resources\ClangTidyChecks.yaml" />\r
+    <None Include="source.extension.vsixmanifest">\r
+      <SubType>Designer</SubType>\r
+    </None>\r
+  </ItemGroup>\r
+  <ItemGroup>\r
+    <VSCTCompile Include="ClangTidy.vsct">\r
+      <ResourceName>Menus.ctmenu</ResourceName>\r
+      <SubType>Designer</SubType>\r
+    </VSCTCompile>\r
+  </ItemGroup>\r
+  <ItemGroup>\r
+    <None Include="Resources\Images_32bit.bmp" />\r
+  </ItemGroup>\r
+  <ItemGroup>\r
+    <Content Include="clang-tidy.exe">\r
+      <IncludeInVSIX>true</IncludeInVSIX>\r
+    </Content>\r
+    <Content Include="license.txt">\r
+      <IncludeInVSIX>true</IncludeInVSIX>\r
+    </Content>\r
+    <Content Include="Resources\Package.ico" />\r
+  </ItemGroup>\r
+  <ItemGroup>\r
+    <BootstrapperPackage Include=".NETFramework,Version=v4.0">\r
+      <Visible>False</Visible>\r
+      <ProductName>Microsoft .NET Framework 4 %28x86 and x64%29</ProductName>\r
+      <Install>true</Install>\r
+    </BootstrapperPackage>\r
+    <BootstrapperPackage Include="Microsoft.Net.Client.3.5">\r
+      <Visible>False</Visible>\r
+      <ProductName>.NET Framework 3.5 SP1 Client Profile</ProductName>\r
+      <Install>false</Install>\r
+    </BootstrapperPackage>\r
+    <BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">\r
+      <Visible>False</Visible>\r
+      <ProductName>.NET Framework 3.5 SP1</ProductName>\r
+      <Install>false</Install>\r
+    </BootstrapperPackage>\r
+    <BootstrapperPackage Include="Microsoft.Windows.Installer.4.5">\r
+      <Visible>False</Visible>\r
+      <ProductName>Windows Installer 4.5</ProductName>\r
+      <Install>true</Install>\r
+    </BootstrapperPackage>\r
+  </ItemGroup>\r
+  <PropertyGroup>\r
+    <UseCodebase>true</UseCodebase>\r
+  </PropertyGroup>\r
+  <PropertyGroup>\r
+    <VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>\r
+    <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>\r
+  </PropertyGroup>\r
+  <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />\r
+  <Import Project="$(VSToolsPath)\VSSDK\Microsoft.VsSDK.targets" Condition="'$(VSToolsPath)' != ''" />\r
+  <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\VSSDK\Microsoft.VsSDK.targets" Condition="false" />\r
+  <PropertyGroup>\r
+    <PreBuildEvent>if not exist $(ProjectDir)Key.snk ("$(SDKToolsPath)\sn.exe" -k $(ProjectDir)Key.snk)</PreBuildEvent>\r
+  </PropertyGroup>\r
+  <Target Name="BeforeBuild">\r
+    <Exec ContinueOnError="false" Command="&quot;..\packages\Brutal.Dev.StrongNameSigner.1.8.0\tools\StrongNameSigner.Console.exe&quot; -in &quot;..\packages&quot; -l Summary" />\r
+  </Target>\r
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. \r
+       Other similar extension points exist, see Microsoft.Common.targets.\r
+  <Target Name="AfterBuild">\r
+  </Target>\r
+  -->\r
+</Project>
\ No newline at end of file
diff --git a/clang-tidy-vs/ClangTidy/ClangTidy.vsct b/clang-tidy-vs/ClangTidy/ClangTidy.vsct
new file mode 100644 (file)
index 0000000..8bdaeec
--- /dev/null
@@ -0,0 +1,118 @@
+<?xml version="1.0" encoding="utf-8"?>\r
+<CommandTable xmlns="http://schemas.microsoft.com/VisualStudio/2005-10-18/CommandTable" xmlns:xs="http://www.w3.org/2001/XMLSchema">\r
+\r
+  <!--  This is the file that defines the actual layout and type of the commands.\r
+        It is divided in different sections (e.g. command definition, command\r
+        placement, ...), with each defining a specific set of properties.\r
+        See the comment before each section for more details about how to\r
+        use it. -->\r
+\r
+  <!--  The VSCT compiler (the tool that translates this file into the binary \r
+        format that VisualStudio will consume) has the ability to run a preprocessor \r
+        on the vsct file; this preprocessor is (usually) the C++ preprocessor, so \r
+        it is possible to define includes and macros with the same syntax used \r
+        in C++ files. Using this ability of the compiler here, we include some files \r
+        defining some of the constants that we will use inside the file. -->\r
+\r
+  <!--This is the file that defines the IDs for all the commands exposed by VisualStudio. -->\r
+  <Extern href="stdidcmd.h"/>\r
+\r
+  <!--This header contains the command ids for the menus provided by the shell. -->\r
+  <Extern href="vsshlids.h"/>\r
+\r
+\r
+\r
+\r
+  <!--The Commands section is where we the commands, menus and menu groups are defined.\r
+      This section uses a Guid to identify the package that provides the command defined inside it. -->\r
+  <Commands package="guidClangTidyPkg">\r
+    <!-- Inside this section we have different sub-sections: one for the menus, another  \r
+    for the menu groups, one for the buttons (the actual commands), one for the combos \r
+    and the last one for the bitmaps used. Each element is identified by a command id that  \r
+    is a unique pair of guid and numeric identifier; the guid part of the identifier is usually  \r
+    called "command set" and is used to group different command inside a logically related  \r
+    group; your package should define its own command set in order to avoid collisions  \r
+    with command ids defined by other packages. -->\r
+\r
+    \r
+    <!-- In this section you can define new menu groups. A menu group is a container for \r
+         other menus or buttons (commands); from a visual point of view you can see the \r
+         group as the part of a menu contained between two lines. The parent of a group \r
+         must be a menu. -->\r
+    <Groups>\r
+\r
+      <Group guid="guidClangTidyCmdSet" id="MyMenuGroup" priority="0x0600">\r
+        <Parent guid="guidSHLMainMenu" id="IDM_VS_MENU_TOOLS"/>\r
+      </Group>\r
+      \r
+\r
+\r
+    </Groups>\r
+    \r
+    <!--Buttons section. -->\r
+    <!--This section defines the elements the user can interact with, like a menu command or a button \r
+        or combo box in a toolbar. -->\r
+    <Buttons>\r
+      <!--To define a menu group you have to specify its ID, the parent menu and its display priority. \r
+          The command is visible and enabled by default. If you need to change the visibility, status, etc, you can use\r
+          the CommandFlag node.\r
+          You can add more than one CommandFlag node e.g.:\r
+              <CommandFlag>DefaultInvisible</CommandFlag>\r
+              <CommandFlag>DynamicVisibility</CommandFlag>\r
+          If you do not want an image next to your command, remove the Icon node /> -->\r
+\r
+      <Button guid="guidClangTidyCmdSet" id="cmdidClangTidy" priority="0x0100" type="Button">\r
+        <Parent guid="guidClangTidyCmdSet" id="MyMenuGroup" />\r
+        <Icon guid="guidImages" id="bmpPic1" />\r
+        <Strings>\r
+          <ButtonText>ClangTidy</ButtonText>\r
+        </Strings>\r
+      </Button>\r
+\r
+\r
+\r
+    </Buttons>\r
+   \r
+    <!--The bitmaps section is used to define the bitmaps that are used for the commands.-->\r
+    <Bitmaps>\r
+      <!--  The bitmap id is defined in a way that is a little bit different from the others: \r
+            the declaration starts with a guid for the bitmap strip, then there is the resource id of the \r
+            bitmap strip containing the bitmaps and then there are the numeric ids of the elements used \r
+            inside a button definition. An important aspect of this declaration is that the element id \r
+            must be the actual index (1-based) of the bitmap inside the bitmap strip. -->\r
+      <Bitmap guid="guidImages" href="Resources\Images_32bit.bmp" usedList="bmpPic1, bmpPic2, bmpPicSearch, bmpPicX, bmpPicArrows"/>\r
+      \r
+    </Bitmaps>\r
\r
+  </Commands>\r
+\r
+\r
+  <KeyBindings>\r
+    <KeyBinding guid="guidClangTidyCmdSet" id="cmdidClangTidy" editor="guidTextEditor" key1="R" mod1="Control" key2="T" mod2="Control"/>\r
+  </KeyBindings>\r
+\r
+\r
+\r
+  <Symbols>\r
+    <!-- This is the package guid. -->\r
+    <GuidSymbol name="guidClangTidyPkg" value="{AE4956BE-3DB8-430E-BBAB-7E2E9A014E9C}" />\r
+    \r
+    <!-- This is the guid used to group the menu commands together -->\r
+    <GuidSymbol name="guidClangTidyCmdSet" value="{9E0F0493-6493-46DE-AEE1-ACD8F60F265E}">\r
+      <IDSymbol name="MyMenuGroup" value="0x1020" />\r
+      <IDSymbol name="cmdidClangTidy" value="0x0100" />\r
+    </GuidSymbol>\r
+\r
+    <GuidSymbol name="guidTextEditor" value="{E10FAD35-7FB8-4991-A269-EF88F12166C9}" />\r
+\r
+\r
+    <GuidSymbol name="guidImages" value="{942F126F-942D-428A-84B4-4AC7C523D0B2}" >\r
+      <IDSymbol name="bmpPic1" value="1" />\r
+      <IDSymbol name="bmpPic2" value="2" />\r
+      <IDSymbol name="bmpPicSearch" value="3" />\r
+      <IDSymbol name="bmpPicX" value="4" />\r
+      <IDSymbol name="bmpPicArrows" value="5" />\r
+    </GuidSymbol>\r
+  </Symbols>\r
+\r
+</CommandTable>\r
diff --git a/clang-tidy-vs/ClangTidy/ClangTidyCheckAttribute.cs b/clang-tidy-vs/ClangTidy/ClangTidyCheckAttribute.cs
new file mode 100644 (file)
index 0000000..59234d7
--- /dev/null
@@ -0,0 +1,22 @@
+using System;\r
+using System.Collections.Generic;\r
+using System.Linq;\r
+using System.Text;\r
+using System.Threading.Tasks;\r
+\r
+namespace LLVM.ClangTidy\r
+{\r
+    public class ClangTidyCheckAttribute : Attribute\r
+    {\r
+        private string CheckName_;\r
+        public ClangTidyCheckAttribute(string CheckName)\r
+        {\r
+            this.CheckName_ = CheckName;\r
+        }\r
+\r
+        public string CheckName\r
+        {\r
+            get { return CheckName_; }\r
+        }\r
+    }\r
+}\r
diff --git a/clang-tidy-vs/ClangTidy/ClangTidyConfigParser.cs b/clang-tidy-vs/ClangTidy/ClangTidyConfigParser.cs
new file mode 100644 (file)
index 0000000..db5b055
--- /dev/null
@@ -0,0 +1,214 @@
+using System;\r
+using System.Collections.Generic;\r
+using System.IO;\r
+using System.Linq;\r
+using System.Text;\r
+using System.Threading.Tasks;\r
+using YamlDotNet.Serialization;\r
+using YamlDotNet.Serialization.NamingConventions;\r
+\r
+namespace LLVM.ClangTidy\r
+{\r
+    static class ClangTidyConfigParser\r
+    {\r
+        public class CheckOption\r
+        {\r
+            [YamlAlias("key")]\r
+            public string Key { get; set; }\r
+\r
+            [YamlAlias("value")]\r
+            public string Value { get; set; }\r
+        }\r
+        public class ClangTidyYaml\r
+        {\r
+            [YamlAlias("Checks")]\r
+            public string Checks { get; set; }\r
+\r
+            [YamlAlias("CheckOptions")]\r
+            public List<CheckOption> CheckOptions { get; set; }\r
+        }\r
+\r
+        public static List<KeyValuePair<string, ClangTidyProperties>> ParseConfigurationChain(string ClangTidyFile)\r
+        {\r
+            List<KeyValuePair<string, ClangTidyProperties>> Result = new List<KeyValuePair<string, ClangTidyProperties>>();\r
+            Result.Add(new KeyValuePair<string, ClangTidyProperties>(null, ClangTidyProperties.RootProperties));\r
+\r
+            foreach (string P in Utility.SplitPath(ClangTidyFile).Reverse())\r
+            {\r
+                if (!Utility.HasClangTidyFile(P))\r
+                    continue;\r
+\r
+                string ConfigFile = Path.Combine(P, ".clang-tidy");\r
+\r
+                using (StreamReader Reader = new StreamReader(ConfigFile))\r
+                {\r
+                    Deserializer D = new Deserializer(namingConvention: new PascalCaseNamingConvention());\r
+                    ClangTidyYaml Y = D.Deserialize<ClangTidyYaml>(Reader);\r
+                    ClangTidyProperties Parent = Result[Result.Count - 1].Value;\r
+                    ClangTidyProperties NewProps = new ClangTidyProperties(Parent);\r
+                    SetPropertiesFromYaml(Y, NewProps);\r
+                    Result.Add(new KeyValuePair<string, ClangTidyProperties>(P, NewProps));\r
+                }\r
+            }\r
+            return Result;\r
+        }\r
+\r
+        enum TreeLevelOp\r
+        {\r
+            Enable,\r
+            Disable,\r
+            Inherit\r
+        }\r
+\r
+        public static void SerializeClangTidyFile(ClangTidyProperties Props, string ClangTidyFilePath)\r
+        {\r
+            List<string> CommandList = new List<string>();\r
+            SerializeCheckTree(CommandList, Props.GetCheckTree(), TreeLevelOp.Inherit);\r
+\r
+            CommandList.Sort((x, y) =>\r
+            {\r
+                bool LeftSub = x.StartsWith("-");\r
+                bool RightSub = y.StartsWith("-");\r
+                if (LeftSub && !RightSub)\r
+                    return -1;\r
+                if (RightSub && !LeftSub)\r
+                    return 1;\r
+                return StringComparer.CurrentCulture.Compare(x, y);\r
+            });\r
+\r
+            string ConfigFile = Path.Combine(ClangTidyFilePath, ".clang-tidy");\r
+            using (StreamWriter Writer = new StreamWriter(ConfigFile))\r
+            {\r
+                Serializer S = new Serializer(namingConvention: new PascalCaseNamingConvention());\r
+                ClangTidyYaml Yaml = new ClangTidyYaml();\r
+                Yaml.Checks = String.Join(",", CommandList.ToArray());\r
+                S.Serialize(Writer, Yaml);\r
+            }\r
+        }\r
+\r
+        /// <summary>\r
+        /// Convert the given check tree into serialized list of commands that can be written to\r
+        /// the Yaml.  The goal here is to determine the minimal sequence of check commands that\r
+        /// will produce the exact configuration displayed in the UI.  This is complicated by the\r
+        /// fact that an inherited True is not the same as an explicitly specified True.  If the\r
+        /// user has chosen to inherit a setting in a .clang-tidy file, then changing it in the\r
+        /// parent should show the reflected changes in the current file as well.  So we cannot\r
+        /// simply -* everything and then add in the checks we need, because -* immediately marks\r
+        /// every single check as explicitly false, thus disabling inheritance.\r
+        /// </summary>\r
+        /// <param name="CommandList">State passed through this recursive algorithm representing\r
+        /// the sequence of commands we have determined so far.\r
+        /// </param>\r
+        /// <param name="Tree">The check tree to serialize.  This is the parameter that will be\r
+        /// recursed on as successive subtrees get serialized to `CommandList`.\r
+        /// </param>\r
+        /// <param name="CurrentOp">The current state of the subtree.  For example, if the\r
+        /// algorithm decides to -* an entire subtree and then add back one single check,\r
+        /// after adding a -subtree-* command to CommandList, it would pass in a value of\r
+        /// CurrentOp=TreeLevelOp.Disable when it recurses down.  This allows deeper iterations\r
+        /// of the algorithm to know what kind of command (if any) needs to be added to CommandList\r
+        /// in order to put a particular check into a particular state.\r
+        /// </param>\r
+        private static void SerializeCheckTree(List<string> CommandList, CheckTree Tree, TreeLevelOp CurrentOp)\r
+        {\r
+            int NumChecks = Tree.CountChecks;\r
+            int NumDisabled = Tree.CountExplicitlyDisabledChecks;\r
+            int NumEnabled = Tree.CountExplicitlyEnabledChecks;\r
+            int NumInherited = Tree.CountInheritedChecks;\r
+\r
+            if (NumChecks == 0)\r
+                return;\r
+\r
+            if (NumInherited > 0)\r
+                System.Diagnostics.Debug.Assert(CurrentOp == TreeLevelOp.Inherit);\r
+\r
+            // If this entire tree is inherited, just exit, nothing about this needs to\r
+            // go in the clang-tidy file.\r
+            if (NumInherited == NumChecks)\r
+                return;\r
+\r
+            TreeLevelOp NewOp = CurrentOp;\r
+            // If there are no inherited properties in this subtree, decide whether to\r
+            // explicitly enable or disable this subtree.  Decide by looking at whether\r
+            // there is a larger proportion of disabled or enabled descendants.  If\r
+            // there are more disabled items in this subtree for example, disabling the\r
+            // subtree will lead to a smaller configuration file.\r
+            if (NumInherited == 0)\r
+            {\r
+                if (NumDisabled >= NumEnabled)\r
+                    NewOp = TreeLevelOp.Disable;\r
+                else\r
+                    NewOp = TreeLevelOp.Enable;\r
+            }\r
+\r
+            if (NewOp == TreeLevelOp.Disable)\r
+            {\r
+                // Only add an explicit disable command if the tree was not already disabled\r
+                // to begin with.\r
+                if (CurrentOp != TreeLevelOp.Disable)\r
+                {\r
+                    string WildcardPath = "*";\r
+                    if (Tree.Path != null)\r
+                        WildcardPath = Tree.Path + "-" + WildcardPath;\r
+                    CommandList.Add("-" + WildcardPath);\r
+                }\r
+                // If the entire subtree was disabled, there's no point descending.\r
+                if (NumDisabled == NumChecks)\r
+                    return;\r
+            }\r
+            else if (NewOp == TreeLevelOp.Enable)\r
+            {\r
+                // Only add an explicit enable command if the tree was not already enabled\r
+                // to begin with.  Note that if we're at the root, all checks are already\r
+                // enabled by default, so there's no need to explicitly include *\r
+                if (CurrentOp != TreeLevelOp.Enable && Tree.Path != null)\r
+                {\r
+                    string WildcardPath = Tree.Path + "-*";\r
+                    CommandList.Add(WildcardPath);\r
+                }\r
+                // If the entire subtree was enabled, there's no point descending.\r
+                if (NumEnabled == NumChecks)\r
+                    return;\r
+            }\r
+\r
+            foreach (var Child in Tree.Children)\r
+            {\r
+                if (Child.Value is CheckLeaf)\r
+                {\r
+                    CheckLeaf Leaf = (CheckLeaf)Child.Value;\r
+                    if (Leaf.CountExplicitlyEnabledChecks == 1 && NewOp != TreeLevelOp.Enable)\r
+                        CommandList.Add(Leaf.Path);\r
+                    else if (Leaf.CountExplicitlyDisabledChecks == 1 && NewOp != TreeLevelOp.Disable)\r
+                        CommandList.Add("-" + Leaf.Path);\r
+                    continue;\r
+                }\r
+\r
+                System.Diagnostics.Debug.Assert(Child.Value is CheckTree);\r
+                CheckTree ChildTree = (CheckTree)Child.Value;\r
+                SerializeCheckTree(CommandList, ChildTree, NewOp);\r
+            }\r
+        }\r
+\r
+        private static void SetPropertiesFromYaml(ClangTidyYaml Yaml, ClangTidyProperties Props)\r
+        {\r
+            string[] CheckCommands = Yaml.Checks.Split(',');\r
+            foreach (string Command in CheckCommands)\r
+            {\r
+                if (Command == null || Command.Length == 0)\r
+                    continue;\r
+                bool Add = true;\r
+                string Pattern = Command;\r
+                if (Pattern[0] == '-')\r
+                {\r
+                    Pattern = Pattern.Substring(1);\r
+                    Add = false;\r
+                }\r
+\r
+                foreach (var Match in CheckDatabase.Checks.Where(x => Utility.MatchWildcardString(x.Name, Pattern)))\r
+                {\r
+                    Props.SetDynamicValue(Match.Name, Add);\r
+                }\r
+            }\r
+        }\r
+    }\r
+}\r
diff --git a/clang-tidy-vs/ClangTidy/ClangTidyConfigurationPage.cs b/clang-tidy-vs/ClangTidy/ClangTidyConfigurationPage.cs
new file mode 100644 (file)
index 0000000..0f7a8e9
--- /dev/null
@@ -0,0 +1,61 @@
+using Microsoft.VisualStudio;\r
+using Microsoft.VisualStudio.Shell;\r
+using Microsoft.VisualStudio.Shell.Interop;\r
+using System;\r
+using System.Collections;\r
+using System.Collections.Generic;\r
+using System.ComponentModel;\r
+using System.Linq;\r
+using System.Runtime.InteropServices;\r
+using System.Text;\r
+using System.Threading.Tasks;\r
+using System.Windows.Forms;\r
+\r
+namespace LLVM.ClangTidy\r
+{\r
+    [ClassInterface(ClassInterfaceType.AutoDual)]\r
+    [CLSCompliant(false), ComVisible(true)]\r
+    public class ClangTidyConfigurationPage : DialogPage\r
+    {\r
+        ClangTidyPropertyGrid Grid = null;\r
+        protected override IWin32Window Window\r
+        {\r
+            get\r
+            {\r
+                if (Grid == null)\r
+                    Grid = new ClangTidyPropertyGrid();\r
+                return Grid;\r
+            }\r
+        }\r
+\r
+        protected override void SaveSetting(PropertyDescriptor property)\r
+        {\r
+            base.SaveSetting(property);\r
+        }\r
+\r
+        public override void SaveSettingsToStorage()\r
+        {\r
+            if (Grid != null)\r
+                Grid.SaveSettingsToStorage();\r
+\r
+            base.SaveSettingsToStorage();\r
+        }\r
+\r
+        public override void ResetSettings()\r
+        {\r
+            base.ResetSettings();\r
+        }\r
+\r
+        protected override void LoadSettingFromStorage(PropertyDescriptor prop)\r
+        {\r
+            base.LoadSettingFromStorage(prop);\r
+        }\r
+\r
+        public override void LoadSettingsFromStorage()\r
+        {\r
+            if (Grid != null)\r
+                Grid.InitializeSettings();\r
+            base.LoadSettingsFromStorage();\r
+        }\r
+    }\r
+}\r
diff --git a/clang-tidy-vs/ClangTidy/ClangTidyPackage.cs b/clang-tidy-vs/ClangTidy/ClangTidyPackage.cs
new file mode 100644 (file)
index 0000000..9a0c9b6
--- /dev/null
@@ -0,0 +1,56 @@
+//===-- ClangTidyPackages.cs - VSPackage for clang-tidy ----------*- C# -*-===//\r
+//\r
+//                     The LLVM Compiler Infrastructure\r
+//\r
+// This file is distributed under the University of Illinois Open Source\r
+// License. See LICENSE.TXT for details.\r
+//\r
+//===----------------------------------------------------------------------===//\r
+//\r
+// This class contains a VS extension package that runs clang-tidy over a\r
+// file in a VS text editor.\r
+//\r
+//===----------------------------------------------------------------------===//\r
+\r
+using Microsoft.VisualStudio.Editor;\r
+using Microsoft.VisualStudio.Shell;\r
+using Microsoft.VisualStudio.Shell.Interop;\r
+using Microsoft.VisualStudio.TextManager.Interop;\r
+using System;\r
+using System.Collections;\r
+using System.ComponentModel;\r
+using System.ComponentModel.Design;\r
+using System.IO;\r
+using System.Runtime.InteropServices;\r
+using System.Windows.Forms;\r
+using System.Xml.Linq;\r
+\r
+namespace LLVM.ClangTidy\r
+{\r
+    [PackageRegistration(UseManagedResourcesOnly = true)]\r
+    [InstalledProductRegistration("#110", "#112", "1.0", IconResourceID = 400)]\r
+    [ProvideMenuResource("Menus.ctmenu", 1)]\r
+    [Guid(GuidList.guidClangTidyPkgString)]\r
+    [ProvideOptionPage(typeof(ClangTidyConfigurationPage), "LLVM/Clang", "ClangTidy", 0, 0, true)]\r
+    public sealed class ClangTidyPackage : Package\r
+    {\r
+        #region Package Members\r
+        protected override void Initialize()\r
+        {\r
+            base.Initialize();\r
+\r
+            var commandService = GetService(typeof(IMenuCommandService)) as OleMenuCommandService;\r
+            if (commandService != null)\r
+            {\r
+                var menuCommandID = new CommandID(GuidList.guidClangTidyCmdSet, (int)PkgCmdIDList.cmdidClangTidy);\r
+                var menuItem = new MenuCommand(MenuItemCallback, menuCommandID);\r
+                commandService.AddCommand(menuItem);\r
+            }\r
+        }\r
+        #endregion\r
+\r
+        private void MenuItemCallback(object sender, EventArgs args)\r
+        {\r
+        }\r
+    }\r
+}\r
diff --git a/clang-tidy-vs/ClangTidy/ClangTidyProperties.cs b/clang-tidy-vs/ClangTidy/ClangTidyProperties.cs
new file mode 100644 (file)
index 0000000..c6597c1
--- /dev/null
@@ -0,0 +1,83 @@
+using System;\r
+using System.Collections.Generic;\r
+using System.ComponentModel;\r
+using System.Globalization;\r
+using System.Linq;\r
+using System.Reflection;\r
+using System.Runtime.InteropServices;\r
+using System.Text;\r
+using System.Threading.Tasks;\r
+\r
+namespace LLVM.ClangTidy\r
+{\r
+\r
+    public class ClangTidyProperties : DynamicPropertyComponent\r
+    {\r
+        private static ClangTidyProperties RootProperties_ = null;\r
+        private CheckTree CheckTree_;\r
+        private bool HasUnsavedChanges_ = false;\r
+\r
+        public struct CheckMapping\r
+        {\r
+            public string CheckName;\r
+            public string Property;\r
+        }\r
+\r
+        public ClangTidyProperties()\r
+            : base(null)\r
+        {\r
+            AddClangCheckProperties();\r
+            CheckTree_ = CheckTree.Build(this);\r
+        }\r
+\r
+        public ClangTidyProperties(DynamicPropertyComponent Parent)\r
+            : base(Parent)\r
+        {\r
+            AddClangCheckProperties();\r
+            CheckTree_ = CheckTree.Build(this);\r
+        }\r
+\r
+        static ClangTidyProperties()\r
+        {\r
+            RootProperties_ = new ClangTidyProperties(null);\r
+        }\r
+\r
+        public static ClangTidyProperties RootProperties\r
+        {\r
+            get { return RootProperties_; }\r
+        }\r
+\r
+        private void AddClangCheckProperties()\r
+        {\r
+            // Add each check in the check database\r
+            HashSet<string> Categories = new HashSet<string>();\r
+            foreach (var Check in CheckDatabase.Checks)\r
+            {\r
+                string Name = Check.Name.Replace('-', '_');\r
+                List<Attribute> Attrs = new List<Attribute>();\r
+                Attrs.Add(new CategoryAttribute(Check.Category));\r
+                Attrs.Add(new DisplayNameAttribute(Check.Label));\r
+                Attrs.Add(new DefaultValueAttribute(true));\r
+                Attrs.Add(new DescriptionAttribute(Check.Desc));\r
+                Attrs.Add(new ClangTidyCheckAttribute(Check.Name));\r
+                Categories.Add(Check.Category);\r
+                AddDynamicProperty<bool>(Check.Name, Attrs.ToArray());\r
+            }\r
+\r
+            // Add a category verb for each unique category.\r
+            foreach (string Cat in Categories)\r
+            {\r
+                List<Attribute> Attrs = new List<Attribute>();\r
+                Attrs.Add(new CategoryAttribute(Cat));\r
+                Attrs.Add(new DisplayNameAttribute("(Category Verbs)"));\r
+                Attrs.Add(new TypeConverterAttribute(typeof(CategoryVerbConverter)));\r
+                Attrs.Add(new DefaultValueAttribute(CategoryVerb.None));\r
+                AddDynamicProperty<CategoryVerb>(Cat + "Verb", Attrs.ToArray());\r
+            }\r
+        }\r
+\r
+        public CheckTree GetCheckTree() { return CheckTree_; }\r
+        public bool GetHasUnsavedChanges() { return HasUnsavedChanges_; }\r
+        public void SetHasUnsavedChanges(bool Value) { HasUnsavedChanges_ = Value; }\r
+    }\r
+}\r
diff --git a/clang-tidy-vs/ClangTidy/ClangTidyPropertyGrid.Designer.cs b/clang-tidy-vs/ClangTidy/ClangTidyPropertyGrid.Designer.cs
new file mode 100644 (file)
index 0000000..ce9324a
--- /dev/null
@@ -0,0 +1,119 @@
+namespace LLVM.ClangTidy\r
+{\r
+    partial class ClangTidyPropertyGrid\r
+    {\r
+        /// <summary> \r
+        /// Required designer variable.\r
+        /// </summary>\r
+        private System.ComponentModel.IContainer components = null;\r
+\r
+        /// <summary> \r
+        /// Clean up any resources being used.\r
+        /// </summary>\r
+        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>\r
+        protected override void Dispose(bool disposing)\r
+        {\r
+            if (disposing && (components != null))\r
+            {\r
+                components.Dispose();\r
+            }\r
+            base.Dispose(disposing);\r
+        }\r
+\r
+        #region Component Designer generated code\r
+\r
+        /// <summary> \r
+        /// Required method for Designer support - do not modify \r
+        /// the contents of this method with the code editor.\r
+        /// </summary>\r
+        private void InitializeComponent()\r
+        {\r
+            this.label1 = new System.Windows.Forms.Label();\r
+            this.textBox1 = new System.Windows.Forms.TextBox();\r
+            this.button1 = new System.Windows.Forms.Button();\r
+            this.propertyGrid1 = new System.Windows.Forms.PropertyGrid();\r
+            this.clangTidyProperties1 = new LLVM.ClangTidy.ClangTidyProperties();\r
+            this.clangTidyConfigurationPage1 = new LLVM.ClangTidy.ClangTidyConfigurationPage();\r
+            this.linkLabelPath = new System.Windows.Forms.LinkLabel();\r
+            this.SuspendLayout();\r
+            // \r
+            // label1\r
+            // \r
+            this.label1.AutoSize = true;\r
+            this.label1.Location = new System.Drawing.Point(14, 17);\r
+            this.label1.Name = "label1";\r
+            this.label1.Size = new System.Drawing.Size(88, 13);\r
+            this.label1.TabIndex = 0;\r
+            this.label1.Text = "Configuration File";\r
+            // \r
+            // textBox1\r
+            // \r
+            this.textBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) \r
+            | System.Windows.Forms.AnchorStyles.Right)));\r
+            this.textBox1.Location = new System.Drawing.Point(108, 14);\r
+            this.textBox1.Name = "textBox1";\r
+            this.textBox1.Size = new System.Drawing.Size(222, 20);\r
+            this.textBox1.TabIndex = 1;\r
+            // \r
+            // button1\r
+            // \r
+            this.button1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));\r
+            this.button1.Location = new System.Drawing.Point(336, 14);\r
+            this.button1.Name = "button1";\r
+            this.button1.Size = new System.Drawing.Size(78, 20);\r
+            this.button1.TabIndex = 2;\r
+            this.button1.Text = "Browse";\r
+            this.button1.UseVisualStyleBackColor = true;\r
+            this.button1.Click += new System.EventHandler(this.button1_Click);\r
+            // \r
+            // propertyGrid1\r
+            // \r
+            this.propertyGrid1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) \r
+            | System.Windows.Forms.AnchorStyles.Left) \r
+            | System.Windows.Forms.AnchorStyles.Right)));\r
+            this.propertyGrid1.Location = new System.Drawing.Point(20, 73);\r
+            this.propertyGrid1.Name = "propertyGrid1";\r
+            this.propertyGrid1.SelectedObject = this.clangTidyProperties1;\r
+            this.propertyGrid1.Size = new System.Drawing.Size(391, 384);\r
+            this.propertyGrid1.TabIndex = 6;\r
+            this.propertyGrid1.ViewBorderColor = System.Drawing.SystemColors.ControlDarkDark;\r
+            this.propertyGrid1.PropertyValueChanged += new System.Windows.Forms.PropertyValueChangedEventHandler(this.propertyGrid1_PropertyValueChanged);\r
+            // \r
+            // linkLabelPath\r
+            // \r
+            this.linkLabelPath.AutoSize = true;\r
+            this.linkLabelPath.Location = new System.Drawing.Point(29, 50);\r
+            this.linkLabelPath.Name = "linkLabelPath";\r
+            this.linkLabelPath.Size = new System.Drawing.Size(55, 13);\r
+            this.linkLabelPath.TabIndex = 7;\r
+            this.linkLabelPath.TabStop = true;\r
+            this.linkLabelPath.Text = "linkLabel1";\r
+            this.linkLabelPath.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkLabelPath_LinkClicked);\r
+            // \r
+            // ClangTidyPropertyGrid\r
+            // \r
+            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);\r
+            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;\r
+            this.Controls.Add(this.linkLabelPath);\r
+            this.Controls.Add(this.propertyGrid1);\r
+            this.Controls.Add(this.button1);\r
+            this.Controls.Add(this.textBox1);\r
+            this.Controls.Add(this.label1);\r
+            this.Name = "ClangTidyPropertyGrid";\r
+            this.Size = new System.Drawing.Size(444, 469);\r
+            this.ResumeLayout(false);\r
+            this.PerformLayout();\r
+\r
+        }\r
+\r
+        #endregion\r
+\r
+        private System.Windows.Forms.Label label1;\r
+        private System.Windows.Forms.TextBox textBox1;\r
+        private System.Windows.Forms.Button button1;\r
+        private System.Windows.Forms.PropertyGrid propertyGrid1;\r
+        private ClangTidyProperties clangTidyProperties1;\r
+        private ClangTidyConfigurationPage clangTidyConfigurationPage1;\r
+        private System.Windows.Forms.LinkLabel linkLabelPath;\r
+    }\r
+}\r
diff --git a/clang-tidy-vs/ClangTidy/ClangTidyPropertyGrid.cs b/clang-tidy-vs/ClangTidy/ClangTidyPropertyGrid.cs
new file mode 100644 (file)
index 0000000..20c8a8f
--- /dev/null
@@ -0,0 +1,208 @@
+//===-- ClangTidyPropertyGrid.cs - UI for configuring clang-tidy -*- C# -*-===//\r
+//\r
+//                     The LLVM Compiler Infrastructure\r
+//\r
+// This file is distributed under the University of Illinois Open Source\r
+// License. See LICENSE.TXT for details.\r
+//\r
+//===----------------------------------------------------------------------===//\r
+//\r
+// This class contains a UserControl consisting of a .NET PropertyGrid control\r
+// allowing configuration of checks and check options for ClangTidy.\r
+//\r
+//===----------------------------------------------------------------------===//\r
+using System;\r
+using System.Collections.Generic;\r
+using System.ComponentModel;\r
+using System.Drawing;\r
+using System.Data;\r
+using System.Linq;\r
+using System.Text;\r
+using System.Threading.Tasks;\r
+using System.Windows.Forms;\r
+using System.IO;\r
+using Microsoft.VisualStudio.Shell;\r
+\r
+namespace LLVM.ClangTidy\r
+{\r
+    /// <summary>\r
+    ///  A UserControl displaying a PropertyGrid allowing configuration of clang-tidy\r
+    ///  checks and check options, as well as serialization and deserialization of\r
+    ///  clang-tidy configuration files.  When a configuration file is loaded, the\r
+    ///  entire chain of configuration files is analyzed based on the file path,\r
+    ///  and quick access is provided to edit or view any of the files in the\r
+    ///  configuration chain, allowing easy visualization of where values come from\r
+    ///  (similar in spirit to the -explain-config option of clang-tidy).\r
+    /// </summary>\r
+    public partial class ClangTidyPropertyGrid : UserControl\r
+    {\r
+        /// <summary>\r
+        /// The sequence of .clang-tidy configuration files, starting from the root\r
+        /// of the filesystem, down to the selected file.\r
+        /// </summary>\r
+        List<KeyValuePair<string, ClangTidyProperties>> PropertyChain_ = null;\r
+\r
+        public ClangTidyPropertyGrid()\r
+        {\r
+            InitializeComponent();\r
+            InitializeSettings();\r
+        }\r
+\r
+        private enum ShouldCancel\r
+        {\r
+            Yes,\r
+            No,\r
+        }\r
+\r
+        public void SaveSettingsToStorage()\r
+        {\r
+            PersistUnsavedChanges(false);\r
+        }\r
+\r
+        private ShouldCancel PersistUnsavedChanges(bool PromptFirst)\r
+        {\r
+            var UnsavedResults = PropertyChain_.Where(x => x.Key != null && x.Value.GetHasUnsavedChanges());\r
+            if (UnsavedResults.Count() == 0)\r
+                return ShouldCancel.No;\r
+\r
+            bool ShouldSave = false;\r
+            if (PromptFirst)\r
+            {\r
+                var Response = MessageBox.Show(\r
+                    "You have unsaved changes!  Do you want to save before loading a new file?",\r
+                    "clang-tidy",\r
+                    MessageBoxButtons.YesNoCancel);\r
+\r
+                ShouldSave = (Response == DialogResult.Yes);\r
+                if (Response == DialogResult.Cancel)\r
+                    return ShouldCancel.Yes;\r
+            }\r
+            else\r
+                ShouldSave = true;\r
+\r
+            if (ShouldSave)\r
+            {\r
+                foreach (var Result in UnsavedResults)\r
+                {\r
+                    ClangTidyConfigParser.SerializeClangTidyFile(Result.Value, Result.Key);\r
+                    Result.Value.SetHasUnsavedChanges(false);\r
+                }\r
+            }\r
+            return ShouldCancel.No;\r
+        }\r
+\r
+        public void InitializeSettings()\r
+        {\r
+            PropertyChain_ = new List<KeyValuePair<string, ClangTidyProperties>>();\r
+            PropertyChain_.Add(new KeyValuePair<string, ClangTidyProperties>(null, ClangTidyProperties.RootProperties));\r
+            reloadPropertyChain();\r
+        }\r
+\r
+        private void button1_Click(object sender, EventArgs e)\r
+        {\r
+            ShouldCancel Cancel = PersistUnsavedChanges(true);\r
+            if (Cancel == ShouldCancel.Yes)\r
+                return;\r
+\r
+            using (OpenFileDialog D = new OpenFileDialog())\r
+            {\r
+                D.Filter = "Clang Tidy files|.clang-tidy";\r
+                D.CheckPathExists = true;\r
+                D.CheckFileExists = true;\r
+\r
+                if (D.ShowDialog() == DialogResult.OK)\r
+                {\r
+                    PropertyChain_.Clear();\r
+                    PropertyChain_ = ClangTidyConfigParser.ParseConfigurationChain(D.FileName);\r
+                    textBox1.Text = D.FileName;\r
+                    reloadPropertyChain();\r
+                }\r
+            }\r
+        }\r
+\r
+        private static readonly string DefaultText = "(Default)";\r
+        private static readonly string BrowseText = "Browse for a file to edit its properties";\r
+\r
+        /// <summary>\r
+        /// After a new configuration file is chosen, analyzes the directory hierarchy\r
+        /// and finds all .clang-tidy files in the path, parses them and updates the\r
+        /// PropertyGrid and quick-access LinkLabel control to reflect the new property\r
+        /// chain.\r
+        /// </summary>\r
+        private void reloadPropertyChain()\r
+        {\r
+            StringBuilder LinkBuilder = new StringBuilder();\r
+            LinkBuilder.Append(DefaultText);\r
+            LinkBuilder.Append(" > ");\r
+            int PrefixLength = LinkBuilder.Length;\r
+\r
+            if (PropertyChain_.Count == 1)\r
+                LinkBuilder.Append(BrowseText);\r
+            else\r
+                LinkBuilder.Append(PropertyChain_[PropertyChain_.Count - 1].Key);\r
+\r
+            linkLabelPath.Text = LinkBuilder.ToString();\r
+\r
+            // Given a path like D:\Foo\Bar\Baz, construct a LinkLabel where individual\r
+            // components of the path are clickable iff they contain a .clang-tidy file.\r
+            // Clicking one of the links then updates the PropertyGrid to display the\r
+            // selected .clang-tidy file.\r
+            ClangTidyProperties LastProps = ClangTidyProperties.RootProperties;\r
+            linkLabelPath.Links.Clear();\r
+            linkLabelPath.Links.Add(0, DefaultText.Length, LastProps);\r
+            foreach (var Prop in PropertyChain_.Skip(1))\r
+            {\r
+                LastProps = Prop.Value;\r
+                string ClangTidyFolder = Path.GetFileName(Prop.Key);\r
+                int ClangTidyFolderOffset = Prop.Key.Length - ClangTidyFolder.Length;\r
+                linkLabelPath.Links.Add(PrefixLength + ClangTidyFolderOffset, ClangTidyFolder.Length, LastProps);\r
+            }\r
+            propertyGrid1.SelectedObject = LastProps;\r
+        }\r
+\r
+        private void propertyGrid1_PropertyValueChanged(object s, PropertyValueChangedEventArgs e)\r
+        {\r
+            ClangTidyProperties Props = (ClangTidyProperties)propertyGrid1.SelectedObject;\r
+            Props.SetHasUnsavedChanges(true);\r
+\r
+            // When a CategoryVerb is selected, perform the corresponding action.\r
+            PropertyDescriptor Property = e.ChangedItem.PropertyDescriptor;\r
+            if (!(e.ChangedItem.Value is CategoryVerb))\r
+                return;\r
+\r
+            CategoryVerb Action = (CategoryVerb)e.ChangedItem.Value;\r
+            if (Action == CategoryVerb.None)\r
+                return;\r
+\r
+            var Category = Property.Attributes.OfType<CategoryAttribute>().FirstOrDefault();\r
+            if (Category == null)\r
+                return;\r
+            var SameCategoryProps = Props.GetProperties(new Attribute[] { Category });\r
+            foreach (PropertyDescriptor P in SameCategoryProps)\r
+            {\r
+                if (P == Property)\r
+                    continue;\r
+                switch (Action)\r
+                {\r
+                    case CategoryVerb.Disable:\r
+                        P.SetValue(propertyGrid1.SelectedObject, false);\r
+                        break;\r
+                    case CategoryVerb.Enable:\r
+                        P.SetValue(propertyGrid1.SelectedObject, true);\r
+                        break;\r
+                    case CategoryVerb.Inherit:\r
+                        P.ResetValue(propertyGrid1.SelectedObject);\r
+                        break;\r
+                }\r
+            }\r
+            Property.ResetValue(propertyGrid1.SelectedObject);\r
+            propertyGrid1.Invalidate();\r
+        }\r
+\r
+        private void linkLabelPath_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)\r
+        {\r
+            ClangTidyProperties Props = (ClangTidyProperties)e.Link.LinkData;\r
+            propertyGrid1.SelectedObject = Props;\r
+        }\r
+    }\r
+}\r
diff --git a/clang-tidy-vs/ClangTidy/ClangTidyPropertyGrid.resx b/clang-tidy-vs/ClangTidy/ClangTidyPropertyGrid.resx
new file mode 100644 (file)
index 0000000..22dfde0
--- /dev/null
@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="utf-8"?>\r
+<root>\r
+  <!-- \r
+    Microsoft ResX Schema \r
+    \r
+    Version 2.0\r
+    \r
+    The primary goals of this format is to allow a simple XML format \r
+    that is mostly human readable. The generation and parsing of the \r
+    various data types are done through the TypeConverter classes \r
+    associated with the data types.\r
+    \r
+    Example:\r
+    \r
+    ... ado.net/XML headers & schema ...\r
+    <resheader name="resmimetype">text/microsoft-resx</resheader>\r
+    <resheader name="version">2.0</resheader>\r
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\r
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\r
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>\r
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>\r
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">\r
+        <value>[base64 mime encoded serialized .NET Framework object]</value>\r
+    </data>\r
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">\r
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\r
+        <comment>This is a comment</comment>\r
+    </data>\r
+                \r
+    There are any number of "resheader" rows that contain simple \r
+    name/value pairs.\r
+    \r
+    Each data row contains a name, and value. The row also contains a \r
+    type or mimetype. Type corresponds to a .NET class that support \r
+    text/value conversion through the TypeConverter architecture. \r
+    Classes that don't support this are serialized and stored with the \r
+    mimetype set.\r
+    \r
+    The mimetype is used for serialized objects, and tells the \r
+    ResXResourceReader how to depersist the object. This is currently not \r
+    extensible. For a given mimetype the value must be set accordingly:\r
+    \r
+    Note - application/x-microsoft.net.object.binary.base64 is the format \r
+    that the ResXResourceWriter will generate, however the reader can \r
+    read any of the formats listed below.\r
+    \r
+    mimetype: application/x-microsoft.net.object.binary.base64\r
+    value   : The object must be serialized with \r
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter\r
+            : and then encoded with base64 encoding.\r
+    \r
+    mimetype: application/x-microsoft.net.object.soap.base64\r
+    value   : The object must be serialized with \r
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\r
+            : and then encoded with base64 encoding.\r
+\r
+    mimetype: application/x-microsoft.net.object.bytearray.base64\r
+    value   : The object must be serialized into a byte array \r
+            : using a System.ComponentModel.TypeConverter\r
+            : and then encoded with base64 encoding.\r
+    -->\r
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">\r
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />\r
+    <xsd:element name="root" msdata:IsDataSet="true">\r
+      <xsd:complexType>\r
+        <xsd:choice maxOccurs="unbounded">\r
+          <xsd:element name="metadata">\r
+            <xsd:complexType>\r
+              <xsd:sequence>\r
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />\r
+              </xsd:sequence>\r
+              <xsd:attribute name="name" use="required" type="xsd:string" />\r
+              <xsd:attribute name="type" type="xsd:string" />\r
+              <xsd:attribute name="mimetype" type="xsd:string" />\r
+              <xsd:attribute ref="xml:space" />\r
+            </xsd:complexType>\r
+          </xsd:element>\r
+          <xsd:element name="assembly">\r
+            <xsd:complexType>\r
+              <xsd:attribute name="alias" type="xsd:string" />\r
+              <xsd:attribute name="name" type="xsd:string" />\r
+            </xsd:complexType>\r
+          </xsd:element>\r
+          <xsd:element name="data">\r
+            <xsd:complexType>\r
+              <xsd:sequence>\r
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />\r
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />\r
+              </xsd:sequence>\r
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />\r
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />\r
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />\r
+              <xsd:attribute ref="xml:space" />\r
+            </xsd:complexType>\r
+          </xsd:element>\r
+          <xsd:element name="resheader">\r
+            <xsd:complexType>\r
+              <xsd:sequence>\r
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />\r
+              </xsd:sequence>\r
+              <xsd:attribute name="name" type="xsd:string" use="required" />\r
+            </xsd:complexType>\r
+          </xsd:element>\r
+        </xsd:choice>\r
+      </xsd:complexType>\r
+    </xsd:element>\r
+  </xsd:schema>\r
+  <resheader name="resmimetype">\r
+    <value>text/microsoft-resx</value>\r
+  </resheader>\r
+  <resheader name="version">\r
+    <value>2.0</value>\r
+  </resheader>\r
+  <resheader name="reader">\r
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\r
+  </resheader>\r
+  <resheader name="writer">\r
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\r
+  </resheader>\r
+  <metadata name="clangTidyConfigurationPage1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">\r
+    <value>183, 17</value>\r
+  </metadata>\r
+</root>
\ No newline at end of file
diff --git a/clang-tidy-vs/ClangTidy/DynamicPropertyComponent.Designer.cs b/clang-tidy-vs/ClangTidy/DynamicPropertyComponent.Designer.cs
new file mode 100644 (file)
index 0000000..099f1c0
--- /dev/null
@@ -0,0 +1,42 @@
+using System;\r
+using System.Collections.Generic;\r
+using System.Linq;\r
+using System.Text;\r
+using System.Threading.Tasks;\r
+\r
+namespace LLVM.ClangTidy\r
+{\r
+    partial class DynamicPropertyComponent\r
+    {\r
+        /// <summary>\r
+        /// Required designer variable.\r
+        /// </summary>\r
+        private System.ComponentModel.IContainer components = null;\r
+\r
+        /// <summary> \r
+        /// Clean up any resources being used.\r
+        /// </summary>\r
+        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>\r
+        protected override void Dispose(bool disposing)\r
+        {\r
+            if (disposing && (components != null))\r
+            {\r
+                components.Dispose();\r
+            }\r
+            base.Dispose(disposing);\r
+        }\r
+\r
+        #region Component Designer generated code\r
+\r
+        /// <summary>\r
+        /// Required method for Designer support - do not modify\r
+        /// the contents of this method with the code editor.\r
+        /// </summary>\r
+        private void InitializeComponent()\r
+        {\r
+            components = new System.ComponentModel.Container();\r
+        }\r
+\r
+        #endregion\r
+    }\r
+}\r
diff --git a/clang-tidy-vs/ClangTidy/DynamicPropertyComponent.cs b/clang-tidy-vs/ClangTidy/DynamicPropertyComponent.cs
new file mode 100644 (file)
index 0000000..e8843db
--- /dev/null
@@ -0,0 +1,138 @@
+using System;\r
+using System.Collections.Generic;\r
+using System.ComponentModel;\r
+using System.Linq;\r
+using System.Text;\r
+using System.Threading.Tasks;\r
+\r
+namespace LLVM.ClangTidy\r
+{\r
+    /// <summary>\r
+    /// The goal of this class is to enable displaying of a PropertyGrid in much the\r
+    /// same way that Visual Studio's C++ project system does.  A project or file can\r
+    /// have properties which might inherit from their parent, or be overridden.\r
+    /// It turns out this is somewhat non-trivial.  The .NET PropertyGrid is good makes\r
+    /// displaying simple properties with a static notion of what constitutes a\r
+    /// "default" value very easy.  You simply apply an Attribute to the class that says\r
+    /// what the default value is and you're done.  But when you try to introduce the idea\r
+    /// that a property's default value depends on some other factor, things get much more\r
+    /// complicated due to the static nature of Attributes.\r
+    /// \r
+    /// The solution to this is to inherit from ICustomTypeDescriptor.  This is the mechanism\r
+    /// by which you can inject or modify attributes or properties at runtime.  The .NET\r
+    /// PropertyGrid is designed in such a way that instead of using simple .NET Reflection to\r
+    /// look for the properties and attributes on a class, it will invoke the methods of\r
+    /// ICustomTypeDescriptor (if your type inherits from it), and ask those methods.  Our\r
+    /// implementation of ICustomTypeDescriptor works by waiting until the PropertyGrid requests\r
+    /// PropertyDescriptors for each of the properties, and then "decorating" them with our\r
+    /// own custom PropertyDescriptor implementation which understands the proeprty inheritance\r
+    /// model we wish to implement.\r
+    /// </summary>\r
+    public partial class DynamicPropertyComponent : Component, ICustomTypeDescriptor\r
+    {\r
+        PropertyDescriptorCollection DynamicProperties_ = new PropertyDescriptorCollection(null);\r
+        private DynamicPropertyComponent Parent_;\r
+\r
+        public DynamicPropertyComponent(DynamicPropertyComponent Parent)\r
+        {\r
+            Parent_ = Parent;\r
+        }\r
+\r
+        public DynamicPropertyComponent(DynamicPropertyComponent Parent, IContainer container)\r
+        {\r
+            Parent_ = Parent;\r
+\r
+            container.Add(this);\r
+            InitializeComponent();\r
+        }\r
+\r
+        public AttributeCollection GetAttributes()\r
+        {\r
+            return TypeDescriptor.GetAttributes(GetType());\r
+        }\r
+\r
+        public string GetClassName()\r
+        {\r
+            return TypeDescriptor.GetClassName(GetType());\r
+        }\r
+\r
+        public string GetComponentName()\r
+        {\r
+            return TypeDescriptor.GetComponentName(GetType());\r
+        }\r
+\r
+        public TypeConverter GetConverter()\r
+        {\r
+            return TypeDescriptor.GetConverter(GetType());\r
+        }\r
+\r
+        public EventDescriptor GetDefaultEvent()\r
+        {\r
+            return TypeDescriptor.GetDefaultEvent(GetType());\r
+        }\r
+\r
+        public PropertyDescriptor GetDefaultProperty()\r
+        {\r
+            return TypeDescriptor.GetDefaultProperty(GetType());\r
+        }\r
+\r
+        public object GetEditor(Type editorBaseType)\r
+        {\r
+            return TypeDescriptor.GetEditor(GetType(), editorBaseType);\r
+        }\r
+\r
+        public EventDescriptorCollection GetEvents()\r
+        {\r
+            return TypeDescriptor.GetEvents(GetType());\r
+        }\r
+\r
+        public EventDescriptorCollection GetEvents(Attribute[] attributes)\r
+        {\r
+            return TypeDescriptor.GetEvents(GetType(), attributes);\r
+        }\r
+\r
+        public PropertyDescriptorCollection GetProperties()\r
+        {\r
+            return DynamicProperties_;\r
+        }\r
+\r
+        public PropertyDescriptorCollection GetProperties(Attribute[] attributes)\r
+        {\r
+            var Props = DynamicProperties_.OfType<PropertyDescriptor>();\r
+            var Filtered = Props.Where(x => x.Attributes.Contains(attributes)).ToArray();\r
+            return new PropertyDescriptorCollection(Filtered);\r
+        }\r
+\r
+        public object GetPropertyOwner(PropertyDescriptor pd)\r
+        {\r
+            return this;\r
+        }\r
+\r
+        public void SetDynamicValue<T>(string Name, T Value)\r
+        {\r
+            Name = Name.Replace('-', '_');\r
+            DynamicPropertyDescriptor<T> Descriptor = (DynamicPropertyDescriptor<T>)DynamicProperties_.Find(Name, false);\r
+            Descriptor.SetValue(this, Value);\r
+        }\r
+\r
+        public T GetDynamicValue<T>(string Name)\r
+        {\r
+            Name = Name.Replace('-', '_');\r
+            DynamicPropertyDescriptor<T> Descriptor = (DynamicPropertyDescriptor<T>)DynamicProperties_.Find(Name, false);\r
+            return (T)Descriptor.GetValue(this);\r
+        }\r
+\r
+        protected void AddDynamicProperty<T>(string Name, Attribute[] Attributes)\r
+        {\r
+            Name = Name.Replace('-', '_');\r
+\r
+            // If we have a parent, find the corresponding PropertyDescriptor with the same\r
+            // name from the parent.\r
+            DynamicPropertyDescriptor<T> ParentDescriptor = null;\r
+            if (Parent_ != null)\r
+                ParentDescriptor = (DynamicPropertyDescriptor<T>)Parent_.GetProperties().Find(Name, false);\r
+\r
+            DynamicProperties_.Add(new DynamicPropertyDescriptor<T>(Name, ParentDescriptor, Name, Attributes));\r
+        }\r
+    }\r
+}\r
diff --git a/clang-tidy-vs/ClangTidy/DynamicPropertyConverter.cs b/clang-tidy-vs/ClangTidy/DynamicPropertyConverter.cs
new file mode 100644 (file)
index 0000000..9442667
--- /dev/null
@@ -0,0 +1,139 @@
+using System;\r
+using System.Collections;\r
+using System.Collections.Generic;\r
+using System.ComponentModel;\r
+using System.Globalization;\r
+using System.Linq;\r
+using System.Text;\r
+using System.Threading.Tasks;\r
+\r
+namespace LLVM.ClangTidy\r
+{\r
+    class MagicInheritance\r
+    {\r
+        public static readonly string Value = "{3A27184D-1774-489B-9BB7-7191B8E8E622}";\r
+        public static readonly string Text = "<Inherit from project or parent>";\r
+    }\r
+\r
+\r
+    class DynamicPropertyConverter<T> : TypeConverter\r
+    {\r
+        private DynamicPropertyDescriptor<T> Descriptor_;\r
+        private TypeConverter Root_;\r
+\r
+        public DynamicPropertyConverter(DynamicPropertyDescriptor<T> Descriptor, TypeConverter Root)\r
+        {\r
+            Descriptor_ = Descriptor;\r
+            Root_ = Root;\r
+        }\r
+\r
+        /// <summary>\r
+        /// Returns true if there are specific values that can be chosen from a dropdown\r
+        /// for this property.  Regardless of whether standard values are supported for\r
+        /// the underlying type, we always support standard values because we need to\r
+        /// display the inheritance option.\r
+        /// </summary>\r
+        /// <returns>true</returns>\r
+        public override bool GetStandardValuesSupported(ITypeDescriptorContext context)\r
+        {\r
+            return true;\r
+        }\r
+\r
+        /// <summary>\r
+        /// Get the set of all standard values that can be chosen from a dropdown for this\r
+        /// property.  If the underlying type supports standard values, we want to include\r
+        /// all those.  Additionally, we want to display the option to inherit the value,\r
+        /// but only if the value is not already inheriting.\r
+        /// </summary>\r
+        public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)\r
+        {\r
+            List<object> Values = new List<object>();\r
+            if (Root_.GetStandardValuesSupported(context))\r
+            {\r
+                StandardValuesCollection RootValues = Root_.GetStandardValues(context);\r
+                Values.AddRange(RootValues.Cast<object>());\r
+            }\r
+            if (!Descriptor_.IsInheriting)\r
+                Values.Add(MagicInheritance.Value);\r
+            StandardValuesCollection Result = new StandardValuesCollection(Values);\r
+            return Result;\r
+        }\r
+\r
+        /// <summary>\r
+        /// Determines whether this property can accept values other than those specified\r
+        /// in the dropdown (for example by manually typing into the field).\r
+        /// </summary>\r
+        public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)\r
+        {\r
+            // Although we add items to the dropdown list, we do not change whether or not\r
+            // the set of values are exclusive.  If the user could type into the field before\r
+            // they still can.  And if they couldn't before, they still can't.\r
+            return Root_.GetStandardValuesExclusive(context);\r
+        }\r
+\r
+        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)\r
+        {\r
+            return Root_.CanConvertFrom(context, sourceType);\r
+        }\r
+\r
+        public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)\r
+        {\r
+            return Root_.CanConvertTo(context, destinationType);\r
+        }\r
+\r
+        public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)\r
+        {\r
+            if (value.Equals(MagicInheritance.Value))\r
+                return MagicInheritance.Text;\r
+            return Root_.ConvertFrom(context, culture, value);\r
+        }\r
+\r
+        public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)\r
+        {\r
+            if (value.GetType() == destinationType)\r
+                return value;\r
+\r
+            return Root_.ConvertTo(context, culture, value, destinationType);\r
+        }\r
+\r
+        public override object CreateInstance(ITypeDescriptorContext context, IDictionary propertyValues)\r
+        {\r
+            return Root_.CreateInstance(context, propertyValues);\r
+        }\r
+\r
+        public override bool Equals(object obj)\r
+        {\r
+            return Root_.Equals(obj);\r
+        }\r
+\r
+        public override bool GetCreateInstanceSupported(ITypeDescriptorContext context)\r
+        {\r
+            return Root_.GetCreateInstanceSupported(context);\r
+        }\r
+\r
+        public override int GetHashCode()\r
+        {\r
+            return Root_.GetHashCode();\r
+        }\r
+\r
+        public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)\r
+        {\r
+            return Root_.GetProperties(context, value, attributes);\r
+        }\r
+\r
+        public override bool GetPropertiesSupported(ITypeDescriptorContext context)\r
+        {\r
+            return Root_.GetPropertiesSupported(context);\r
+        }\r
+\r
+        public override bool IsValid(ITypeDescriptorContext context, object value)\r
+        {\r
+            return Root_.IsValid(context, value);\r
+        }\r
+\r
+        public override string ToString()\r
+        {\r
+            return Root_.ToString();\r
+        }\r
+    }\r
+}\r
diff --git a/clang-tidy-vs/ClangTidy/DynamicPropertyDescriptor.cs b/clang-tidy-vs/ClangTidy/DynamicPropertyDescriptor.cs
new file mode 100644 (file)
index 0000000..f0d7691
--- /dev/null
@@ -0,0 +1,137 @@
+using System;\r
+using System.Collections.Generic;\r
+using System.ComponentModel;\r
+using System.Linq;\r
+using System.Text;\r
+using System.Threading.Tasks;\r
+\r
+namespace LLVM.ClangTidy\r
+{\r
+    public class DynamicPropertyDescriptor<T> : PropertyDescriptor\r
+    {\r
+        T Value_;\r
+        DynamicPropertyDescriptor<T> Parent_;\r
+        bool IsInheriting_;\r
+        object Component_;\r
+\r
+        public DynamicPropertyDescriptor(object Component, DynamicPropertyDescriptor<T> Parent, string Name, Attribute[] Attrs)\r
+            : base(Name, Attrs)\r
+        {\r
+            foreach (DefaultValueAttribute Attr in Attrs.OfType<DefaultValueAttribute>())\r
+            {\r
+                Value_ = (T)Attr.Value;\r
+            }\r
+            Parent_ = Parent;\r
+            IsInheriting_ = true;\r
+            Component_ = Component;\r
+        }\r
+\r
+        public bool IsInheriting { get { return IsInheriting_; } set { IsInheriting_ = value; } }\r
+        public DynamicPropertyDescriptor<T> Parent { get { return Parent_; } }\r
+\r
+        /// <summary>\r
+        /// Determines whether this property's value should be considered "default" (e.g.\r
+        /// displayed in bold in the property grid).  Root properties are unmodifiable and\r
+        /// always default.  Non-root properties are default iff they are inheriting.\r
+        /// That is to say, if a property is explicitly set to False, the property should\r
+        /// be serialized even if the parent is also False.  It would only not be serialized\r
+        /// if the user had explicitly chosen to inherit it.\r
+        /// </summary>\r
+        /// <param name="component"></param>\r
+        /// <returns></returns>\r
+        public override bool ShouldSerializeValue(object component)\r
+        {\r
+            return (Parent_ != null) && !IsInheriting;\r
+        }\r
+\r
+        /// <summary>\r
+        /// Set the value back to the default.  For root properties, this essentially does\r
+        /// nothing as they are read-only anyway.  For non-root properties, this only means\r
+        /// that the property is now inheriting.\r
+        /// </summary>\r
+        /// <param name="component"></param>\r
+        public override void ResetValue(object component)\r
+        {\r
+            IsInheriting_ = true;\r
+        }\r
+\r
+        public override void SetValue(object component, object value)\r
+        {\r
+            // This is a bit of a trick.  If the user chose the inheritance option from the\r
+            // dropdown, we will try to set the value to that string.  So look for that and\r
+            // then just reset the value.\r
+            if (value.Equals(MagicInheritance.Text))\r
+                ResetValue(component);\r
+            else\r
+            {\r
+                // By explicitly setting the value, this property is no longer inheriting,\r
+                // even if the value the property is being set to is the same as that of\r
+                // the parent.\r
+                IsInheriting_ = false;\r
+                Value_ = (T)value;\r
+            }\r
+        }\r
+\r
+        public override TypeConverter Converter\r
+        {\r
+            get\r
+            {\r
+                // We need to return a DynamicPropertyConverter<> that can deal with our requirement\r
+                // to inject the inherit property option into the dropdown.  But we still need to use\r
+                // the "real" converter to do the actual work for the underlying type.  Therefore,\r
+                // we need to look for a TypeConverter<> attribute on the property, and if it is present\r
+                // forward an instance of that converter to the DynamicPropertyConverter<>.  Otherwise,\r
+                // forward an instance of the default converter for type T to the DynamicPropertyConverter<>.\r
+                TypeConverter UnderlyingConverter = null;\r
+                var ConverterAttr = this.Attributes.OfType<TypeConverterAttribute>().LastOrDefault();\r
+                if (ConverterAttr != null)\r
+                {\r
+                    Type ConverterType = Type.GetType(ConverterAttr.ConverterTypeName);\r
+                    UnderlyingConverter = (TypeConverter)Activator.CreateInstance(ConverterType);\r
+                }\r
+                else\r
+                    UnderlyingConverter = TypeDescriptor.GetConverter(typeof(T));\r
+\r
+                return new DynamicPropertyConverter<T>(this, UnderlyingConverter);\r
+            }\r
+        }\r
+\r
+        public override bool IsReadOnly\r
+        {\r
+            get\r
+            {\r
+                return (Parent_ == null);\r
+            }\r
+        }\r
+\r
+        public override Type ComponentType\r
+        {\r
+            get\r
+            {\r
+                return Component_.GetType();\r
+            }\r
+        }\r
+\r
+        public override object GetValue(object component)\r
+        {\r
+            // Return either this property's value or the parents value, depending on\r
+            // whether or not this property is inheriting.\r
+            if (IsInheriting_ && Parent != null)\r
+                return Parent.GetValue(component);\r
+            return Value_;\r
+        }\r
+\r
+        public override bool CanResetValue(object component)\r
+        {\r
+            return !IsReadOnly;\r
+        }\r
+\r
+        public override Type PropertyType\r
+        {\r
+            get\r
+            {\r
+                return typeof(T);\r
+            }\r
+        }\r
+    }\r
+}\r
diff --git a/clang-tidy-vs/ClangTidy/ForwardingPropertyDescriptor.cs b/clang-tidy-vs/ClangTidy/ForwardingPropertyDescriptor.cs
new file mode 100644 (file)
index 0000000..22b08fb
--- /dev/null
@@ -0,0 +1,191 @@
+using System;\r
+using System.Collections.Generic;\r
+using System.ComponentModel;\r
+using System.Linq;\r
+using System.Text;\r
+using System.Threading.Tasks;\r
+\r
+namespace LLVM.ClangTidy\r
+{\r
+    /// <summary>\r
+    /// A decorator of sorts.  Accepts a PropertyDescriptor to its constructor\r
+    /// and forwards all calls to the underlying PropertyDescriptor.  In this way\r
+    /// we can inherit from ForwardingPropertyDescriptor and override only the\r
+    /// few methods we need to customize the behavior of, while allowing the\r
+    /// underlying PropertyDescriptor to do the real work.\r
+    /// </summary>\r
+    public abstract class ForwardingPropertyDescriptor : PropertyDescriptor\r
+    {\r
+        private readonly PropertyDescriptor root;\r
+        protected PropertyDescriptor Root { get { return root; } }\r
+        protected ForwardingPropertyDescriptor(PropertyDescriptor root)\r
+            : base(root)\r
+        {\r
+            this.root = root;\r
+        }\r
+\r
+        public override void AddValueChanged(object component, EventHandler handler)\r
+        {\r
+            root.AddValueChanged(component, handler);\r
+        }\r
+\r
+        public override AttributeCollection Attributes\r
+        {\r
+            get\r
+            {\r
+                return root.Attributes;\r
+            }\r
+        }\r
+\r
+        public override bool CanResetValue(object component)\r
+        {\r
+            return root.CanResetValue(component);\r
+        }\r
+\r
+        public override string Category\r
+        {\r
+            get\r
+            {\r
+                return root.Category;\r
+            }\r
+        }\r
+\r
+        public override Type ComponentType\r
+        {\r
+            get\r
+            {\r
+                return root.ComponentType;\r
+            }\r
+        }\r
+\r
+        public override TypeConverter Converter\r
+        {\r
+            get\r
+            {\r
+                return root.Converter;\r
+            }\r
+        }\r
+\r
+        public override string Description\r
+        {\r
+            get\r
+            {\r
+                return root.Description;\r
+            }\r
+        }\r
+\r
+        public override bool DesignTimeOnly\r
+        {\r
+            get\r
+            {\r
+                return root.DesignTimeOnly;\r
+            }\r
+        }\r
+\r
+        public override string DisplayName\r
+        {\r
+            get\r
+            {\r
+                return root.DisplayName;\r
+            }\r
+        }\r
+\r
+        public override bool Equals(object obj)\r
+        {\r
+            return root.Equals(obj);\r
+        }\r
+\r
+        public override PropertyDescriptorCollection GetChildProperties(object instance, Attribute[] filter)\r
+        {\r
+            return root.GetChildProperties(instance, filter);\r
+        }\r
+\r
+        public override object GetEditor(Type editorBaseType)\r
+        {\r
+            return root.GetEditor(editorBaseType);\r
+        }\r
+\r
+        public override int GetHashCode()\r
+        {\r
+            return root.GetHashCode();\r
+        }\r
+\r
+        public override object GetValue(object component)\r
+        {\r
+            return root.GetValue(component);\r
+        }\r
+\r
+        public override bool IsBrowsable\r
+        {\r
+            get\r
+            {\r
+                return root.IsBrowsable;\r
+            }\r
+        }\r
+\r
+        public override bool IsLocalizable\r
+        {\r
+            get\r
+            {\r
+                return root.IsLocalizable;\r
+            }\r
+        }\r
+\r
+        public override bool IsReadOnly\r
+        {\r
+            get\r
+            {\r
+                return root.IsReadOnly;\r
+            }\r
+        }\r
+\r
+        public override string Name\r
+        {\r
+            get\r
+            {\r
+                return root.Name;\r
+            }\r
+        }\r
+\r
+        public override Type PropertyType\r
+        {\r
+            get\r
+            {\r
+                return root.PropertyType;\r
+            }\r
+        }\r
+\r
+        public override void RemoveValueChanged(object component, EventHandler handler)\r
+        {\r
+            root.RemoveValueChanged(component, handler);\r
+        }\r
+\r
+        public override void ResetValue(object component)\r
+        {\r
+            root.ResetValue(component);\r
+        }\r
+\r
+        public override void SetValue(object component, object value)\r
+        {\r
+            root.SetValue(component, value);\r
+        }\r
+\r
+        public override bool ShouldSerializeValue(object component)\r
+        {\r
+            return root.ShouldSerializeValue(component);\r
+        }\r
+\r
+        public override bool SupportsChangeEvents\r
+        {\r
+            get\r
+            {\r
+                return root.SupportsChangeEvents;\r
+            }\r
+        }\r
+\r
+        public override string ToString()\r
+        {\r
+            return root.ToString();\r
+        }\r
+    }\r
+}\r
diff --git a/clang-tidy-vs/ClangTidy/GlobalSuppressions.cs b/clang-tidy-vs/ClangTidy/GlobalSuppressions.cs
new file mode 100644 (file)
index 0000000..175a74e
--- /dev/null
@@ -0,0 +1,11 @@
+// This file is used by Code Analysis to maintain SuppressMessage\r
+// attributes that are applied to this project. Project-level\r
+// suppressions either have no target or are given a specific target\r
+// and scoped to a namespace, type, member, etc.\r
+//\r
+// To add a suppression to this file, right-click the message in the\r
+// Error List, point to "Suppress Message(s)", and click "In Project\r
+// Suppression File". You do not need to add suppressions to this\r
+// file manually.\r
+\r
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1017:MarkAssembliesWithComVisible")]\r
diff --git a/clang-tidy-vs/ClangTidy/Guids.cs b/clang-tidy-vs/ClangTidy/Guids.cs
new file mode 100644 (file)
index 0000000..0c99a6f
--- /dev/null
@@ -0,0 +1,12 @@
+using System;\r
+\r
+namespace LLVM.ClangTidy\r
+{\r
+    static class GuidList\r
+    {\r
+        public const string guidClangTidyPkgString = "AE4956BE-3DB8-430E-BBAB-7E2E9A014E9C";\r
+        public const string guidClangTidyCmdSetString = "9E0F0493-6493-46DE-AEE1-ACD8F60F265E";\r
+\r
+        public static readonly Guid guidClangTidyCmdSet = new Guid(guidClangTidyCmdSetString);\r
+    };\r
+}\r
diff --git a/clang-tidy-vs/ClangTidy/PkgCmdID.cs b/clang-tidy-vs/ClangTidy/PkgCmdID.cs
new file mode 100644 (file)
index 0000000..3faf403
--- /dev/null
@@ -0,0 +1,7 @@
+namespace LLVM.ClangTidy\r
+{\r
+    static class PkgCmdIDList\r
+    {\r
+        public const uint cmdidClangTidy = 0x100;\r
+    };\r
+}
\ No newline at end of file
diff --git a/clang-tidy-vs/ClangTidy/Properties/AssemblyInfo.cs b/clang-tidy-vs/ClangTidy/Properties/AssemblyInfo.cs
new file mode 100644 (file)
index 0000000..7105305
--- /dev/null
@@ -0,0 +1,33 @@
+using System;\r
+using System.Reflection;\r
+using System.Resources;\r
+using System.Runtime.CompilerServices;\r
+using System.Runtime.InteropServices;\r
+\r
+// General Information about an assembly is controlled through the following \r
+// set of attributes. Change these attribute values to modify the information\r
+// associated with an assembly.\r
+[assembly: AssemblyTitle("ClangFormat")]\r
+[assembly: AssemblyDescription("")]\r
+[assembly: AssemblyConfiguration("")]\r
+[assembly: AssemblyCompany("LLVM")]\r
+[assembly: AssemblyProduct("ClangFormat")]\r
+[assembly: AssemblyCopyright("")]\r
+[assembly: AssemblyTrademark("")]\r
+[assembly: AssemblyCulture("")]\r
+[assembly: ComVisible(false)]\r
+[assembly: CLSCompliant(false)]\r
+[assembly: NeutralResourcesLanguage("en-US")]\r
+\r
+// Version information for an assembly consists of the following four values:\r
+//\r
+//      Major Version\r
+//      Minor Version \r
+//      Build Number\r
+//      Revision\r
+//\r
+// You can specify all the values or you can default the Revision and Build Numbers \r
+// by using the '*' as shown below:\r
+// FIXME: Add a way to have this generated automatically by CMake\r
+[assembly: AssemblyVersion("1.1.0.0")]\r
+[assembly: AssemblyFileVersion("1.1.0.0")]\r
diff --git a/clang-tidy-vs/ClangTidy/Resources.Designer.cs b/clang-tidy-vs/ClangTidy/Resources.Designer.cs
new file mode 100644 (file)
index 0000000..b601fb3
--- /dev/null
@@ -0,0 +1,81 @@
+//------------------------------------------------------------------------------\r
+// <auto-generated>\r
+//     This code was generated by a tool.\r
+//     Runtime Version:4.0.30319.42000\r
+//\r
+//     Changes to this file may cause incorrect behavior and will be lost if\r
+//     the code is regenerated.\r
+// </auto-generated>\r
+//------------------------------------------------------------------------------\r
+\r
+namespace LLVM.ClangTidy {\r
+    using System;\r
+    \r
+    \r
+    /// <summary>\r
+    ///   A strongly-typed resource class, for looking up localized strings, etc.\r
+    /// </summary>\r
+    // This class was auto-generated by the StronglyTypedResourceBuilder\r
+    // class via a tool like ResGen or Visual Studio.\r
+    // To add or remove a member, edit your .ResX file then rerun ResGen\r
+    // with the /str option, or rebuild your VS project.\r
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]\r
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]\r
+    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]\r
+    internal class Resources {\r
+        \r
+        private static global::System.Resources.ResourceManager resourceMan;\r
+        \r
+        private static global::System.Globalization.CultureInfo resourceCulture;\r
+        \r
+        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]\r
+        internal Resources() {\r
+        }\r
+        \r
+        /// <summary>\r
+        ///   Returns the cached ResourceManager instance used by this class.\r
+        /// </summary>\r
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]\r
+        internal static global::System.Resources.ResourceManager ResourceManager {\r
+            get {\r
+                if (object.ReferenceEquals(resourceMan, null)) {\r
+                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("LLVM.ClangTidy.Resources", typeof(Resources).Assembly);\r
+                    resourceMan = temp;\r
+                }\r
+                return resourceMan;\r
+            }\r
+        }\r
+        \r
+        /// <summary>\r
+        ///   Overrides the current thread's CurrentUICulture property for all\r
+        ///   resource lookups using this strongly typed resource class.\r
+        /// </summary>\r
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]\r
+        internal static global::System.Globalization.CultureInfo Culture {\r
+            get {\r
+                return resourceCulture;\r
+            }\r
+            set {\r
+                resourceCulture = value;\r
+            }\r
+        }\r
+        \r
+        /// <summary>\r
+        ///   Looks up a localized string similar to ---\r
+        ///Checks:\r
+        ///Checks:\r
+        ///  - Name:        cert-dcl54-cpp\r
+        ///    Label:       Overloaded allocation function pairs\r
+        ///    Description: Checks for violations of CERT DCL54-CPP - Overload allocation and deallocation functions as a pair in the same scope\r
+        ///    Category:    CERT Secure Coding Standards\r
+        ///  - Name:        cppcoreguidelines-interfaces-global-init\r
+        ///    Label:       I.22 - Complex Global Initializers\r
+        ///    Description: Checks for violations of Core Guideline I.22 - Avoid complex initializers of global object [rest of string was truncated]&quot;;.\r
+        /// </summary>\r
+        internal static string ClangTidyChecks {\r
+            get {\r
+                return ResourceManager.GetString("ClangTidyChecks", resourceCulture);\r
+            }\r
+        }\r
+    }\r
+}\r
diff --git a/clang-tidy-vs/ClangTidy/Resources.resx b/clang-tidy-vs/ClangTidy/Resources.resx
new file mode 100644 (file)
index 0000000..7f63475
--- /dev/null
@@ -0,0 +1,124 @@
+<?xml version="1.0" encoding="utf-8"?>\r
+<root>\r
+  <!-- \r
+    Microsoft ResX Schema \r
+    \r
+    Version 2.0\r
+    \r
+    The primary goals of this format is to allow a simple XML format \r
+    that is mostly human readable. The generation and parsing of the \r
+    various data types are done through the TypeConverter classes \r
+    associated with the data types.\r
+    \r
+    Example:\r
+    \r
+    ... ado.net/XML headers & schema ...\r
+    <resheader name="resmimetype">text/microsoft-resx</resheader>\r
+    <resheader name="version">2.0</resheader>\r
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\r
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\r
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>\r
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>\r
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">\r
+        <value>[base64 mime encoded serialized .NET Framework object]</value>\r
+    </data>\r
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">\r
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\r
+        <comment>This is a comment</comment>\r
+    </data>\r
+                \r
+    There are any number of "resheader" rows that contain simple \r
+    name/value pairs.\r
+    \r
+    Each data row contains a name, and value. The row also contains a \r
+    type or mimetype. Type corresponds to a .NET class that support \r
+    text/value conversion through the TypeConverter architecture. \r
+    Classes that don't support this are serialized and stored with the \r
+    mimetype set.\r
+    \r
+    The mimetype is used for serialized objects, and tells the \r
+    ResXResourceReader how to depersist the object. This is currently not \r
+    extensible. For a given mimetype the value must be set accordingly:\r
+    \r
+    Note - application/x-microsoft.net.object.binary.base64 is the format \r
+    that the ResXResourceWriter will generate, however the reader can \r
+    read any of the formats listed below.\r
+    \r
+    mimetype: application/x-microsoft.net.object.binary.base64\r
+    value   : The object must be serialized with \r
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter\r
+            : and then encoded with base64 encoding.\r
+    \r
+    mimetype: application/x-microsoft.net.object.soap.base64\r
+    value   : The object must be serialized with \r
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\r
+            : and then encoded with base64 encoding.\r
+\r
+    mimetype: application/x-microsoft.net.object.bytearray.base64\r
+    value   : The object must be serialized into a byte array \r
+            : using a System.ComponentModel.TypeConverter\r
+            : and then encoded with base64 encoding.\r
+    -->\r
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">\r
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />\r
+    <xsd:element name="root" msdata:IsDataSet="true">\r
+      <xsd:complexType>\r
+        <xsd:choice maxOccurs="unbounded">\r
+          <xsd:element name="metadata">\r
+            <xsd:complexType>\r
+              <xsd:sequence>\r
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />\r
+              </xsd:sequence>\r
+              <xsd:attribute name="name" use="required" type="xsd:string" />\r
+              <xsd:attribute name="type" type="xsd:string" />\r
+              <xsd:attribute name="mimetype" type="xsd:string" />\r
+              <xsd:attribute ref="xml:space" />\r
+            </xsd:complexType>\r
+          </xsd:element>\r
+          <xsd:element name="assembly">\r
+            <xsd:complexType>\r
+              <xsd:attribute name="alias" type="xsd:string" />\r
+              <xsd:attribute name="name" type="xsd:string" />\r
+            </xsd:complexType>\r
+          </xsd:element>\r
+          <xsd:element name="data">\r
+            <xsd:complexType>\r
+              <xsd:sequence>\r
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />\r
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />\r
+              </xsd:sequence>\r
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />\r
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />\r
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />\r
+              <xsd:attribute ref="xml:space" />\r
+            </xsd:complexType>\r
+          </xsd:element>\r
+          <xsd:element name="resheader">\r
+            <xsd:complexType>\r
+              <xsd:sequence>\r
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />\r
+              </xsd:sequence>\r
+              <xsd:attribute name="name" type="xsd:string" use="required" />\r
+            </xsd:complexType>\r
+          </xsd:element>\r
+        </xsd:choice>\r
+      </xsd:complexType>\r
+    </xsd:element>\r
+  </xsd:schema>\r
+  <resheader name="resmimetype">\r
+    <value>text/microsoft-resx</value>\r
+  </resheader>\r
+  <resheader name="version">\r
+    <value>2.0</value>\r
+  </resheader>\r
+  <resheader name="reader">\r
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\r
+  </resheader>\r
+  <resheader name="writer">\r
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\r
+  </resheader>\r
+  <assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />\r
+  <data name="ClangTidyChecks" type="System.Resources.ResXFileRef, System.Windows.Forms">\r
+    <value>Resources\ClangTidyChecks.yaml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252</value>\r
+  </data>\r
+</root>
\ No newline at end of file
diff --git a/clang-tidy-vs/ClangTidy/Resources/ClangTidyChecks.yaml b/clang-tidy-vs/ClangTidy/Resources/ClangTidyChecks.yaml
new file mode 100644 (file)
index 0000000..d35e81d
--- /dev/null
@@ -0,0 +1,321 @@
+---\r
+Checks:\r
+   # This file should be updated when new checks are added, and eventually we should\r
+   # generate this file automatically from the .rst files in clang-tidy.\r
+  - Category:    CERT Secure Coding Standards\r
+    Label:       Overloaded allocation function pairs\r
+    Description: Checks for violations of CERT DCL54-CPP - Overload allocation and deallocation functions as a pair in the same scope\r
+    Name:        cert-dcl54-cpp\r
+  - Category:    C++ Core Guidelines\r
+    Label:       I.22 - Complex Global Initializers\r
+    Description: Checks for violations of Core Guideline I.22 - Avoid complex initializers of global objects\r
+    Name:        cppcoreguidelines-interfaces-global-init\r
+  - Category:    CERT Secure Coding Standards\r
+    Label:       DCL50-CPP\r
+    Description: Checks for violations of CERT DCL50-CPP - Do not define a C-style variadic function\r
+    Name:        cert-dcl50-cpp\r
+  - Category:    C++ Core Guidelines\r
+    Label:       Bounds.1 - No pointer arithmetic\r
+    Description: Checks for violations of Core Guideline Bounds.3 - Don't use pointer arithmetic.  Use span<> instead.\r
+    Name:        cppcoreguidelines-pro-bounds-pointer-arithmetic\r
+  - Category:    C++ Core Guidelines\r
+    Label:       Bounds.2 - Constant array indices\r
+    Description: Checks for violations of Core Bounds.2 - Only index into arrays using constant expressions.\r
+    Name:        cppcoreguidelines-pro-bounds-constant-array-index\r
+  - Category:    C++ Core Guidelines\r
+    Label:       Bounds.3 - Array to Pointer Decay\r
+    Description: Checks for violations of Core Guideline Bounds.3 - No array-to-pointer decay\r
+    Name:        cppcoreguidelines-pro-bounds-array-to-pointer-decay\r
+  - Category:    C++ Core Guidelines\r
+    Label:       const_cast (Type.3)\r
+    Description: Checks for violations of Core Guideline Type.3 - Don't use const_cast to cast away const\r
+    Name:        cppcoreguidelines-pro-type-const-cast\r
+  - Category:    C++ Core Guidelines\r
+    Label: C style casts (Type.4)\r
+    Description: Checks for violations of Core Guideline Type.3 - Don't use C-style (T)expression casts that would perform a static downcast, const_cast, or reinterpret_cast\r
+    Name:        cppcoreguidelines-pro-type-cstyle-cast\r
+  - Category:    C++ Core Guidelines\r
+    Label:       reinterpret_cast (Type.1)\r
+    Description: Checks for violations of Core Guideline Type.1 - Don't use reinterpret_cast.\r
+    Name:        cppcoreguidelines-pro-type-reinterpret-cast\r
+  - Category:    C++ Core Guidelines\r
+    Label:       Prefer dynamic_cast (Type.2)\r
+    Description: Checks for violations of Core Guideline Type.2 - Don't use static_cast downcasts.  Use dynamic_cast instead.\r
+    Name:        cppcoreguidelines-pro-type-static-cast-downcast\r
+  - Category:    C++ Core Guidelines\r
+    Label:       Member variable initialization (Type.6)\r
+    Description: Checks for violations of Core Guideline Type.6 - Always initialize a member variable.\r
+    Name:        cppcoreguidelines-pro-type-member-init\r
+  - Category:    C++ Core Guidelines\r
+    Label:       Avoid unions (Type.7)\r
+    Description: Checks for violations of Core Guideline Type.7 - Avoid accessing members of raw unions.  Use variant instead.\r
+    Name:        cppcoreguidelines-pro-type-union-access\r
+  - Category:    C++ Core Guidelines\r
+    Label:       Don't use varargs (Type.8)\r
+    Description: Checks for violations of Core Guideline Type.8 - Avoid reading varargs or passing vararg arguments.  Prefer variadic templates instead.\r
+    Name:        cppcoreguidelines-pro-type-vararg\r
+  - Category:    C++ Core Guidelines\r
+    Label:       Don't slice (ES.63 & C.145)\r
+    Description: Checks for violations of Core Guidelines ES.63 (Don't slice) and C.145 (Access polymorphic objects through pointers and references)\r
+    Name:        cppcoreguidelines-slicing\r
+  - Category:    C++ Core Guidelines\r
+    Label:       Detect unsafe special functions (C.21)\r
+    Description: Checks for violations of Core Guidelines C.21 - If you define or =delete any default operation, define or =delete them all.\r
+    Name:        cppcoreguidelines-special-member-functions\r
+  - Category:    Google Style Guide\r
+    Label:       Forbid explicitly parameterized make_pair\r
+    Description: \r
+    Name:        google-build-explicit-make-pair\r
+  - Category:    Google Style Guide\r
+    Label:       Anonymous namespace in headers\r
+    Description: \r
+    Name:        google-build-namespaces\r
+  - Category:    Google Style Guide\r
+    Label:       Find using namespace directives\r
+    Description: \r
+    Name:        google-build-using-namespace\r
+  - Category:    Google Style Guide\r
+    Label:       Default arguments in virtual methods\r
+    Description: \r
+    Name:        google-default-arguments\r
+  - Category:    Google Style Guide\r
+    Label:       explicit constructors\r
+    Description: \r
+    Name:        google-explicit-constructor\r
+  - Category:    Google Style Guide\r
+    Label:       Global namespace pollution in headers\r
+    Description: \r
+    Name:        google-global-names-in-headers\r
+  - Category:    Google Style Guide\r
+    Label:       Braces around statements\r
+    Description: \r
+    Name:        google-readability-braces-around-statements\r
+  - Category:    Google Style Guide\r
+    Label:       No C-style casts\r
+    Description: \r
+    Name:        google-readability-casting\r
+  - Category:    Google Style Guide\r
+    Label:       Find large functions\r
+    Description: \r
+    Name:        google-readability-function-size\r
+  - Category:    Google Style Guide\r
+    Label:       Namespace closing comments\r
+    Description: \r
+    Name:        google-readability-namespace-comments\r
+  - Category:    Google Style Guide\r
+    Label:       Find unnecessary calls to .get()\r
+    Description: \r
+    Name:        google-readability-redundant-smartptr-get\r
+  - Category:    Google Style Guide\r
+    Label:       Find noncomformant TODO comments\r
+    Description: \r
+    Name:        google-readability-todo\r
+  - Category:    Google Style Guide\r
+    Label:       Find implementation-specific integral types\r
+    Description: \r
+    Name:        google-runtime-int\r
+  - Category:    Google Style Guide\r
+    Label:       Find const string references\r
+    Description: \r
+    Name:        google-runtime-member-string-references\r
+  - Category:    Google Style Guide\r
+    Label:       Find zero-length memsets\r
+    Description: \r
+    Name:        google-runtime-memset\r
+  - Category:    Google Style Guide\r
+    Label:       Find overloads of operator&\r
+    Description: \r
+    Name:        google-runtime-operator\r
+  - Category:    Google Style Guide\r
+    Label:       Check usage of non-const references\r
+    Description: \r
+    Name:        google-runtime-references\r
+  - Category:    LLVM Style Guide\r
+    Label:       LLVM header guards\r
+    Description: \r
+    Name:        llvm-header-guard\r
+  - Category:    LLVM Style Guide\r
+    Label:       LLVM include order\r
+    Description: \r
+    Name:        llvm-include-order\r
+  - Category:    LLVM Style Guide\r
+    Label:       LLVM namespace comments\r
+    Description: \r
+    Name:        llvm-namespace-comment\r
+  - Category:    LLVM Style Guide\r
+    Label:       Find local twines\r
+    Description: \r
+    Name:        llvm-twine-local\r
+  - Category:    Clang Diagnostics\r
+    Label:       Warnings\r
+    Description: \r
+    Name:        clang-diagnostic-warning\r
+  - Category:    Clang Diagnostics\r
+    Label:       Errors\r
+    Description: \r
+    Name:        clang-diagnostic-error\r
+  - Category:    Clang Diagnostics\r
+    Label:       Unknown\r
+    Description: \r
+    Name:        clang-diagnostic-unknown\r
+  - Category:    Miscellaneous\r
+    Label:       Validate argument comments\r
+    Description: \r
+    Name:        misc-argument-comment\r
+  - Category:    Miscellaneous\r
+    Label:       Side effects in assert()\r
+    Description: \r
+    Name:        misc-assert-side-effect\r
+  - Category:    Miscellaneous\r
+    Label:       bool / pointer implicit conversions\r
+    Description: \r
+    Name:        misc-bool-pointer-implicit-conversion\r
+  - Category:    Miscellaneous\r
+    Label:       Dangling handles\r
+    Description: \r
+    Name:        misc-dangling-handle\r
+  - Category:    Miscellaneous\r
+    Label:       Definitions in headers\r
+    Description: \r
+    Name:        misc-definitions-in-headers\r
+  - Category:    Miscellaneous\r
+    Label:       Type mismatch in fold operations\r
+    Description: \r
+    Name:        misc-fold-init-type\r
+  - Category:    Miscellaneous\r
+    Label:       Forward declaration namespace\r
+    Description: \r
+    Name:        misc-forward-declaration-namespace\r
+  - Category:    Miscellaneous\r
+    Label:       Inaccurate erase\r
+    Description: \r
+    Name:        misc-inaccurate-erase\r
+  - Category:    Miscellaneous\r
+    Label:       Incorrect rounding\r
+    Description: \r
+    Name:        misc-incorrect-roundings\r
+  - Category:    Miscellaneous\r
+    Label:       Inefficient STL algorithms\r
+    Description: \r
+    Name:        misc-inefficient-algorithm\r
+  - Category:    Miscellaneous\r
+    Label:       Macro parentheses\r
+    Description: \r
+    Name:        misc-macro-parentheses\r
+  - Category:    Miscellaneous\r
+    Label:       Macro repeated side effects\r
+    Description: \r
+    Name:        misc-macro-repeated-side-effects\r
+  - Category:    Miscellaneous\r
+    Label:       Misplaced const\r
+    Description: \r
+    Name:        misc-misplaced-const\r
+  - Category:    Miscellaneous\r
+    Label:       Misplaced widening casts\r
+    Description: \r
+    Name:        misc-misplaced-widening-cast\r
+  - Category:    Miscellaneous\r
+    Label:       Move constructor const arguments\r
+    Description: \r
+    Name:        misc-move-const-arg\r
+  - Category:    Miscellaneous\r
+    Label:       Move constructor initialization\r
+    Description: \r
+    Name:        misc-move-constructor-init\r
+  - Category:    Miscellaneous\r
+    Label:       Multi-statement macros\r
+    Description: \r
+    Name:        misc-multiple-statement-macro\r
+  - Category:    Miscellaneous\r
+    Label:       Verify new / delete overloads\r
+    Description: \r
+    Name:        misc-new-delete-overloads\r
+  - Category:    Miscellaneous\r
+    Label:       Ensure move constructors are noexcept\r
+    Description: \r
+    Name:        misc-noexcept-move-constructor\r
+  - Category:    Miscellaneous\r
+    Label:       Copying of non-copyable objects\r
+    Description: \r
+    Name:        misc-non-copyable-objects\r
+  - Category:    Miscellaneous\r
+    Label:       Find redundant expressions\r
+    Description: \r
+    Name:        misc-redundant-expression\r
+  - Category:    Miscellaneous\r
+    Label:       sizeof() on stl containers\r
+    Description: \r
+    Name:        misc-sizeof-container\r
+  - Category:    Miscellaneous\r
+    Label:       Suspicious sizeof() usage\r
+    Description: \r
+    Name:        misc-sizeof-expression\r
+  - Category:    Miscellaneous\r
+    Label:       Replace assert with static_assert\r
+    Description: \r
+    Name:        misc-static-assert\r
+  - Category:    Miscellaneous\r
+    Label:       Suspicious string constructor\r
+    Description: \r
+    Name:        misc-string-constructor\r
+  - Category:    Miscellaneous\r
+    Label:       String integer assignment\r
+    Description: \r
+    Name:        misc-string-integer-assignment\r
+  - Category:    Miscellaneous\r
+    Label:       String literal with embedded null\r
+    Description: \r
+    Name:        misc-string-literal-with-embedded-nul\r
+  - Category:    Miscellaneous\r
+    Label:       Suspicious missing comma\r
+    Description: \r
+    Name:        misc-suspicious-missing-comma\r
+  - Category:    Miscellaneous\r
+    Label:       Suspicious semicolon\r
+    Description: \r
+    Name:        misc-suspicious-semicolon\r
+  - Category:    Miscellaneous\r
+    Label:       Suspicious string compare\r
+    Description: \r
+    Name:        misc-suspicious-string-compare\r
+  - Category:    Miscellaneous\r
+    Label:       Swapped arguments\r
+    Description: \r
+    Name:        misc-swapped-arguments\r
+  - Category:    Miscellaneous\r
+    Label:       Throw by value / catch by reference\r
+    Description: \r
+    Name:        misc-throw-by-value-catch-by-reference\r
+  - Category:    Miscellaneous\r
+    Label:       Unconventional operator=()\r
+    Description: \r
+    Name:        misc-unconventional-assign-operator\r
+  - Category:    Miscellaneous\r
+    Label:       Undelegated constructor\r
+    Description: \r
+    Name:        misc-undelegated-constructor\r
+  - Category:    Miscellaneous\r
+    Label:       unique_ptr<> reset / release\r
+    Description: \r
+    Name:        misc-uniqueptr-reset-release\r
+  - Category:    Miscellaneous\r
+    Label:       Unused Alias Decls\r
+    Description: \r
+    Name:        misc-unused-alias-decls\r
+  - Category:    Miscellaneous\r
+    Label:       Unused Params\r
+    Description: \r
+    Name:        misc-unused-parameters\r
+  - Category:    Miscellaneous\r
+    Label:       Unused Raii\r
+    Description: \r
+    Name:        misc-unused-raii\r
+  - Category:    Miscellaneous\r
+    Label:       Unused Using Decls\r
+    Description: \r
+    Name:        misc-unused-using-decls\r
+  - Category:    Miscellaneous\r
+    Label:       Virtual Near Miss\r
+    Description: \r
+    Name:        misc-virtual-near-miss\r
+...\r
diff --git a/clang-tidy-vs/ClangTidy/Resources/Images_32bit.bmp b/clang-tidy-vs/ClangTidy/Resources/Images_32bit.bmp
new file mode 100644 (file)
index 0000000..2fa7ab0
Binary files /dev/null and b/clang-tidy-vs/ClangTidy/Resources/Images_32bit.bmp differ
diff --git a/clang-tidy-vs/ClangTidy/Resources/Package.ico b/clang-tidy-vs/ClangTidy/Resources/Package.ico
new file mode 100644 (file)
index 0000000..ea3b23f
Binary files /dev/null and b/clang-tidy-vs/ClangTidy/Resources/Package.ico differ
diff --git a/clang-tidy-vs/ClangTidy/Utility.cs b/clang-tidy-vs/ClangTidy/Utility.cs
new file mode 100644 (file)
index 0000000..5a220e6
--- /dev/null
@@ -0,0 +1,35 @@
+using System;\r
+using System.Collections.Generic;\r
+using System.IO;\r
+using System.Linq;\r
+using System.Text;\r
+using System.Text.RegularExpressions;\r
+using System.Threading.Tasks;\r
+\r
+namespace LLVM.ClangTidy\r
+{\r
+    static class Utility\r
+    {\r
+        public static IEnumerable<string> SplitPath(string FileOrDir)\r
+        {\r
+            string P = Path.GetDirectoryName(FileOrDir);\r
+            do\r
+            {\r
+                yield return P;\r
+                P = Path.GetDirectoryName(P);\r
+            } while (P != null);\r
+        }\r
+\r
+        public static bool HasClangTidyFile(string Folder)\r
+        {\r
+            string ClangTidy = Path.Combine(Folder, ".clang-tidy");\r
+            return File.Exists(ClangTidy);\r
+        }\r
+\r
+        public static bool MatchWildcardString(string Value, string Pattern)\r
+        {\r
+            string RE = Regex.Escape(Pattern).Replace(@"\*", ".*");\r
+            return Regex.IsMatch(Value, RE);\r
+        }\r
+    }\r
+}\r
diff --git a/clang-tidy-vs/ClangTidy/VSPackage.resx b/clang-tidy-vs/ClangTidy/VSPackage.resx
new file mode 100644 (file)
index 0000000..932b062
--- /dev/null
@@ -0,0 +1,130 @@
+<?xml version="1.0" encoding="utf-8"?>\r
+<root>\r
+  <!-- \r
+    Microsoft ResX Schema \r
+    \r
+    Version 2.0\r
+    \r
+    The primary goals of this format is to allow a simple XML format \r
+    that is mostly human readable. The generation and parsing of the \r
+    various data types are done through the TypeConverter classes \r
+    associated with the data types.\r
+    \r
+    Example:\r
+    \r
+    ... ado.net/XML headers & schema ...\r
+    <resheader name="resmimetype">text/microsoft-resx</resheader>\r
+    <resheader name="version">2.0</resheader>\r
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\r
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\r
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>\r
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>\r
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">\r
+        <value>[base64 mime encoded serialized .NET Framework object]</value>\r
+    </data>\r
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">\r
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\r
+        <comment>This is a comment</comment>\r
+    </data>\r
+                \r
+    There are any number of "resheader" rows that contain simple \r
+    name/value pairs.\r
+    \r
+    Each data row contains a name, and value. The row also contains a \r
+    type or mimetype. Type corresponds to a .NET class that support \r
+    text/value conversion through the TypeConverter architecture. \r
+    Classes that don't support this are serialized and stored with the \r
+    mimetype set.\r
+    \r
+    The mimetype is used for serialized objects, and tells the \r
+    ResXResourceReader how to depersist the object. This is currently not \r
+    extensible. For a given mimetype the value must be set accordingly:\r
+    \r
+    Note - application/x-microsoft.net.object.binary.base64 is the format \r
+    that the ResXResourceWriter will generate, however the reader can \r
+    read any of the formats listed below.\r
+    \r
+    mimetype: application/x-microsoft.net.object.binary.base64\r
+    value   : The object must be serialized with \r
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter\r
+            : and then encoded with base64 encoding.\r
+    \r
+    mimetype: application/x-microsoft.net.object.soap.base64\r
+    value   : The object must be serialized with \r
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\r
+            : and then encoded with base64 encoding.\r
+\r
+    mimetype: application/x-microsoft.net.object.bytearray.base64\r
+    value   : The object must be serialized into a byte array \r
+            : using a System.ComponentModel.TypeConverter\r
+            : and then encoded with base64 encoding.\r
+    -->\r
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">\r
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />\r
+    <xsd:element name="root" msdata:IsDataSet="true">\r
+      <xsd:complexType>\r
+        <xsd:choice maxOccurs="unbounded">\r
+          <xsd:element name="metadata">\r
+            <xsd:complexType>\r
+              <xsd:sequence>\r
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />\r
+              </xsd:sequence>\r
+              <xsd:attribute name="name" use="required" type="xsd:string" />\r
+              <xsd:attribute name="type" type="xsd:string" />\r
+              <xsd:attribute name="mimetype" type="xsd:string" />\r
+              <xsd:attribute ref="xml:space" />\r
+            </xsd:complexType>\r
+          </xsd:element>\r
+          <xsd:element name="assembly">\r
+            <xsd:complexType>\r
+              <xsd:attribute name="alias" type="xsd:string" />\r
+              <xsd:attribute name="name" type="xsd:string" />\r
+            </xsd:complexType>\r
+          </xsd:element>\r
+          <xsd:element name="data">\r
+            <xsd:complexType>\r
+              <xsd:sequence>\r
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />\r
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />\r
+              </xsd:sequence>\r
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />\r
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />\r
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />\r
+              <xsd:attribute ref="xml:space" />\r
+            </xsd:complexType>\r
+          </xsd:element>\r
+          <xsd:element name="resheader">\r
+            <xsd:complexType>\r
+              <xsd:sequence>\r
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />\r
+              </xsd:sequence>\r
+              <xsd:attribute name="name" type="xsd:string" use="required" />\r
+            </xsd:complexType>\r
+          </xsd:element>\r
+        </xsd:choice>\r
+      </xsd:complexType>\r
+    </xsd:element>\r
+  </xsd:schema>\r
+  <resheader name="resmimetype">\r
+    <value>text/microsoft-resx</value>\r
+  </resheader>\r
+  <resheader name="version">\r
+    <value>2.0</value>\r
+  </resheader>\r
+  <resheader name="reader">\r
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\r
+  </resheader>\r
+  <resheader name="writer">\r
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\r
+  </resheader>\r
+  <data name="110" xml:space="preserve">\r
+    <value>ClangTidy</value>\r
+  </data>\r
+  <data name="112" xml:space="preserve">\r
+    <value>Analyzes code by calling the clang-tidy executable.</value>\r
+  </data>\r
+  <assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />\r
+  <data name="400" type="System.Resources.ResXFileRef, System.Windows.Forms">\r
+    <value>Resources\Package.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>\r
+  </data>\r
+</root>
\ No newline at end of file
diff --git a/clang-tidy-vs/ClangTidy/license.txt b/clang-tidy-vs/ClangTidy/license.txt
new file mode 100644 (file)
index 0000000..b452ca2
--- /dev/null
@@ -0,0 +1,63 @@
+==============================================================================
+LLVM Release License
+==============================================================================
+University of Illinois/NCSA
+Open Source License
+
+Copyright (c) 2007-2016 University of Illinois at Urbana-Champaign.
+All rights reserved.
+
+Developed by:
+
+    LLVM Team
+
+    University of Illinois at Urbana-Champaign
+
+    http://llvm.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal with
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+    * Redistributions of source code must retain the above copyright notice,
+      this list of conditions and the following disclaimers.
+
+    * Redistributions in binary form must reproduce the above copyright notice,
+      this list of conditions and the following disclaimers in the
+      documentation and/or other materials provided with the distribution.
+
+    * Neither the names of the LLVM Team, University of Illinois at
+      Urbana-Champaign, nor the names of its contributors may be used to
+      endorse or promote products derived from this Software without specific
+      prior written permission.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
+SOFTWARE.
+
+==============================================================================
+The LLVM software contains code written by third parties.  Such software will
+have its own individual LICENSE.TXT file in the directory in which it appears.
+This file will describe the copyrights, license, and restrictions which apply
+to that code.
+
+The disclaimer of warranty in the University of Illinois Open Source License
+applies to all code in the LLVM Distribution, and nothing in any of the
+other licenses gives permission to use the names of the LLVM Team or the
+University of Illinois to endorse or promote products derived from this
+Software.
+
+The following pieces of software have additional or alternate copyrights,
+licenses, and/or restrictions:
+
+Program             Directory
+-------             ---------
+<none yet>
+
diff --git a/clang-tidy-vs/ClangTidy/packages.config b/clang-tidy-vs/ClangTidy/packages.config
new file mode 100644 (file)
index 0000000..75d7faf
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>\r
+<packages>\r
+  <package id="Brutal.Dev.StrongNameSigner" version="1.8.0" targetFramework="net45" />\r
+  <package id="YamlDotNet" version="3.3.0" targetFramework="net45" />\r
+  <package id="YamlDotNet.Dynamic" version="3.2.3" targetFramework="net45" />\r
+</packages>
\ No newline at end of file
diff --git a/clang-tidy-vs/ClangTidy/source.extension.vsixmanifest b/clang-tidy-vs/ClangTidy/source.extension.vsixmanifest
new file mode 100644 (file)
index 0000000..cd0e863
--- /dev/null
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>\r
+<Vsix Version="1.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2010">\r
+  <Identifier Id="405594C3-042A-4155-B9A6-E25DAB8B1924">\r
+    <Name>ClangFormat</Name>\r
+    <Author>LLVM</Author>\r
+    <Version>4.0.0</Version>\r
+    <Description xml:space="preserve">A static analysis tool for C/C++ code.</Description>\r
+    <Locale>1033</Locale>\r
+    <MoreInfoUrl>http://clang.llvm.org/extra/clang-tidy/</MoreInfoUrl>\r
+    <License>license.txt</License>\r
+    <InstalledByMsi>false</InstalledByMsi>\r
+    <SupportedProducts>\r
+      <VisualStudio Version="10.0">\r
+        <Edition>Pro</Edition>\r
+      </VisualStudio>\r
+      <VisualStudio Version="11.0">\r
+        <Edition>Pro</Edition>\r
+      </VisualStudio>\r
+      <VisualStudio Version="12.0">\r
+        <Edition>Pro</Edition>\r
+      </VisualStudio>\r
+      <VisualStudio Version="14.0">\r
+        <Edition>Pro</Edition>\r
+      </VisualStudio>\r
+    </SupportedProducts>\r
+    <SupportedFrameworkRuntimeEdition MinVersion="4.0" MaxVersion="4.0" />\r
+  </Identifier>\r
+  <References>\r
+        <Reference Id="Microsoft.VisualStudio.MPF" MinVersion="10.0">\r
+            <Name>Visual Studio MPF</Name>\r
+        </Reference>\r
+  </References>\r
+  <Content>\r
+    <VsPackage>|%CurrentProject%;PkgdefProjectOutputGroup|</VsPackage>\r
+  </Content>\r
+</Vsix>\r
diff --git a/clang-tidy-vs/README.txt b/clang-tidy-vs/README.txt
new file mode 100644 (file)
index 0000000..d8785c9
--- /dev/null
@@ -0,0 +1,17 @@
+This directory contains a VSPackage project to generate a Visual Studio extension\r
+for clang-tidy.\r
+\r
+Build prerequisites are:\r
+- Visual Studio 2013 Professional\r
+- Visual Studio 2013 SDK\r
+- Visual Studio 2010 Professional (?)\r
+- Visual Studio 2010 SDK (?)\r
+\r
+The extension is built using CMake by setting BUILD_CLANG_TIDY_VS_PLUGIN=ON\r
+when configuring a Clang build, and building the clang_tidy_vsix target.\r
+\r
+The CMake build will copy clang-tidy.exe and LICENSE.TXT into the ClangTidy/\r
+directory so they can be bundled with the plug-in, as well as creating\r
+ClangTidy/source.extension.vsixmanifest. Once the plug-in has been built with\r
+CMake once, it can be built manually from the ClangTidy.sln solution in Visual\r
+Studio.\r
diff --git a/clang-tidy-vs/source.extension.vsixmanifest.in b/clang-tidy-vs/source.extension.vsixmanifest.in
new file mode 100644 (file)
index 0000000..86ee571
--- /dev/null
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>\r
+<Vsix Version="1.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2010">\r
+  <Identifier Id="405594C3-042A-4155-B9A6-E25DAB8B1924">\r
+    <Name>ClangTidy</Name>\r
+    <Author>LLVM</Author>\r
+    <Version>@CLANG_TIDY_VS_VERSION@</Version>\r
+    <Description xml:space="preserve">A static analysis tool for C/C++ code.</Description>\r
+    <Locale>1033</Locale>\r
+    <MoreInfoUrl>http://clang.llvm.org/extra/clang-tidy/</MoreInfoUrl>\r
+    <License>license.txt</License>\r
+    <InstalledByMsi>false</InstalledByMsi>\r
+    <SupportedProducts>\r
+      <VisualStudio Version="10.0">\r
+        <Edition>Pro</Edition>\r
+      </VisualStudio>\r
+      <VisualStudio Version="11.0">\r
+        <Edition>Pro</Edition>\r
+      </VisualStudio>\r
+      <VisualStudio Version="12.0">\r
+        <Edition>Pro</Edition>\r
+      </VisualStudio>\r
+      <VisualStudio Version="14.0">\r
+        <Edition>Pro</Edition>\r
+      </VisualStudio>\r
+    </SupportedProducts>\r
+    <SupportedFrameworkRuntimeEdition MinVersion="4.0" MaxVersion="4.0" />\r
+  </Identifier>\r
+  <References>\r
+        <Reference Id="Microsoft.VisualStudio.MPF" MinVersion="10.0">\r
+            <Name>Visual Studio MPF</Name>\r
+        </Reference>\r
+  </References>\r
+  <Content>\r
+    <VsPackage>|%CurrentProject%;PkgdefProjectOutputGroup|</VsPackage>\r
+  </Content>\r
+</Vsix>\r
diff --git a/clang-tidy/CMakeLists.txt b/clang-tidy/CMakeLists.txt
new file mode 100644 (file)
index 0000000..9d6fc7f
--- /dev/null
@@ -0,0 +1,44 @@
+set(LLVM_LINK_COMPONENTS
+  Support
+  )
+
+add_clang_library(clangTidy
+  ClangTidy.cpp
+  ClangTidyModule.cpp
+  ClangTidyDiagnosticConsumer.cpp
+  ClangTidyOptions.cpp
+
+  DEPENDS
+  ClangSACheckers
+
+  LINK_LIBS
+  clangAST
+  clangASTMatchers
+  clangBasic
+  clangFormat
+  clangFrontend
+  clangLex
+  clangRewrite
+  clangSema
+  clangStaticAnalyzerCore
+  clangStaticAnalyzerFrontend
+  clangTooling
+  clangToolingCore
+  )
+
+add_subdirectory(android)
+add_subdirectory(boost)
+add_subdirectory(bugprone)
+add_subdirectory(cert)
+add_subdirectory(cppcoreguidelines)
+add_subdirectory(google)
+add_subdirectory(hicpp)
+add_subdirectory(llvm)
+add_subdirectory(misc)
+add_subdirectory(modernize)
+add_subdirectory(mpi)
+add_subdirectory(performance)
+add_subdirectory(plugin)
+add_subdirectory(readability)
+add_subdirectory(tool)
+add_subdirectory(utils)
diff --git a/clang-tidy/ClangTidy.cpp b/clang-tidy/ClangTidy.cpp
new file mode 100644 (file)
index 0000000..c3ddc45
--- /dev/null
@@ -0,0 +1,588 @@
+//===--- tools/extra/clang-tidy/ClangTidy.cpp - Clang tidy tool -----------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+///  \file This file implements a clang-tidy tool.
+///
+///  This tool uses the Clang Tooling infrastructure, see
+///    http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html
+///  for details on setting it up with LLVM source tree.
+///
+//===----------------------------------------------------------------------===//
+
+#include "ClangTidy.h"
+#include "ClangTidyDiagnosticConsumer.h"
+#include "ClangTidyModuleRegistry.h"
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Format/Format.h"
+#include "clang/Frontend/ASTConsumers.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendActions.h"
+#include "clang/Frontend/FrontendDiagnostic.h"
+#include "clang/Frontend/MultiplexConsumer.h"
+#include "clang/Frontend/TextDiagnosticPrinter.h"
+#include "clang/Lex/PPCallbacks.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Rewrite/Frontend/FixItRewriter.h"
+#include "clang/Rewrite/Frontend/FrontendActions.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
+#include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h"
+#include "clang/Tooling/DiagnosticsYaml.h"
+#include "clang/Tooling/Refactoring.h"
+#include "clang/Tooling/ReplacementsYaml.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/Support/Process.h"
+#include "llvm/Support/Signals.h"
+#include <algorithm>
+#include <utility>
+
+using namespace clang::ast_matchers;
+using namespace clang::driver;
+using namespace clang::tooling;
+using namespace llvm;
+
+LLVM_INSTANTIATE_REGISTRY(clang::tidy::ClangTidyModuleRegistry)
+
+namespace clang {
+namespace tidy {
+
+namespace {
+static const char *AnalyzerCheckNamePrefix = "clang-analyzer-";
+
+class AnalyzerDiagnosticConsumer : public ento::PathDiagnosticConsumer {
+public:
+  AnalyzerDiagnosticConsumer(ClangTidyContext &Context) : Context(Context) {}
+
+  void FlushDiagnosticsImpl(std::vector<const ento::PathDiagnostic *> &Diags,
+                            FilesMade *filesMade) override {
+    for (const ento::PathDiagnostic *PD : Diags) {
+      SmallString<64> CheckName(AnalyzerCheckNamePrefix);
+      CheckName += PD->getCheckName();
+      Context.diag(CheckName, PD->getLocation().asLocation(),
+                   PD->getShortDescription())
+          << PD->path.back()->getRanges();
+
+      for (const auto &DiagPiece :
+           PD->path.flatten(/*ShouldFlattenMacros=*/true)) {
+        Context.diag(CheckName, DiagPiece->getLocation().asLocation(),
+                     DiagPiece->getString(), DiagnosticIDs::Note)
+            << DiagPiece->getRanges();
+      }
+    }
+  }
+
+  StringRef getName() const override { return "ClangTidyDiags"; }
+  bool supportsLogicalOpControlFlow() const override { return true; }
+  bool supportsCrossFileDiagnostics() const override { return true; }
+
+private:
+  ClangTidyContext &Context;
+};
+
+class ErrorReporter {
+public:
+  ErrorReporter(ClangTidyContext &Context, bool ApplyFixes)
+      : Files(FileSystemOptions()), DiagOpts(new DiagnosticOptions()),
+        DiagPrinter(new TextDiagnosticPrinter(llvm::outs(), &*DiagOpts)),
+        Diags(IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), &*DiagOpts,
+              DiagPrinter),
+        SourceMgr(Diags, Files), Context(Context), ApplyFixes(ApplyFixes),
+        TotalFixes(0), AppliedFixes(0), WarningsAsErrors(0) {
+    DiagOpts->ShowColors = llvm::sys::Process::StandardOutHasColors();
+    DiagPrinter->BeginSourceFile(LangOpts);
+  }
+
+  SourceManager &getSourceManager() { return SourceMgr; }
+
+  void reportDiagnostic(const ClangTidyError &Error) {
+    const tooling::DiagnosticMessage &Message = Error.Message;
+    SourceLocation Loc = getLocation(Message.FilePath, Message.FileOffset);
+    // Contains a pair for each attempted fix: location and whether the fix was
+    // applied successfully.
+    SmallVector<std::pair<SourceLocation, bool>, 4> FixLocations;
+    {
+      auto Level = static_cast<DiagnosticsEngine::Level>(Error.DiagLevel);
+      std::string Name = Error.DiagnosticName;
+      if (Error.IsWarningAsError) {
+        Name += ",-warnings-as-errors";
+        Level = DiagnosticsEngine::Error;
+        WarningsAsErrors++;
+      }
+      auto Diag = Diags.Report(Loc, Diags.getCustomDiagID(Level, "%0 [%1]"))
+                  << Message.Message << Name;
+      for (const auto &FileAndReplacements : Error.Fix) {
+        for (const auto &Repl : FileAndReplacements.second) {
+          // Retrieve the source range for applicable fixes. Macro definitions
+          // on the command line have locations in a virtual buffer and don't
+          // have valid file paths and are therefore not applicable.
+          SourceRange Range;
+          SourceLocation FixLoc;
+          ++TotalFixes;
+          bool CanBeApplied = false;
+          if (Repl.isApplicable()) {
+            SmallString<128> FixAbsoluteFilePath = Repl.getFilePath();
+            Files.makeAbsolutePath(FixAbsoluteFilePath);
+            if (ApplyFixes) {
+              tooling::Replacement R(FixAbsoluteFilePath, Repl.getOffset(),
+                                     Repl.getLength(),
+                                     Repl.getReplacementText());
+              Replacements &Replacements = FileReplacements[R.getFilePath()];
+              llvm::Error Err = Replacements.add(R);
+              if (Err) {
+                // FIXME: Implement better conflict handling.
+                llvm::errs() << "Trying to resolve conflict: "
+                             << llvm::toString(std::move(Err)) << "\n";
+                unsigned NewOffset =
+                    Replacements.getShiftedCodePosition(R.getOffset());
+                unsigned NewLength = Replacements.getShiftedCodePosition(
+                                         R.getOffset() + R.getLength()) -
+                                     NewOffset;
+                if (NewLength == R.getLength()) {
+                  R = Replacement(R.getFilePath(), NewOffset, NewLength,
+                                  R.getReplacementText());
+                  Replacements = Replacements.merge(tooling::Replacements(R));
+                  CanBeApplied = true;
+                  ++AppliedFixes;
+                } else {
+                  llvm::errs()
+                      << "Can't resolve conflict, skipping the replacement.\n";
+                }
+
+              } else {
+                CanBeApplied = true;
+                ++AppliedFixes;
+              }
+            }
+            FixLoc = getLocation(FixAbsoluteFilePath, Repl.getOffset());
+            SourceLocation FixEndLoc =
+                FixLoc.getLocWithOffset(Repl.getLength());
+            Range = SourceRange(FixLoc, FixEndLoc);
+            Diag << FixItHint::CreateReplacement(Range,
+                                                 Repl.getReplacementText());
+          }
+
+          if (ApplyFixes)
+            FixLocations.push_back(std::make_pair(FixLoc, CanBeApplied));
+        }
+      }
+    }
+    for (auto Fix : FixLocations) {
+      Diags.Report(Fix.first, Fix.second ? diag::note_fixit_applied
+                                         : diag::note_fixit_failed);
+    }
+    for (const auto &Note : Error.Notes)
+      reportNote(Note);
+  }
+
+  void Finish() {
+    if (ApplyFixes && TotalFixes > 0) {
+      Rewriter Rewrite(SourceMgr, LangOpts);
+      for (const auto &FileAndReplacements : FileReplacements) {
+        StringRef File = FileAndReplacements.first();
+        llvm::ErrorOr<std::unique_ptr<MemoryBuffer>> Buffer =
+            SourceMgr.getFileManager().getBufferForFile(File);
+        if (!Buffer) {
+          llvm::errs() << "Can't get buffer for file " << File << ": "
+                       << Buffer.getError().message() << "\n";
+          // FIXME: Maybe don't apply fixes for other files as well.
+          continue;
+        }
+        StringRef Code = Buffer.get()->getBuffer();
+        auto Style = format::getStyle(
+            *Context.getOptionsForFile(File).FormatStyle, File, "none");
+        if (!Style) {
+          llvm::errs() << llvm::toString(Style.takeError()) << "\n";
+          continue;
+        }
+        llvm::Expected<tooling::Replacements> Replacements =
+            format::cleanupAroundReplacements(Code, FileAndReplacements.second,
+                                              *Style);
+        if (!Replacements) {
+          llvm::errs() << llvm::toString(Replacements.takeError()) << "\n";
+          continue;
+        }
+        if (llvm::Expected<tooling::Replacements> FormattedReplacements =
+                format::formatReplacements(Code, *Replacements, *Style)) {
+          Replacements = std::move(FormattedReplacements);
+          if (!Replacements)
+            llvm_unreachable("!Replacements");
+        } else {
+          llvm::errs() << llvm::toString(FormattedReplacements.takeError())
+                       << ". Skipping formatting.\n";
+        }
+        if (!tooling::applyAllReplacements(Replacements.get(), Rewrite)) {
+          llvm::errs() << "Can't apply replacements for file " << File << "\n";
+        }
+      }
+      if (Rewrite.overwriteChangedFiles()) {
+        llvm::errs() << "clang-tidy failed to apply suggested fixes.\n";
+      } else {
+        llvm::errs() << "clang-tidy applied " << AppliedFixes << " of "
+                     << TotalFixes << " suggested fixes.\n";
+      }
+    }
+  }
+
+  unsigned getWarningsAsErrorsCount() const { return WarningsAsErrors; }
+
+private:
+  SourceLocation getLocation(StringRef FilePath, unsigned Offset) {
+    if (FilePath.empty())
+      return SourceLocation();
+
+    const FileEntry *File = SourceMgr.getFileManager().getFile(FilePath);
+    FileID ID = SourceMgr.getOrCreateFileID(File, SrcMgr::C_User);
+    return SourceMgr.getLocForStartOfFile(ID).getLocWithOffset(Offset);
+  }
+
+  void reportNote(const tooling::DiagnosticMessage &Message) {
+    SourceLocation Loc = getLocation(Message.FilePath, Message.FileOffset);
+    Diags.Report(Loc, Diags.getCustomDiagID(DiagnosticsEngine::Note, "%0"))
+        << Message.Message;
+  }
+
+  FileManager Files;
+  LangOptions LangOpts; // FIXME: use langopts from each original file
+  IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts;
+  DiagnosticConsumer *DiagPrinter;
+  DiagnosticsEngine Diags;
+  SourceManager SourceMgr;
+  llvm::StringMap<Replacements> FileReplacements;
+  ClangTidyContext &Context;
+  bool ApplyFixes;
+  unsigned TotalFixes;
+  unsigned AppliedFixes;
+  unsigned WarningsAsErrors;
+};
+
+class ClangTidyASTConsumer : public MultiplexConsumer {
+public:
+  ClangTidyASTConsumer(std::vector<std::unique_ptr<ASTConsumer>> Consumers,
+                       std::unique_ptr<ast_matchers::MatchFinder> Finder,
+                       std::vector<std::unique_ptr<ClangTidyCheck>> Checks)
+      : MultiplexConsumer(std::move(Consumers)), Finder(std::move(Finder)),
+        Checks(std::move(Checks)) {}
+
+private:
+  std::unique_ptr<ast_matchers::MatchFinder> Finder;
+  std::vector<std::unique_ptr<ClangTidyCheck>> Checks;
+};
+
+} // namespace
+
+ClangTidyASTConsumerFactory::ClangTidyASTConsumerFactory(
+    ClangTidyContext &Context)
+    : Context(Context), CheckFactories(new ClangTidyCheckFactories) {
+  for (ClangTidyModuleRegistry::iterator I = ClangTidyModuleRegistry::begin(),
+                                         E = ClangTidyModuleRegistry::end();
+       I != E; ++I) {
+    std::unique_ptr<ClangTidyModule> Module(I->instantiate());
+    Module->addCheckFactories(*CheckFactories);
+  }
+}
+
+static void setStaticAnalyzerCheckerOpts(const ClangTidyOptions &Opts,
+                                         AnalyzerOptionsRef AnalyzerOptions) {
+  StringRef AnalyzerPrefix(AnalyzerCheckNamePrefix);
+  for (const auto &Opt : Opts.CheckOptions) {
+    StringRef OptName(Opt.first);
+    if (!OptName.startswith(AnalyzerPrefix))
+      continue;
+    AnalyzerOptions->Config[OptName.substr(AnalyzerPrefix.size())] = Opt.second;
+  }
+}
+
+typedef std::vector<std::pair<std::string, bool>> CheckersList;
+
+static CheckersList getCheckersControlList(ClangTidyContext &Context) {
+  CheckersList List;
+
+  const auto &RegisteredCheckers =
+      AnalyzerOptions::getRegisteredCheckers(/*IncludeExperimental=*/false);
+  bool AnalyzerChecksEnabled = false;
+  for (StringRef CheckName : RegisteredCheckers) {
+    std::string ClangTidyCheckName((AnalyzerCheckNamePrefix + CheckName).str());
+    AnalyzerChecksEnabled |= Context.isCheckEnabled(ClangTidyCheckName);
+  }
+
+  if (!AnalyzerChecksEnabled)
+    return List;
+
+  // List all static analyzer checkers that our filter enables.
+  //
+  // Always add all core checkers if any other static analyzer check is enabled.
+  // This is currently necessary, as other path sensitive checks rely on the
+  // core checkers.
+  for (StringRef CheckName : RegisteredCheckers) {
+    std::string ClangTidyCheckName((AnalyzerCheckNamePrefix + CheckName).str());
+
+    if (CheckName.startswith("core") ||
+        Context.isCheckEnabled(ClangTidyCheckName)) {
+      List.emplace_back(CheckName, true);
+    }
+  }
+  return List;
+}
+
+std::unique_ptr<clang::ASTConsumer>
+ClangTidyASTConsumerFactory::CreateASTConsumer(
+    clang::CompilerInstance &Compiler, StringRef File) {
+  // FIXME: Move this to a separate method, so that CreateASTConsumer doesn't
+  // modify Compiler.
+  Context.setSourceManager(&Compiler.getSourceManager());
+  Context.setCurrentFile(File);
+  Context.setASTContext(&Compiler.getASTContext());
+
+  auto WorkingDir = Compiler.getSourceManager()
+                        .getFileManager()
+                        .getVirtualFileSystem()
+                        ->getCurrentWorkingDirectory();
+  if (WorkingDir)
+    Context.setCurrentBuildDirectory(WorkingDir.get());
+
+  std::vector<std::unique_ptr<ClangTidyCheck>> Checks;
+  CheckFactories->createChecks(&Context, Checks);
+
+  ast_matchers::MatchFinder::MatchFinderOptions FinderOptions;
+  if (auto *P = Context.getCheckProfileData())
+    FinderOptions.CheckProfiling.emplace(P->Records);
+
+  std::unique_ptr<ast_matchers::MatchFinder> Finder(
+      new ast_matchers::MatchFinder(std::move(FinderOptions)));
+
+  for (auto &Check : Checks) {
+    Check->registerMatchers(&*Finder);
+    Check->registerPPCallbacks(Compiler);
+  }
+
+  std::vector<std::unique_ptr<ASTConsumer>> Consumers;
+  if (!Checks.empty())
+    Consumers.push_back(Finder->newASTConsumer());
+
+  AnalyzerOptionsRef AnalyzerOptions = Compiler.getAnalyzerOpts();
+  // FIXME: Remove this option once clang's cfg-temporary-dtors option defaults
+  // to true.
+  AnalyzerOptions->Config["cfg-temporary-dtors"] =
+      Context.getOptions().AnalyzeTemporaryDtors ? "true" : "false";
+
+  AnalyzerOptions->CheckersControlList = getCheckersControlList(Context);
+  if (!AnalyzerOptions->CheckersControlList.empty()) {
+    setStaticAnalyzerCheckerOpts(Context.getOptions(), AnalyzerOptions);
+    AnalyzerOptions->AnalysisStoreOpt = RegionStoreModel;
+    AnalyzerOptions->AnalysisDiagOpt = PD_NONE;
+    AnalyzerOptions->AnalyzeNestedBlocks = true;
+    AnalyzerOptions->eagerlyAssumeBinOpBifurcation = true;
+    std::unique_ptr<ento::AnalysisASTConsumer> AnalysisConsumer =
+        ento::CreateAnalysisConsumer(Compiler);
+    AnalysisConsumer->AddDiagnosticConsumer(
+        new AnalyzerDiagnosticConsumer(Context));
+    Consumers.push_back(std::move(AnalysisConsumer));
+  }
+  return llvm::make_unique<ClangTidyASTConsumer>(
+      std::move(Consumers), std::move(Finder), std::move(Checks));
+}
+
+std::vector<std::string> ClangTidyASTConsumerFactory::getCheckNames() {
+  std::vector<std::string> CheckNames;
+  for (const auto &CheckFactory : *CheckFactories) {
+    if (Context.isCheckEnabled(CheckFactory.first))
+      CheckNames.push_back(CheckFactory.first);
+  }
+
+  for (const auto &AnalyzerCheck : getCheckersControlList(Context))
+    CheckNames.push_back(AnalyzerCheckNamePrefix + AnalyzerCheck.first);
+
+  std::sort(CheckNames.begin(), CheckNames.end());
+  return CheckNames;
+}
+
+ClangTidyOptions::OptionMap ClangTidyASTConsumerFactory::getCheckOptions() {
+  ClangTidyOptions::OptionMap Options;
+  std::vector<std::unique_ptr<ClangTidyCheck>> Checks;
+  CheckFactories->createChecks(&Context, Checks);
+  for (const auto &Check : Checks)
+    Check->storeOptions(Options);
+  return Options;
+}
+
+DiagnosticBuilder ClangTidyCheck::diag(SourceLocation Loc, StringRef Message,
+                                       DiagnosticIDs::Level Level) {
+  return Context->diag(CheckName, Loc, Message, Level);
+}
+
+void ClangTidyCheck::run(const ast_matchers::MatchFinder::MatchResult &Result) {
+  Context->setSourceManager(Result.SourceManager);
+  check(Result);
+}
+
+OptionsView::OptionsView(StringRef CheckName,
+                         const ClangTidyOptions::OptionMap &CheckOptions)
+    : NamePrefix(CheckName.str() + "."), CheckOptions(CheckOptions) {}
+
+std::string OptionsView::get(StringRef LocalName, StringRef Default) const {
+  const auto &Iter = CheckOptions.find(NamePrefix + LocalName.str());
+  if (Iter != CheckOptions.end())
+    return Iter->second;
+  return Default;
+}
+
+std::string OptionsView::getLocalOrGlobal(StringRef LocalName,
+                                          StringRef Default) const {
+  auto Iter = CheckOptions.find(NamePrefix + LocalName.str());
+  if (Iter != CheckOptions.end())
+    return Iter->second;
+  // Fallback to global setting, if present.
+  Iter = CheckOptions.find(LocalName.str());
+  if (Iter != CheckOptions.end())
+    return Iter->second;
+  return Default;
+}
+
+void OptionsView::store(ClangTidyOptions::OptionMap &Options,
+                        StringRef LocalName, StringRef Value) const {
+  Options[NamePrefix + LocalName.str()] = Value;
+}
+
+void OptionsView::store(ClangTidyOptions::OptionMap &Options,
+                        StringRef LocalName, int64_t Value) const {
+  store(Options, LocalName, llvm::itostr(Value));
+}
+
+std::vector<std::string> getCheckNames(const ClangTidyOptions &Options) {
+  clang::tidy::ClangTidyContext Context(
+      llvm::make_unique<DefaultOptionsProvider>(ClangTidyGlobalOptions(),
+                                                Options));
+  ClangTidyASTConsumerFactory Factory(Context);
+  return Factory.getCheckNames();
+}
+
+ClangTidyOptions::OptionMap getCheckOptions(const ClangTidyOptions &Options) {
+  clang::tidy::ClangTidyContext Context(
+      llvm::make_unique<DefaultOptionsProvider>(ClangTidyGlobalOptions(),
+                                                Options));
+  ClangTidyASTConsumerFactory Factory(Context);
+  return Factory.getCheckOptions();
+}
+
+void runClangTidy(clang::tidy::ClangTidyContext &Context,
+                  const CompilationDatabase &Compilations,
+                  ArrayRef<std::string> InputFiles, ProfileData *Profile) {
+  ClangTool Tool(Compilations, InputFiles);
+
+  // Add extra arguments passed by the clang-tidy command-line.
+  ArgumentsAdjuster PerFileExtraArgumentsInserter =
+      [&Context](const CommandLineArguments &Args, StringRef Filename) {
+        ClangTidyOptions Opts = Context.getOptionsForFile(Filename);
+        CommandLineArguments AdjustedArgs = Args;
+        if (Opts.ExtraArgsBefore) {
+          auto I = AdjustedArgs.begin();
+          if (I != AdjustedArgs.end() && !StringRef(*I).startswith("-"))
+            ++I; // Skip compiler binary name, if it is there.
+          AdjustedArgs.insert(I, Opts.ExtraArgsBefore->begin(),
+                              Opts.ExtraArgsBefore->end());
+        }
+        if (Opts.ExtraArgs)
+          AdjustedArgs.insert(AdjustedArgs.end(), Opts.ExtraArgs->begin(),
+                              Opts.ExtraArgs->end());
+        return AdjustedArgs;
+      };
+
+  // Remove plugins arguments.
+  ArgumentsAdjuster PluginArgumentsRemover =
+      [](const CommandLineArguments &Args, StringRef Filename) {
+        CommandLineArguments AdjustedArgs;
+        for (size_t I = 0, E = Args.size(); I < E; ++I) {
+          if (I + 4 < Args.size() && Args[I] == "-Xclang" &&
+              (Args[I + 1] == "-load" || Args[I + 1] == "-add-plugin" ||
+               StringRef(Args[I + 1]).startswith("-plugin-arg-")) &&
+              Args[I + 2] == "-Xclang") {
+            I += 3;
+          } else
+            AdjustedArgs.push_back(Args[I]);
+        }
+        return AdjustedArgs;
+      };
+
+  Tool.appendArgumentsAdjuster(PerFileExtraArgumentsInserter);
+  Tool.appendArgumentsAdjuster(PluginArgumentsRemover);
+  if (Profile)
+    Context.setCheckProfileData(Profile);
+
+  ClangTidyDiagnosticConsumer DiagConsumer(Context);
+
+  Tool.setDiagnosticConsumer(&DiagConsumer);
+
+  class ActionFactory : public FrontendActionFactory {
+  public:
+    ActionFactory(ClangTidyContext &Context) : ConsumerFactory(Context) {}
+    FrontendAction *create() override { return new Action(&ConsumerFactory); }
+
+  private:
+    class Action : public ASTFrontendAction {
+    public:
+      Action(ClangTidyASTConsumerFactory *Factory) : Factory(Factory) {}
+      std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler,
+                                                     StringRef File) override {
+        return Factory->CreateASTConsumer(Compiler, File);
+      }
+
+    private:
+      ClangTidyASTConsumerFactory *Factory;
+    };
+
+    ClangTidyASTConsumerFactory ConsumerFactory;
+  };
+
+  ActionFactory Factory(Context);
+  Tool.run(&Factory);
+}
+
+void handleErrors(ClangTidyContext &Context, bool Fix,
+                  unsigned &WarningsAsErrorsCount) {
+  ErrorReporter Reporter(Context, Fix);
+  vfs::FileSystem &FileSystem =
+      *Reporter.getSourceManager().getFileManager().getVirtualFileSystem();
+  auto InitialWorkingDir = FileSystem.getCurrentWorkingDirectory();
+  if (!InitialWorkingDir)
+    llvm::report_fatal_error("Cannot get current working path.");
+
+  for (const ClangTidyError &Error : Context.getErrors()) {
+    if (!Error.BuildDirectory.empty()) {
+      // By default, the working directory of file system is the current
+      // clang-tidy running directory.
+      //
+      // Change the directory to the one used during the analysis.
+      FileSystem.setCurrentWorkingDirectory(Error.BuildDirectory);
+    }
+    Reporter.reportDiagnostic(Error);
+    // Return to the initial directory to correctly resolve next Error.
+    FileSystem.setCurrentWorkingDirectory(InitialWorkingDir.get());
+  }
+  Reporter.Finish();
+  WarningsAsErrorsCount += Reporter.getWarningsAsErrorsCount();
+}
+
+void exportReplacements(const llvm::StringRef MainFilePath,
+                        const std::vector<ClangTidyError> &Errors,
+                        raw_ostream &OS) {
+  TranslationUnitDiagnostics TUD;
+  TUD.MainSourceFile = MainFilePath;
+  for (const auto &Error : Errors) {
+    tooling::Diagnostic Diag = Error;
+    TUD.Diagnostics.insert(TUD.Diagnostics.end(), Diag);
+  }
+
+  yaml::Output YAML(OS);
+  YAML << TUD;
+}
+
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/ClangTidy.h b/clang-tidy/ClangTidy.h
new file mode 100644 (file)
index 0000000..8d4d6c9
--- /dev/null
@@ -0,0 +1,250 @@
+//===--- ClangTidy.h - clang-tidy -------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDY_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDY_H
+
+#include "ClangTidyDiagnosticConsumer.h"
+#include "ClangTidyOptions.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Tooling/Refactoring.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/raw_ostream.h"
+#include <memory>
+#include <type_traits>
+#include <vector>
+
+namespace clang {
+
+class CompilerInstance;
+namespace tooling {
+class CompilationDatabase;
+}
+
+namespace tidy {
+
+/// \brief Provides access to the ``ClangTidyCheck`` options via check-local
+/// names.
+///
+/// Methods of this class prepend ``CheckName + "."`` to translate check-local
+/// option names to global option names.
+class OptionsView {
+public:
+  /// \brief Initializes the instance using \p CheckName + "." as a prefix.
+  OptionsView(StringRef CheckName,
+              const ClangTidyOptions::OptionMap &CheckOptions);
+
+  /// \brief Read a named option from the ``Context``.
+  ///
+  /// Reads the option with the check-local name \p LocalName from the
+  /// ``CheckOptions``. If the corresponding key is not present, returns
+  /// \p Default.
+  std::string get(StringRef LocalName, StringRef Default) const;
+
+  /// \brief Read a named option from the ``Context``.
+  ///
+  /// Reads the option with the check-local name \p LocalName from local or
+  /// global ``CheckOptions``. Gets local option first. If local is not present,
+  /// falls back to get global option. If global option is not present either,
+  /// returns Default.
+  std::string getLocalOrGlobal(StringRef LocalName, StringRef Default) const;
+
+  /// \brief Read a named option from the ``Context`` and parse it as an
+  /// integral type ``T``.
+  ///
+  /// Reads the option with the check-local name \p LocalName from the
+  /// ``CheckOptions``. If the corresponding key is not present, returns
+  /// \p Default.
+  template <typename T>
+  typename std::enable_if<std::is_integral<T>::value, T>::type
+  get(StringRef LocalName, T Default) const {
+    std::string Value = get(LocalName, "");
+    T Result = Default;
+    if (!Value.empty())
+      StringRef(Value).getAsInteger(10, Result);
+    return Result;
+  }
+
+  /// \brief Read a named option from the ``Context`` and parse it as an
+  /// integral type ``T``.
+  ///
+  /// Reads the option with the check-local name \p LocalName from local or
+  /// global ``CheckOptions``. Gets local option first. If local is not present,
+  /// falls back to get global option. If global option is not present either,
+  /// returns Default.
+  template <typename T>
+  typename std::enable_if<std::is_integral<T>::value, T>::type
+  getLocalOrGlobal(StringRef LocalName, T Default) const {
+    std::string Value = getLocalOrGlobal(LocalName, "");
+    T Result = Default;
+    if (!Value.empty())
+      StringRef(Value).getAsInteger(10, Result);
+    return Result;
+  }
+
+  /// \brief Stores an option with the check-local name \p LocalName with string
+  /// value \p Value to \p Options.
+  void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName,
+             StringRef Value) const;
+
+  /// \brief Stores an option with the check-local name \p LocalName with
+  /// ``int64_t`` value \p Value to \p Options.
+  void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName,
+             int64_t Value) const;
+
+private:
+  std::string NamePrefix;
+  const ClangTidyOptions::OptionMap &CheckOptions;
+};
+
+/// \brief Base class for all clang-tidy checks.
+///
+/// To implement a ``ClangTidyCheck``, write a subclass and override some of the
+/// base class's methods. E.g. to implement a check that validates namespace
+/// declarations, override ``registerMatchers``:
+///
+/// ~~~{.cpp}
+/// void registerMatchers(ast_matchers::MatchFinder *Finder) override {
+///   Finder->addMatcher(namespaceDecl().bind("namespace"), this);
+/// }
+/// ~~~
+///
+/// and then override ``check(const MatchResult &Result)`` to do the actual
+/// check for each match.
+///
+/// A new ``ClangTidyCheck`` instance is created per translation unit.
+///
+/// FIXME: Figure out whether carrying information from one TU to another is
+/// useful/necessary.
+class ClangTidyCheck : public ast_matchers::MatchFinder::MatchCallback {
+public:
+  /// \brief Initializes the check with \p CheckName and \p Context.
+  ///
+  /// Derived classes must implement the constructor with this signature or
+  /// delegate it. If a check needs to read options, it can do this in the
+  /// constructor using the Options.get() methods below.
+  ClangTidyCheck(StringRef CheckName, ClangTidyContext *Context)
+      : CheckName(CheckName), Context(Context),
+        Options(CheckName, Context->getOptions().CheckOptions) {
+    assert(Context != nullptr);
+    assert(!CheckName.empty());
+  }
+
+  /// \brief Override this to register ``PPCallbacks`` with ``Compiler``.
+  ///
+  /// This should be used for clang-tidy checks that analyze preprocessor-
+  /// dependent properties, e.g. the order of include directives.
+  virtual void registerPPCallbacks(CompilerInstance &Compiler) {}
+
+  /// \brief Override this to register AST matchers with \p Finder.
+  ///
+  /// This should be used by clang-tidy checks that analyze code properties that
+  /// dependent on AST knowledge.
+  ///
+  /// You can register as many matchers as necessary with \p Finder. Usually,
+  /// "this" will be used as callback, but you can also specify other callback
+  /// classes. Thereby, different matchers can trigger different callbacks.
+  ///
+  /// If you need to merge information between the different matchers, you can
+  /// store these as members of the derived class. However, note that all
+  /// matches occur in the order of the AST traversal.
+  virtual void registerMatchers(ast_matchers::MatchFinder *Finder) {}
+
+  /// \brief ``ClangTidyChecks`` that register ASTMatchers should do the actual
+  /// work in here.
+  virtual void check(const ast_matchers::MatchFinder::MatchResult &Result) {}
+
+  /// \brief Add a diagnostic with the check's name.
+  DiagnosticBuilder diag(SourceLocation Loc, StringRef Description,
+                         DiagnosticIDs::Level Level = DiagnosticIDs::Warning);
+
+  /// \brief Should store all options supported by this check with their
+  /// current values or default values for options that haven't been overridden.
+  ///
+  /// The check should use ``Options.store()`` to store each option it supports
+  /// whether it has the default value or it has been overridden.
+  virtual void storeOptions(ClangTidyOptions::OptionMap &Options) {}
+
+private:
+  void run(const ast_matchers::MatchFinder::MatchResult &Result) override;
+  StringRef getID() const override { return CheckName; }
+  std::string CheckName;
+  ClangTidyContext *Context;
+
+protected:
+  OptionsView Options;
+  /// \brief Returns the main file name of the current translation unit.
+  StringRef getCurrentMainFile() const { return Context->getCurrentFile(); }
+  /// \brief Returns the language options from the context.
+  LangOptions getLangOpts() const { return Context->getLangOpts(); }
+};
+
+class ClangTidyCheckFactories;
+
+class ClangTidyASTConsumerFactory {
+public:
+  ClangTidyASTConsumerFactory(ClangTidyContext &Context);
+
+  /// \brief Returns an ASTConsumer that runs the specified clang-tidy checks.
+  std::unique_ptr<clang::ASTConsumer>
+  CreateASTConsumer(clang::CompilerInstance &Compiler, StringRef File);
+
+  /// \brief Get the list of enabled checks.
+  std::vector<std::string> getCheckNames();
+
+  /// \brief Get the union of options from all checks.
+  ClangTidyOptions::OptionMap getCheckOptions();
+
+private:
+  ClangTidyContext &Context;
+  std::unique_ptr<ClangTidyCheckFactories> CheckFactories;
+};
+
+/// \brief Fills the list of check names that are enabled when the provided
+/// filters are applied.
+std::vector<std::string> getCheckNames(const ClangTidyOptions &Options);
+
+/// \brief Returns the effective check-specific options.
+///
+/// The method configures ClangTidy with the specified \p Options and collects
+/// effective options from all created checks. The returned set of options
+/// includes default check-specific options for all keys not overridden by \p
+/// Options.
+ClangTidyOptions::OptionMap getCheckOptions(const ClangTidyOptions &Options);
+
+/// \brief Run a set of clang-tidy checks on a set of files.
+///
+/// \param Profile if provided, it enables check profile collection in
+/// MatchFinder, and will contain the result of the profile.
+void runClangTidy(clang::tidy::ClangTidyContext &Context,
+                  const tooling::CompilationDatabase &Compilations,
+                  ArrayRef<std::string> InputFiles,
+                  ProfileData *Profile = nullptr);
+
+// FIXME: This interface will need to be significantly extended to be useful.
+// FIXME: Implement confidence levels for displaying/fixing errors.
+//
+/// \brief Displays the found \p Errors to the users. If \p Fix is true, \p
+/// Errors containing fixes are automatically applied and reformatted. If no
+/// clang-format configuration file is found, the given \P FormatStyle is used.
+void handleErrors(ClangTidyContext &Context, bool Fix,
+                  unsigned &WarningsAsErrorsCount);
+
+/// \brief Serializes replacements into YAML and writes them to the specified
+/// output stream.
+void exportReplacements(StringRef MainFilePath,
+                        const std::vector<ClangTidyError> &Errors,
+                        raw_ostream &OS);
+
+} // end namespace tidy
+} // end namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDY_H
diff --git a/clang-tidy/ClangTidyDiagnosticConsumer.cpp b/clang-tidy/ClangTidyDiagnosticConsumer.cpp
new file mode 100644 (file)
index 0000000..0793311
--- /dev/null
@@ -0,0 +1,643 @@
+//===--- tools/extra/clang-tidy/ClangTidyDiagnosticConsumer.cpp ----------=== //
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+///  \file This file implements ClangTidyDiagnosticConsumer, ClangTidyContext
+///  and ClangTidyError classes.
+///
+///  This tool uses the Clang Tooling infrastructure, see
+///    http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html
+///  for details on setting it up with LLVM source tree.
+///
+//===----------------------------------------------------------------------===//
+
+#include "ClangTidyDiagnosticConsumer.h"
+#include "ClangTidyOptions.h"
+#include "clang/AST/ASTDiagnostic.h"
+#include "clang/Basic/DiagnosticOptions.h"
+#include "clang/Frontend/DiagnosticRenderer.h"
+#include "llvm/ADT/SmallString.h"
+#include <tuple>
+#include <vector>
+using namespace clang;
+using namespace tidy;
+
+namespace {
+class ClangTidyDiagnosticRenderer : public DiagnosticRenderer {
+public:
+  ClangTidyDiagnosticRenderer(const LangOptions &LangOpts,
+                              DiagnosticOptions *DiagOpts,
+                              ClangTidyError &Error)
+      : DiagnosticRenderer(LangOpts, DiagOpts), Error(Error) {}
+
+protected:
+  void emitDiagnosticMessage(FullSourceLoc Loc, PresumedLoc PLoc,
+                             DiagnosticsEngine::Level Level, StringRef Message,
+                             ArrayRef<CharSourceRange> Ranges,
+                             DiagOrStoredDiag Info) override {
+    // Remove check name from the message.
+    // FIXME: Remove this once there's a better way to pass check names than
+    // appending the check name to the message in ClangTidyContext::diag and
+    // using getCustomDiagID.
+    std::string CheckNameInMessage = " [" + Error.DiagnosticName + "]";
+    if (Message.endswith(CheckNameInMessage))
+      Message = Message.substr(0, Message.size() - CheckNameInMessage.size());
+
+    auto TidyMessage =
+        Loc.isValid()
+            ? tooling::DiagnosticMessage(Message, Loc.getManager(), Loc)
+            : tooling::DiagnosticMessage(Message);
+    if (Level == DiagnosticsEngine::Note) {
+      Error.Notes.push_back(TidyMessage);
+      return;
+    }
+    assert(Error.Message.Message.empty() && "Overwriting a diagnostic message");
+    Error.Message = TidyMessage;
+  }
+
+  void emitDiagnosticLoc(FullSourceLoc Loc, PresumedLoc PLoc,
+                         DiagnosticsEngine::Level Level,
+                         ArrayRef<CharSourceRange> Ranges) override {}
+
+  void emitCodeContext(FullSourceLoc Loc, DiagnosticsEngine::Level Level,
+                       SmallVectorImpl<CharSourceRange> &Ranges,
+                       ArrayRef<FixItHint> Hints) override {
+    assert(Loc.isValid());
+    for (const auto &FixIt : Hints) {
+      CharSourceRange Range = FixIt.RemoveRange;
+      assert(Range.getBegin().isValid() && Range.getEnd().isValid() &&
+             "Invalid range in the fix-it hint.");
+      assert(Range.getBegin().isFileID() && Range.getEnd().isFileID() &&
+             "Only file locations supported in fix-it hints.");
+
+      tooling::Replacement Replacement(Loc.getManager(), Range,
+                                       FixIt.CodeToInsert);
+      llvm::Error Err = Error.Fix[Replacement.getFilePath()].add(Replacement);
+      // FIXME: better error handling (at least, don't let other replacements be
+      // applied).
+      if (Err) {
+        llvm::errs() << "Fix conflicts with existing fix! "
+                     << llvm::toString(std::move(Err)) << "\n";
+        assert(false && "Fix conflicts with existing fix!");
+      }
+    }
+  }
+
+  void emitIncludeLocation(FullSourceLoc Loc, PresumedLoc PLoc) override {}
+
+  void emitImportLocation(FullSourceLoc Loc, PresumedLoc PLoc,
+                          StringRef ModuleName) override {}
+
+  void emitBuildingModuleLocation(FullSourceLoc Loc, PresumedLoc PLoc,
+                                  StringRef ModuleName) override {}
+
+  void endDiagnostic(DiagOrStoredDiag D,
+                     DiagnosticsEngine::Level Level) override {
+    assert(!Error.Message.Message.empty() && "Message has not been set");
+  }
+
+private:
+  ClangTidyError &Error;
+};
+} // end anonymous namespace
+
+ClangTidyError::ClangTidyError(StringRef CheckName,
+                               ClangTidyError::Level DiagLevel,
+                               StringRef BuildDirectory, bool IsWarningAsError)
+    : tooling::Diagnostic(CheckName, DiagLevel, BuildDirectory),
+      IsWarningAsError(IsWarningAsError) {}
+
+// Returns true if GlobList starts with the negative indicator ('-'), removes it
+// from the GlobList.
+static bool ConsumeNegativeIndicator(StringRef &GlobList) {
+  GlobList = GlobList.trim(' ');
+  if (GlobList.startswith("-")) {
+    GlobList = GlobList.substr(1);
+    return true;
+  }
+  return false;
+}
+// Converts first glob from the comma-separated list of globs to Regex and
+// removes it and the trailing comma from the GlobList.
+static llvm::Regex ConsumeGlob(StringRef &GlobList) {
+  StringRef UntrimmedGlob = GlobList.substr(0, GlobList.find(','));
+  StringRef Glob = UntrimmedGlob.trim(' ');
+  GlobList = GlobList.substr(UntrimmedGlob.size() + 1);
+  SmallString<128> RegexText("^");
+  StringRef MetaChars("()^$|*+?.[]\\{}");
+  for (char C : Glob) {
+    if (C == '*')
+      RegexText.push_back('.');
+    else if (MetaChars.find(C) != StringRef::npos)
+      RegexText.push_back('\\');
+    RegexText.push_back(C);
+  }
+  RegexText.push_back('$');
+  return llvm::Regex(RegexText);
+}
+
+GlobList::GlobList(StringRef Globs)
+    : Positive(!ConsumeNegativeIndicator(Globs)), Regex(ConsumeGlob(Globs)),
+      NextGlob(Globs.empty() ? nullptr : new GlobList(Globs)) {}
+
+bool GlobList::contains(StringRef S, bool Contains) {
+  if (Regex.match(S))
+    Contains = Positive;
+
+  if (NextGlob)
+    Contains = NextGlob->contains(S, Contains);
+  return Contains;
+}
+
+class ClangTidyContext::CachedGlobList {
+public:
+  CachedGlobList(StringRef Globs) : Globs(Globs) {}
+
+  bool contains(StringRef S) {
+    switch (auto &Result = Cache[S]) {
+      case Yes: return true;
+      case No: return false;
+      case None:
+        Result = Globs.contains(S) ? Yes : No;
+        return Result == Yes;
+    }
+    llvm_unreachable("invalid enum");
+  }
+
+private:
+  GlobList Globs;
+  enum Tristate { None, Yes, No };
+  llvm::StringMap<Tristate> Cache;
+};
+
+ClangTidyContext::ClangTidyContext(
+    std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider)
+    : DiagEngine(nullptr), OptionsProvider(std::move(OptionsProvider)),
+      Profile(nullptr) {
+  // Before the first translation unit we can get errors related to command-line
+  // parsing, use empty string for the file name in this case.
+  setCurrentFile("");
+}
+
+ClangTidyContext::~ClangTidyContext() = default;
+
+DiagnosticBuilder ClangTidyContext::diag(
+    StringRef CheckName, SourceLocation Loc, StringRef Description,
+    DiagnosticIDs::Level Level /* = DiagnosticIDs::Warning*/) {
+  assert(Loc.isValid());
+  unsigned ID = DiagEngine->getDiagnosticIDs()->getCustomDiagID(
+      Level, (Description + " [" + CheckName + "]").str());
+  CheckNamesByDiagnosticID.try_emplace(ID, CheckName);
+  return DiagEngine->Report(Loc, ID);
+}
+
+void ClangTidyContext::setDiagnosticsEngine(DiagnosticsEngine *Engine) {
+  DiagEngine = Engine;
+}
+
+void ClangTidyContext::setSourceManager(SourceManager *SourceMgr) {
+  DiagEngine->setSourceManager(SourceMgr);
+}
+
+void ClangTidyContext::setCurrentFile(StringRef File) {
+  CurrentFile = File;
+  CurrentOptions = getOptionsForFile(CurrentFile);
+  CheckFilter = llvm::make_unique<CachedGlobList>(*getOptions().Checks);
+  WarningAsErrorFilter =
+      llvm::make_unique<CachedGlobList>(*getOptions().WarningsAsErrors);
+}
+
+void ClangTidyContext::setASTContext(ASTContext *Context) {
+  DiagEngine->SetArgToStringFn(&FormatASTNodeDiagnosticArgument, Context);
+  LangOpts = Context->getLangOpts();
+}
+
+const ClangTidyGlobalOptions &ClangTidyContext::getGlobalOptions() const {
+  return OptionsProvider->getGlobalOptions();
+}
+
+const ClangTidyOptions &ClangTidyContext::getOptions() const {
+  return CurrentOptions;
+}
+
+ClangTidyOptions ClangTidyContext::getOptionsForFile(StringRef File) const {
+  // Merge options on top of getDefaults() as a safeguard against options with
+  // unset values.
+  return ClangTidyOptions::getDefaults().mergeWith(
+      OptionsProvider->getOptions(File));
+}
+
+void ClangTidyContext::setCheckProfileData(ProfileData *P) { Profile = P; }
+
+bool ClangTidyContext::isCheckEnabled(StringRef CheckName) const {
+  assert(CheckFilter != nullptr);
+  return CheckFilter->contains(CheckName);
+}
+
+bool ClangTidyContext::treatAsError(StringRef CheckName) const {
+  assert(WarningAsErrorFilter != nullptr);
+  return WarningAsErrorFilter->contains(CheckName);
+}
+
+/// \brief Store a \c ClangTidyError.
+void ClangTidyContext::storeError(const ClangTidyError &Error) {
+  Errors.push_back(Error);
+}
+
+StringRef ClangTidyContext::getCheckName(unsigned DiagnosticID) const {
+  llvm::DenseMap<unsigned, std::string>::const_iterator I =
+      CheckNamesByDiagnosticID.find(DiagnosticID);
+  if (I != CheckNamesByDiagnosticID.end())
+    return I->second;
+  return "";
+}
+
+ClangTidyDiagnosticConsumer::ClangTidyDiagnosticConsumer(
+    ClangTidyContext &Ctx, bool RemoveIncompatibleErrors)
+    : Context(Ctx), RemoveIncompatibleErrors(RemoveIncompatibleErrors),
+      LastErrorRelatesToUserCode(false), LastErrorPassesLineFilter(false),
+      LastErrorWasIgnored(false) {
+  IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
+  Diags = llvm::make_unique<DiagnosticsEngine>(
+      IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), &*DiagOpts, this,
+      /*ShouldOwnClient=*/false);
+  Context.setDiagnosticsEngine(Diags.get());
+}
+
+void ClangTidyDiagnosticConsumer::finalizeLastError() {
+  if (!Errors.empty()) {
+    ClangTidyError &Error = Errors.back();
+    if (!Context.isCheckEnabled(Error.DiagnosticName) &&
+        Error.DiagLevel != ClangTidyError::Error) {
+      ++Context.Stats.ErrorsIgnoredCheckFilter;
+      Errors.pop_back();
+    } else if (!LastErrorRelatesToUserCode) {
+      ++Context.Stats.ErrorsIgnoredNonUserCode;
+      Errors.pop_back();
+    } else if (!LastErrorPassesLineFilter) {
+      ++Context.Stats.ErrorsIgnoredLineFilter;
+      Errors.pop_back();
+    } else {
+      ++Context.Stats.ErrorsDisplayed;
+    }
+  }
+  LastErrorRelatesToUserCode = false;
+  LastErrorPassesLineFilter = false;
+}
+
+static bool LineIsMarkedWithNOLINT(SourceManager &SM, SourceLocation Loc) {
+  bool Invalid;
+  const char *CharacterData = SM.getCharacterData(Loc, &Invalid);
+  if (Invalid)
+    return false;
+
+  // Check if there's a NOLINT on this line.
+  const char *P = CharacterData;
+  while (*P != '\0' && *P != '\r' && *P != '\n')
+    ++P;
+  StringRef RestOfLine(CharacterData, P - CharacterData + 1);
+  // FIXME: Handle /\bNOLINT\b(\([^)]*\))?/ as cpplint.py does.
+  if (RestOfLine.find("NOLINT") != StringRef::npos)
+    return true;
+
+  // Check if there's a NOLINTNEXTLINE on the previous line.
+  const char *BufBegin =
+      SM.getCharacterData(SM.getLocForStartOfFile(SM.getFileID(Loc)), &Invalid);
+  if (Invalid || P == BufBegin)
+    return false;
+
+  // Scan backwards over the current line.
+  P = CharacterData;
+  while (P != BufBegin && *P != '\n')
+    --P;
+
+  // If we reached the begin of the file there is no line before it.
+  if (P == BufBegin)
+    return false;
+
+  // Skip over the newline.
+  --P;
+  const char *LineEnd = P;
+
+  // Now we're on the previous line. Skip to the beginning of it.
+  while (P != BufBegin && *P != '\n')
+    --P;
+
+  RestOfLine = StringRef(P, LineEnd - P + 1);
+  if (RestOfLine.find("NOLINTNEXTLINE") != StringRef::npos)
+    return true;
+
+  return false;
+}
+
+static bool LineIsMarkedWithNOLINTinMacro(SourceManager &SM,
+                                          SourceLocation Loc) {
+  while (true) {
+    if (LineIsMarkedWithNOLINT(SM, Loc))
+      return true;
+    if (!Loc.isMacroID())
+      return false;
+    Loc = SM.getImmediateExpansionRange(Loc).first;
+  }
+  return false;
+}
+
+void ClangTidyDiagnosticConsumer::HandleDiagnostic(
+    DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) {
+  if (LastErrorWasIgnored && DiagLevel == DiagnosticsEngine::Note)
+    return;
+
+  if (Info.getLocation().isValid() && DiagLevel != DiagnosticsEngine::Error &&
+      DiagLevel != DiagnosticsEngine::Fatal &&
+      LineIsMarkedWithNOLINTinMacro(Diags->getSourceManager(),
+                                    Info.getLocation())) {
+    ++Context.Stats.ErrorsIgnoredNOLINT;
+    // Ignored a warning, should ignore related notes as well
+    LastErrorWasIgnored = true;
+    return;
+  }
+
+  LastErrorWasIgnored = false;
+  // Count warnings/errors.
+  DiagnosticConsumer::HandleDiagnostic(DiagLevel, Info);
+
+  if (DiagLevel == DiagnosticsEngine::Note) {
+    assert(!Errors.empty() &&
+           "A diagnostic note can only be appended to a message.");
+  } else {
+    finalizeLastError();
+    StringRef WarningOption =
+        Context.DiagEngine->getDiagnosticIDs()->getWarningOptionForDiag(
+            Info.getID());
+    std::string CheckName = !WarningOption.empty()
+                                ? ("clang-diagnostic-" + WarningOption).str()
+                                : Context.getCheckName(Info.getID()).str();
+
+    if (CheckName.empty()) {
+      // This is a compiler diagnostic without a warning option. Assign check
+      // name based on its level.
+      switch (DiagLevel) {
+      case DiagnosticsEngine::Error:
+      case DiagnosticsEngine::Fatal:
+        CheckName = "clang-diagnostic-error";
+        break;
+      case DiagnosticsEngine::Warning:
+        CheckName = "clang-diagnostic-warning";
+        break;
+      default:
+        CheckName = "clang-diagnostic-unknown";
+        break;
+      }
+    }
+
+    ClangTidyError::Level Level = ClangTidyError::Warning;
+    if (DiagLevel == DiagnosticsEngine::Error ||
+        DiagLevel == DiagnosticsEngine::Fatal) {
+      // Force reporting of Clang errors regardless of filters and non-user
+      // code.
+      Level = ClangTidyError::Error;
+      LastErrorRelatesToUserCode = true;
+      LastErrorPassesLineFilter = true;
+    }
+    bool IsWarningAsError = DiagLevel == DiagnosticsEngine::Warning &&
+                            Context.treatAsError(CheckName);
+    Errors.emplace_back(CheckName, Level, Context.getCurrentBuildDirectory(),
+                        IsWarningAsError);
+  }
+
+  ClangTidyDiagnosticRenderer Converter(
+      Context.getLangOpts(), &Context.DiagEngine->getDiagnosticOptions(),
+      Errors.back());
+  SmallString<100> Message;
+  Info.FormatDiagnostic(Message);
+  FullSourceLoc Loc =
+      (Info.getLocation().isInvalid())
+          ? FullSourceLoc()
+          : FullSourceLoc(Info.getLocation(), Info.getSourceManager());
+  Converter.emitDiagnostic(Loc, DiagLevel, Message, Info.getRanges(),
+                           Info.getFixItHints());
+
+  checkFilters(Info.getLocation());
+}
+
+bool ClangTidyDiagnosticConsumer::passesLineFilter(StringRef FileName,
+                                                   unsigned LineNumber) const {
+  if (Context.getGlobalOptions().LineFilter.empty())
+    return true;
+  for (const FileFilter &Filter : Context.getGlobalOptions().LineFilter) {
+    if (FileName.endswith(Filter.Name)) {
+      if (Filter.LineRanges.empty())
+        return true;
+      for (const FileFilter::LineRange &Range : Filter.LineRanges) {
+        if (Range.first <= LineNumber && LineNumber <= Range.second)
+          return true;
+      }
+      return false;
+    }
+  }
+  return false;
+}
+
+void ClangTidyDiagnosticConsumer::checkFilters(SourceLocation Location) {
+  // Invalid location may mean a diagnostic in a command line, don't skip these.
+  if (!Location.isValid()) {
+    LastErrorRelatesToUserCode = true;
+    LastErrorPassesLineFilter = true;
+    return;
+  }
+
+  const SourceManager &Sources = Diags->getSourceManager();
+  if (!*Context.getOptions().SystemHeaders &&
+      Sources.isInSystemHeader(Location))
+    return;
+
+  // FIXME: We start with a conservative approach here, but the actual type of
+  // location needed depends on the check (in particular, where this check wants
+  // to apply fixes).
+  FileID FID = Sources.getDecomposedExpansionLoc(Location).first;
+  const FileEntry *File = Sources.getFileEntryForID(FID);
+
+  // -DMACRO definitions on the command line have locations in a virtual buffer
+  // that doesn't have a FileEntry. Don't skip these as well.
+  if (!File) {
+    LastErrorRelatesToUserCode = true;
+    LastErrorPassesLineFilter = true;
+    return;
+  }
+
+  StringRef FileName(File->getName());
+  LastErrorRelatesToUserCode = LastErrorRelatesToUserCode ||
+                               Sources.isInMainFile(Location) ||
+                               getHeaderFilter()->match(FileName);
+
+  unsigned LineNumber = Sources.getExpansionLineNumber(Location);
+  LastErrorPassesLineFilter =
+      LastErrorPassesLineFilter || passesLineFilter(FileName, LineNumber);
+}
+
+llvm::Regex *ClangTidyDiagnosticConsumer::getHeaderFilter() {
+  if (!HeaderFilter)
+    HeaderFilter =
+        llvm::make_unique<llvm::Regex>(*Context.getOptions().HeaderFilterRegex);
+  return HeaderFilter.get();
+}
+
+void ClangTidyDiagnosticConsumer::removeIncompatibleErrors(
+    SmallVectorImpl<ClangTidyError> &Errors) const {
+  // Each error is modelled as the set of intervals in which it applies
+  // replacements. To detect overlapping replacements, we use a sweep line
+  // algorithm over these sets of intervals.
+  // An event here consists of the opening or closing of an interval. During the
+  // process, we maintain a counter with the amount of open intervals. If we
+  // find an endpoint of an interval and this counter is different from 0, it
+  // means that this interval overlaps with another one, so we set it as
+  // inapplicable.
+  struct Event {
+    // An event can be either the begin or the end of an interval.
+    enum EventType {
+      ET_Begin = 1,
+      ET_End = -1,
+    };
+
+    Event(unsigned Begin, unsigned End, EventType Type, unsigned ErrorId,
+          unsigned ErrorSize)
+        : Type(Type), ErrorId(ErrorId) {
+      // The events are going to be sorted by their position. In case of draw:
+      //
+      // * If an interval ends at the same position at which other interval
+      //   begins, this is not an overlapping, so we want to remove the ending
+      //   interval before adding the starting one: end events have higher
+      //   priority than begin events.
+      //
+      // * If we have several begin points at the same position, we will mark as
+      //   inapplicable the ones that we process later, so the first one has to
+      //   be the one with the latest end point, because this one will contain
+      //   all the other intervals. For the same reason, if we have several end
+      //   points in the same position, the last one has to be the one with the
+      //   earliest begin point. In both cases, we sort non-increasingly by the
+      //   position of the complementary.
+      //
+      // * In case of two equal intervals, the one whose error is bigger can
+      //   potentially contain the other one, so we want to process its begin
+      //   points before and its end points later.
+      //
+      // * Finally, if we have two equal intervals whose errors have the same
+      //   size, none of them will be strictly contained inside the other.
+      //   Sorting by ErrorId will guarantee that the begin point of the first
+      //   one will be processed before, disallowing the second one, and the
+      //   end point of the first one will also be processed before,
+      //   disallowing the first one.
+      if (Type == ET_Begin)
+        Priority = std::make_tuple(Begin, Type, -End, -ErrorSize, ErrorId);
+      else
+        Priority = std::make_tuple(End, Type, -Begin, ErrorSize, ErrorId);
+    }
+
+    bool operator<(const Event &Other) const {
+      return Priority < Other.Priority;
+    }
+
+    // Determines if this event is the begin or the end of an interval.
+    EventType Type;
+    // The index of the error to which the interval that generated this event
+    // belongs.
+    unsigned ErrorId;
+    // The events will be sorted based on this field.
+    std::tuple<unsigned, EventType, int, int, unsigned> Priority;
+  };
+
+  // Compute error sizes.
+  std::vector<int> Sizes;
+  for (const auto &Error : Errors) {
+    int Size = 0;
+    for (const auto &FileAndReplaces : Error.Fix) {
+      for (const auto &Replace : FileAndReplaces.second)
+        Size += Replace.getLength();
+    }
+    Sizes.push_back(Size);
+  }
+
+  // Build events from error intervals.
+  std::map<std::string, std::vector<Event>> FileEvents;
+  for (unsigned I = 0; I < Errors.size(); ++I) {
+    for (const auto &FileAndReplace : Errors[I].Fix) {
+      for (const auto &Replace : FileAndReplace.second) {
+        unsigned Begin = Replace.getOffset();
+        unsigned End = Begin + Replace.getLength();
+        const std::string &FilePath = Replace.getFilePath();
+        // FIXME: Handle empty intervals, such as those from insertions.
+        if (Begin == End)
+          continue;
+        auto &Events = FileEvents[FilePath];
+        Events.emplace_back(Begin, End, Event::ET_Begin, I, Sizes[I]);
+        Events.emplace_back(Begin, End, Event::ET_End, I, Sizes[I]);
+      }
+    }
+  }
+
+  std::vector<bool> Apply(Errors.size(), true);
+  for (auto &FileAndEvents : FileEvents) {
+    std::vector<Event> &Events = FileAndEvents.second;
+    // Sweep.
+    std::sort(Events.begin(), Events.end());
+    int OpenIntervals = 0;
+    for (const auto &Event : Events) {
+      if (Event.Type == Event::ET_End)
+        --OpenIntervals;
+      // This has to be checked after removing the interval from the count if it
+      // is an end event, or before adding it if it is a begin event.
+      if (OpenIntervals != 0)
+        Apply[Event.ErrorId] = false;
+      if (Event.Type == Event::ET_Begin)
+        ++OpenIntervals;
+    }
+    assert(OpenIntervals == 0 && "Amount of begin/end points doesn't match");
+  }
+
+  for (unsigned I = 0; I < Errors.size(); ++I) {
+    if (!Apply[I]) {
+      Errors[I].Fix.clear();
+      Errors[I].Notes.emplace_back(
+          "this fix will not be applied because it overlaps with another fix");
+    }
+  }
+}
+
+namespace {
+struct LessClangTidyError {
+  bool operator()(const ClangTidyError &LHS, const ClangTidyError &RHS) const {
+    const tooling::DiagnosticMessage &M1 = LHS.Message;
+    const tooling::DiagnosticMessage &M2 = RHS.Message;
+
+    return std::tie(M1.FilePath, M1.FileOffset, M1.Message) <
+           std::tie(M2.FilePath, M2.FileOffset, M2.Message);
+  }
+};
+struct EqualClangTidyError {
+  bool operator()(const ClangTidyError &LHS, const ClangTidyError &RHS) const {
+    LessClangTidyError Less;
+    return !Less(LHS, RHS) && !Less(RHS, LHS);
+  }
+};
+} // end anonymous namespace
+
+// Flushes the internal diagnostics buffer to the ClangTidyContext.
+void ClangTidyDiagnosticConsumer::finish() {
+  finalizeLastError();
+
+  std::sort(Errors.begin(), Errors.end(), LessClangTidyError());
+  Errors.erase(std::unique(Errors.begin(), Errors.end(), EqualClangTidyError()),
+               Errors.end());
+
+  if (RemoveIncompatibleErrors)
+    removeIncompatibleErrors(Errors);
+
+  for (const ClangTidyError &Error : Errors)
+    Context.storeError(Error);
+  Errors.clear();
+}
diff --git a/clang-tidy/ClangTidyDiagnosticConsumer.h b/clang-tidy/ClangTidyDiagnosticConsumer.h
new file mode 100644 (file)
index 0000000..b484794
--- /dev/null
@@ -0,0 +1,268 @@
+//===--- ClangTidyDiagnosticConsumer.h - clang-tidy -------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYDIAGNOSTICCONSUMER_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYDIAGNOSTICCONSUMER_H
+
+#include "ClangTidyOptions.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Tooling/Core/Diagnostic.h"
+#include "clang/Tooling/Refactoring.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/Support/Regex.h"
+#include "llvm/Support/Timer.h"
+
+namespace clang {
+
+class ASTContext;
+class CompilerInstance;
+namespace ast_matchers {
+class MatchFinder;
+}
+namespace tooling {
+class CompilationDatabase;
+}
+
+namespace tidy {
+
+/// \brief A detected error complete with information to display diagnostic and
+/// automatic fix.
+///
+/// This is used as an intermediate format to transport Diagnostics without a
+/// dependency on a SourceManager.
+///
+/// FIXME: Make Diagnostics flexible enough to support this directly.
+struct ClangTidyError : tooling::Diagnostic {
+  ClangTidyError(StringRef CheckName, Level DiagLevel, StringRef BuildDirectory,
+                 bool IsWarningAsError);
+
+  bool IsWarningAsError;
+};
+
+/// \brief Read-only set of strings represented as a list of positive and
+/// negative globs. Positive globs add all matched strings to the set, negative
+/// globs remove them in the order of appearance in the list.
+class GlobList {
+public:
+  /// \brief \p GlobList is a comma-separated list of globs (only '*'
+  /// metacharacter is supported) with optional '-' prefix to denote exclusion.
+  GlobList(StringRef Globs);
+
+  /// \brief Returns \c true if the pattern matches \p S. The result is the last
+  /// matching glob's Positive flag.
+  bool contains(StringRef S) { return contains(S, false); }
+
+private:
+  bool contains(StringRef S, bool Contains);
+
+  bool Positive;
+  llvm::Regex Regex;
+  std::unique_ptr<GlobList> NextGlob;
+};
+
+/// \brief Contains displayed and ignored diagnostic counters for a ClangTidy
+/// run.
+struct ClangTidyStats {
+  ClangTidyStats()
+      : ErrorsDisplayed(0), ErrorsIgnoredCheckFilter(0), ErrorsIgnoredNOLINT(0),
+        ErrorsIgnoredNonUserCode(0), ErrorsIgnoredLineFilter(0) {}
+
+  unsigned ErrorsDisplayed;
+  unsigned ErrorsIgnoredCheckFilter;
+  unsigned ErrorsIgnoredNOLINT;
+  unsigned ErrorsIgnoredNonUserCode;
+  unsigned ErrorsIgnoredLineFilter;
+
+  unsigned errorsIgnored() const {
+    return ErrorsIgnoredNOLINT + ErrorsIgnoredCheckFilter +
+           ErrorsIgnoredNonUserCode + ErrorsIgnoredLineFilter;
+  }
+};
+
+/// \brief Container for clang-tidy profiling data.
+struct ProfileData {
+  llvm::StringMap<llvm::TimeRecord> Records;
+};
+
+/// \brief Every \c ClangTidyCheck reports errors through a \c DiagnosticsEngine
+/// provided by this context.
+///
+/// A \c ClangTidyCheck always has access to the active context to report
+/// warnings like:
+/// \code
+/// Context->Diag(Loc, "Single-argument constructors must be explicit")
+///     << FixItHint::CreateInsertion(Loc, "explicit ");
+/// \endcode
+class ClangTidyContext {
+public:
+  /// \brief Initializes \c ClangTidyContext instance.
+  ClangTidyContext(std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider);
+
+  ~ClangTidyContext();
+
+  /// \brief Report any errors detected using this method.
+  ///
+  /// This is still under heavy development and will likely change towards using
+  /// tablegen'd diagnostic IDs.
+  /// FIXME: Figure out a way to manage ID spaces.
+  DiagnosticBuilder diag(StringRef CheckName, SourceLocation Loc,
+                         StringRef Message,
+                         DiagnosticIDs::Level Level = DiagnosticIDs::Warning);
+
+  /// \brief Sets the \c SourceManager of the used \c DiagnosticsEngine.
+  ///
+  /// This is called from the \c ClangTidyCheck base class.
+  void setSourceManager(SourceManager *SourceMgr);
+
+  /// \brief Should be called when starting to process new translation unit.
+  void setCurrentFile(StringRef File);
+
+  /// \brief Returns the main file name of the current translation unit.
+  StringRef getCurrentFile() const { return CurrentFile; }
+
+  /// \brief Sets ASTContext for the current translation unit.
+  void setASTContext(ASTContext *Context);
+
+  /// \brief Gets the language options from the AST context.
+  const LangOptions &getLangOpts() const { return LangOpts; }
+
+  /// \brief Returns the name of the clang-tidy check which produced this
+  /// diagnostic ID.
+  StringRef getCheckName(unsigned DiagnosticID) const;
+
+  /// \brief Returns \c true if the check is enabled for the \c CurrentFile.
+  ///
+  /// The \c CurrentFile can be changed using \c setCurrentFile.
+  bool isCheckEnabled(StringRef CheckName) const;
+
+  /// \brief Returns \c true if the check should be upgraded to error for the
+  /// \c CurrentFile.
+  bool treatAsError(StringRef CheckName) const;
+
+  /// \brief Returns global options.
+  const ClangTidyGlobalOptions &getGlobalOptions() const;
+
+  /// \brief Returns options for \c CurrentFile.
+  ///
+  /// The \c CurrentFile can be changed using \c setCurrentFile.
+  const ClangTidyOptions &getOptions() const;
+
+  /// \brief Returns options for \c File. Does not change or depend on
+  /// \c CurrentFile.
+  ClangTidyOptions getOptionsForFile(StringRef File) const;
+
+  /// \brief Returns \c ClangTidyStats containing issued and ignored diagnostic
+  /// counters.
+  const ClangTidyStats &getStats() const { return Stats; }
+
+  /// \brief Returns all collected errors.
+  ArrayRef<ClangTidyError> getErrors() const { return Errors; }
+
+  /// \brief Clears collected errors.
+  void clearErrors() { Errors.clear(); }
+
+  /// \brief Set the output struct for profile data.
+  ///
+  /// Setting a non-null pointer here will enable profile collection in
+  /// clang-tidy.
+  void setCheckProfileData(ProfileData *Profile);
+  ProfileData *getCheckProfileData() const { return Profile; }
+
+  /// \brief Should be called when starting to process new translation unit.
+  void setCurrentBuildDirectory(StringRef BuildDirectory) {
+    CurrentBuildDirectory = BuildDirectory;
+  }
+
+  /// \brief Returns build directory of the current translation unit.
+  const std::string &getCurrentBuildDirectory() {
+    return CurrentBuildDirectory;
+  }
+
+private:
+  // Calls setDiagnosticsEngine() and storeError().
+  friend class ClangTidyDiagnosticConsumer;
+  friend class ClangTidyPluginAction;
+
+  /// \brief Sets the \c DiagnosticsEngine so that Diagnostics can be generated
+  /// correctly.
+  void setDiagnosticsEngine(DiagnosticsEngine *Engine);
+
+  /// \brief Store an \p Error.
+  void storeError(const ClangTidyError &Error);
+
+  std::vector<ClangTidyError> Errors;
+  DiagnosticsEngine *DiagEngine;
+  std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider;
+
+  std::string CurrentFile;
+  ClangTidyOptions CurrentOptions;
+  class CachedGlobList;
+  std::unique_ptr<CachedGlobList> CheckFilter;
+  std::unique_ptr<CachedGlobList> WarningAsErrorFilter;
+
+  LangOptions LangOpts;
+
+  ClangTidyStats Stats;
+
+  std::string CurrentBuildDirectory;
+
+  llvm::DenseMap<unsigned, std::string> CheckNamesByDiagnosticID;
+
+  ProfileData *Profile;
+};
+
+/// \brief A diagnostic consumer that turns each \c Diagnostic into a
+/// \c SourceManager-independent \c ClangTidyError.
+//
+// FIXME: If we move away from unit-tests, this can be moved to a private
+// implementation file.
+class ClangTidyDiagnosticConsumer : public DiagnosticConsumer {
+public:
+  ClangTidyDiagnosticConsumer(ClangTidyContext &Ctx,
+                              bool RemoveIncompatibleErrors = true);
+
+  // FIXME: The concept of converting between FixItHints and Replacements is
+  // more generic and should be pulled out into a more useful Diagnostics
+  // library.
+  void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
+                        const Diagnostic &Info) override;
+
+  /// \brief Flushes the internal diagnostics buffer to the ClangTidyContext.
+  void finish() override;
+
+private:
+  void finalizeLastError();
+
+  void removeIncompatibleErrors(SmallVectorImpl<ClangTidyError> &Errors) const;
+
+  /// \brief Returns the \c HeaderFilter constructed for the options set in the
+  /// context.
+  llvm::Regex *getHeaderFilter();
+
+  /// \brief Updates \c LastErrorRelatesToUserCode and LastErrorPassesLineFilter
+  /// according to the diagnostic \p Location.
+  void checkFilters(SourceLocation Location);
+  bool passesLineFilter(StringRef FileName, unsigned LineNumber) const;
+
+  ClangTidyContext &Context;
+  bool RemoveIncompatibleErrors;
+  std::unique_ptr<DiagnosticsEngine> Diags;
+  SmallVector<ClangTidyError, 8> Errors;
+  std::unique_ptr<llvm::Regex> HeaderFilter;
+  bool LastErrorRelatesToUserCode;
+  bool LastErrorPassesLineFilter;
+  bool LastErrorWasIgnored;
+};
+
+} // end namespace tidy
+} // end namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYDIAGNOSTICCONSUMER_H
diff --git a/clang-tidy/ClangTidyModule.cpp b/clang-tidy/ClangTidyModule.cpp
new file mode 100644 (file)
index 0000000..9dbf016
--- /dev/null
@@ -0,0 +1,38 @@
+//===--- tools/extra/clang-tidy/ClangTidyModule.cpp - Clang tidy tool -----===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+///  \file Implements classes required to build clang-tidy modules.
+///
+//===----------------------------------------------------------------------===//
+
+#include "ClangTidyModule.h"
+
+namespace clang {
+namespace tidy {
+
+void ClangTidyCheckFactories::registerCheckFactory(StringRef Name,
+                                                   CheckFactory Factory) {
+  Factories[Name] = std::move(Factory);
+}
+
+void ClangTidyCheckFactories::createChecks(
+    ClangTidyContext *Context,
+    std::vector<std::unique_ptr<ClangTidyCheck>> &Checks) {
+  for (const auto &Factory : Factories) {
+    if (Context->isCheckEnabled(Factory.first))
+      Checks.emplace_back(Factory.second(Factory.first, Context));
+  }
+}
+
+ClangTidyOptions ClangTidyModule::getModuleOptions() {
+  return ClangTidyOptions();
+}
+
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/ClangTidyModule.h b/clang-tidy/ClangTidyModule.h
new file mode 100644 (file)
index 0000000..4721636
--- /dev/null
@@ -0,0 +1,99 @@
+//===--- ClangTidyModule.h - clang-tidy -------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYMODULE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYMODULE_H
+
+#include "ClangTidy.h"
+#include "llvm/ADT/StringRef.h"
+#include <functional>
+#include <map>
+#include <string>
+#include <utility>
+
+namespace clang {
+namespace tidy {
+
+/// \brief A collection of \c ClangTidyCheckFactory instances.
+///
+/// All clang-tidy modules register their check factories with an instance of
+/// this object.
+class ClangTidyCheckFactories {
+public:
+  typedef std::function<ClangTidyCheck *(StringRef Name,
+                                         ClangTidyContext *Context)>
+      CheckFactory;
+
+  /// \brief Registers check \p Factory with name \p Name.
+  ///
+  /// For all checks that have default constructors, use \c registerCheck.
+  void registerCheckFactory(StringRef Name, CheckFactory Factory);
+
+  /// \brief Registers the \c CheckType with the name \p Name.
+  ///
+  /// This method should be used for all \c ClangTidyChecks that don't require
+  /// constructor parameters.
+  ///
+  /// For example, if have a clang-tidy check like:
+  /// \code
+  /// class MyTidyCheck : public ClangTidyCheck {
+  ///   void registerMatchers(ast_matchers::MatchFinder *Finder) override {
+  ///     ..
+  ///   }
+  /// };
+  /// \endcode
+  /// you can register it with:
+  /// \code
+  /// class MyModule : public ClangTidyModule {
+  ///   void addCheckFactories(ClangTidyCheckFactories &Factories) override {
+  ///     Factories.registerCheck<MyTidyCheck>("myproject-my-check");
+  ///   }
+  /// };
+  /// \endcode
+  template <typename CheckType> void registerCheck(StringRef CheckName) {
+    registerCheckFactory(CheckName,
+                         [](StringRef Name, ClangTidyContext *Context) {
+                           return new CheckType(Name, Context);
+                         });
+  }
+
+  /// \brief Create instances of all checks matching \p CheckRegexString and
+  /// store them in \p Checks.
+  ///
+  /// The caller takes ownership of the return \c ClangTidyChecks.
+  void createChecks(ClangTidyContext *Context,
+                    std::vector<std::unique_ptr<ClangTidyCheck>> &Checks);
+
+  typedef std::map<std::string, CheckFactory> FactoryMap;
+  FactoryMap::const_iterator begin() const { return Factories.begin(); }
+  FactoryMap::const_iterator end() const { return Factories.end(); }
+  bool empty() const { return Factories.empty(); }
+
+private:
+  FactoryMap Factories;
+};
+
+/// \brief A clang-tidy module groups a number of \c ClangTidyChecks and gives
+/// them a prefixed name.
+class ClangTidyModule {
+public:
+  virtual ~ClangTidyModule() {}
+
+  /// \brief Implement this function in order to register all \c CheckFactories
+  /// belonging to this module.
+  virtual void addCheckFactories(ClangTidyCheckFactories &CheckFactories) = 0;
+
+  /// \brief Gets default options for checks defined in this module.
+  virtual ClangTidyOptions getModuleOptions();
+};
+
+} // end namespace tidy
+} // end namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYMODULE_H
diff --git a/clang-tidy/ClangTidyModuleRegistry.h b/clang-tidy/ClangTidyModuleRegistry.h
new file mode 100644 (file)
index 0000000..dc44d14
--- /dev/null
@@ -0,0 +1,24 @@
+//===--- ClangTidyModuleRegistry.h - clang-tidy -----------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYMODULEREGISTRY_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYMODULEREGISTRY_H
+
+#include "ClangTidyModule.h"
+#include "llvm/Support/Registry.h"
+
+namespace clang {
+namespace tidy {
+
+typedef llvm::Registry<ClangTidyModule> ClangTidyModuleRegistry;
+
+} // end namespace tidy
+} // end namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYMODULEREGISTRY_H
diff --git a/clang-tidy/ClangTidyOptions.cpp b/clang-tidy/ClangTidyOptions.cpp
new file mode 100644 (file)
index 0000000..7cbe6ac
--- /dev/null
@@ -0,0 +1,340 @@
+//===--- ClangTidyOptions.cpp - clang-tidy ----------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ClangTidyOptions.h"
+#include "ClangTidyModuleRegistry.h"
+#include "clang/Basic/LLVM.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/YAMLTraits.h"
+#include "llvm/Support/raw_ostream.h"
+#include <utility>
+
+#define DEBUG_TYPE "clang-tidy-options"
+
+using clang::tidy::ClangTidyOptions;
+using clang::tidy::FileFilter;
+using OptionsSource = clang::tidy::ClangTidyOptionsProvider::OptionsSource;
+
+LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(FileFilter)
+LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(FileFilter::LineRange)
+
+namespace llvm {
+namespace yaml {
+
+// Map std::pair<int, int> to a JSON array of size 2.
+template <> struct SequenceTraits<FileFilter::LineRange> {
+  static size_t size(IO &IO, FileFilter::LineRange &Range) {
+    return Range.first == 0 ? 0 : Range.second == 0 ? 1 : 2;
+  }
+  static unsigned &element(IO &IO, FileFilter::LineRange &Range, size_t Index) {
+    if (Index > 1)
+      IO.setError("Too many elements in line range.");
+    return Index == 0 ? Range.first : Range.second;
+  }
+};
+
+template <> struct MappingTraits<FileFilter> {
+  static void mapping(IO &IO, FileFilter &File) {
+    IO.mapRequired("name", File.Name);
+    IO.mapOptional("lines", File.LineRanges);
+  }
+  static StringRef validate(IO &io, FileFilter &File) {
+    if (File.Name.empty())
+      return "No file name specified";
+    for (const FileFilter::LineRange &Range : File.LineRanges) {
+      if (Range.first <= 0 || Range.second <= 0)
+        return "Invalid line range";
+    }
+    return StringRef();
+  }
+};
+
+template <> struct MappingTraits<ClangTidyOptions::StringPair> {
+  static void mapping(IO &IO, ClangTidyOptions::StringPair &KeyValue) {
+    IO.mapRequired("key", KeyValue.first);
+    IO.mapRequired("value", KeyValue.second);
+  }
+};
+
+struct NOptionMap {
+  NOptionMap(IO &) {}
+  NOptionMap(IO &, const ClangTidyOptions::OptionMap &OptionMap)
+      : Options(OptionMap.begin(), OptionMap.end()) {}
+  ClangTidyOptions::OptionMap denormalize(IO &) {
+    ClangTidyOptions::OptionMap Map;
+    for (const auto &KeyValue : Options)
+      Map[KeyValue.first] = KeyValue.second;
+    return Map;
+  }
+  std::vector<ClangTidyOptions::StringPair> Options;
+};
+
+template <> struct MappingTraits<ClangTidyOptions> {
+  static void mapping(IO &IO, ClangTidyOptions &Options) {
+    MappingNormalization<NOptionMap, ClangTidyOptions::OptionMap> NOpts(
+        IO, Options.CheckOptions);
+    IO.mapOptional("Checks", Options.Checks);
+    IO.mapOptional("WarningsAsErrors", Options.WarningsAsErrors);
+    IO.mapOptional("HeaderFilterRegex", Options.HeaderFilterRegex);
+    IO.mapOptional("AnalyzeTemporaryDtors", Options.AnalyzeTemporaryDtors);
+    IO.mapOptional("FormatStyle", Options.FormatStyle);
+    IO.mapOptional("User", Options.User);
+    IO.mapOptional("CheckOptions", NOpts->Options);
+    IO.mapOptional("ExtraArgs", Options.ExtraArgs);
+    IO.mapOptional("ExtraArgsBefore", Options.ExtraArgsBefore);
+  }
+};
+
+} // namespace yaml
+} // namespace llvm
+
+namespace clang {
+namespace tidy {
+
+ClangTidyOptions ClangTidyOptions::getDefaults() {
+  ClangTidyOptions Options;
+  Options.Checks = "";
+  Options.WarningsAsErrors = "";
+  Options.HeaderFilterRegex = "";
+  Options.SystemHeaders = false;
+  Options.AnalyzeTemporaryDtors = false;
+  Options.FormatStyle = "none";
+  Options.User = llvm::None;
+  for (ClangTidyModuleRegistry::iterator I = ClangTidyModuleRegistry::begin(),
+                                         E = ClangTidyModuleRegistry::end();
+       I != E; ++I)
+    Options = Options.mergeWith(I->instantiate()->getModuleOptions());
+  return Options;
+}
+
+template <typename T>
+static void mergeVectors(Optional<T> &Dest, const Optional<T> &Src) {
+  if (Src) {
+    if (Dest)
+      Dest->insert(Dest->end(), Src->begin(), Src->end());
+    else
+      Dest = Src;
+  }
+}
+
+static void mergeCommaSeparatedLists(Optional<std::string> &Dest,
+                                     const Optional<std::string> &Src) {
+  if (Src)
+    Dest = (Dest && !Dest->empty() ? *Dest + "," : "") + *Src;
+}
+
+template <typename T>
+static void overrideValue(Optional<T> &Dest, const Optional<T> &Src) {
+  if (Src)
+    Dest = Src;
+}
+
+ClangTidyOptions
+ClangTidyOptions::mergeWith(const ClangTidyOptions &Other) const {
+  ClangTidyOptions Result = *this;
+
+  mergeCommaSeparatedLists(Result.Checks, Other.Checks);
+  mergeCommaSeparatedLists(Result.WarningsAsErrors, Other.WarningsAsErrors);
+  overrideValue(Result.HeaderFilterRegex, Other.HeaderFilterRegex);
+  overrideValue(Result.SystemHeaders, Other.SystemHeaders);
+  overrideValue(Result.AnalyzeTemporaryDtors, Other.AnalyzeTemporaryDtors);
+  overrideValue(Result.FormatStyle, Other.FormatStyle);
+  overrideValue(Result.User, Other.User);
+  mergeVectors(Result.ExtraArgs, Other.ExtraArgs);
+  mergeVectors(Result.ExtraArgsBefore, Other.ExtraArgsBefore);
+
+  for (const auto &KeyValue : Other.CheckOptions)
+    Result.CheckOptions[KeyValue.first] = KeyValue.second;
+
+  return Result;
+}
+
+const char ClangTidyOptionsProvider::OptionsSourceTypeDefaultBinary[] =
+    "clang-tidy binary";
+const char ClangTidyOptionsProvider::OptionsSourceTypeCheckCommandLineOption[] =
+    "command-line option '-checks'";
+const char
+    ClangTidyOptionsProvider::OptionsSourceTypeConfigCommandLineOption[] =
+        "command-line option '-config'";
+
+ClangTidyOptions
+ClangTidyOptionsProvider::getOptions(llvm::StringRef FileName) {
+  ClangTidyOptions Result;
+  for (const auto &Source : getRawOptions(FileName))
+    Result = Result.mergeWith(Source.first);
+  return Result;
+}
+
+std::vector<OptionsSource>
+DefaultOptionsProvider::getRawOptions(llvm::StringRef FileName) {
+  std::vector<OptionsSource> Result;
+  Result.emplace_back(DefaultOptions, OptionsSourceTypeDefaultBinary);
+  return Result;
+}
+
+ConfigOptionsProvider::ConfigOptionsProvider(
+    const ClangTidyGlobalOptions &GlobalOptions,
+    const ClangTidyOptions &DefaultOptions,
+    const ClangTidyOptions &ConfigOptions,
+    const ClangTidyOptions &OverrideOptions)
+    : DefaultOptionsProvider(GlobalOptions, DefaultOptions),
+      ConfigOptions(ConfigOptions), OverrideOptions(OverrideOptions) {}
+
+std::vector<OptionsSource>
+ConfigOptionsProvider::getRawOptions(llvm::StringRef FileName) {
+  std::vector<OptionsSource> RawOptions =
+      DefaultOptionsProvider::getRawOptions(FileName);
+  RawOptions.emplace_back(ConfigOptions,
+                          OptionsSourceTypeConfigCommandLineOption);
+  RawOptions.emplace_back(OverrideOptions,
+                          OptionsSourceTypeCheckCommandLineOption);
+  return RawOptions;
+}
+
+FileOptionsProvider::FileOptionsProvider(
+    const ClangTidyGlobalOptions &GlobalOptions,
+    const ClangTidyOptions &DefaultOptions,
+    const ClangTidyOptions &OverrideOptions)
+    : DefaultOptionsProvider(GlobalOptions, DefaultOptions),
+      OverrideOptions(OverrideOptions) {
+  ConfigHandlers.emplace_back(".clang-tidy", parseConfiguration);
+}
+
+FileOptionsProvider::FileOptionsProvider(
+    const ClangTidyGlobalOptions &GlobalOptions,
+    const ClangTidyOptions &DefaultOptions,
+    const ClangTidyOptions &OverrideOptions,
+    const FileOptionsProvider::ConfigFileHandlers &ConfigHandlers)
+    : DefaultOptionsProvider(GlobalOptions, DefaultOptions),
+      OverrideOptions(OverrideOptions), ConfigHandlers(ConfigHandlers) {}
+
+// FIXME: This method has some common logic with clang::format::getStyle().
+// Consider pulling out common bits to a findParentFileWithName function or
+// similar.
+std::vector<OptionsSource>
+FileOptionsProvider::getRawOptions(StringRef FileName) {
+  DEBUG(llvm::dbgs() << "Getting options for file " << FileName << "...\n");
+
+  std::vector<OptionsSource> RawOptions =
+      DefaultOptionsProvider::getRawOptions(FileName);
+  OptionsSource CommandLineOptions(OverrideOptions,
+                                   OptionsSourceTypeCheckCommandLineOption);
+  // Look for a suitable configuration file in all parent directories of the
+  // file. Start with the immediate parent directory and move up.
+  StringRef Path = llvm::sys::path::parent_path(FileName);
+  for (StringRef CurrentPath = Path; !CurrentPath.empty();
+       CurrentPath = llvm::sys::path::parent_path(CurrentPath)) {
+    llvm::Optional<OptionsSource> Result;
+
+    auto Iter = CachedOptions.find(CurrentPath);
+    if (Iter != CachedOptions.end())
+      Result = Iter->second;
+
+    if (!Result)
+      Result = tryReadConfigFile(CurrentPath);
+
+    if (Result) {
+      // Store cached value for all intermediate directories.
+      while (Path != CurrentPath) {
+        DEBUG(llvm::dbgs() << "Caching configuration for path " << Path
+                           << ".\n");
+        CachedOptions[Path] = *Result;
+        Path = llvm::sys::path::parent_path(Path);
+      }
+      CachedOptions[Path] = *Result;
+
+      RawOptions.push_back(*Result);
+      break;
+    }
+  }
+  RawOptions.push_back(CommandLineOptions);
+  return RawOptions;
+}
+
+llvm::Optional<OptionsSource>
+FileOptionsProvider::tryReadConfigFile(StringRef Directory) {
+  assert(!Directory.empty());
+
+  if (!llvm::sys::fs::is_directory(Directory)) {
+    llvm::errs() << "Error reading configuration from " << Directory
+                 << ": directory doesn't exist.\n";
+    return llvm::None;
+  }
+
+  for (const ConfigFileHandler &ConfigHandler : ConfigHandlers) {
+    SmallString<128> ConfigFile(Directory);
+    llvm::sys::path::append(ConfigFile, ConfigHandler.first);
+    DEBUG(llvm::dbgs() << "Trying " << ConfigFile << "...\n");
+
+    bool IsFile = false;
+    // Ignore errors from is_regular_file: we only need to know if we can read
+    // the file or not.
+    llvm::sys::fs::is_regular_file(Twine(ConfigFile), IsFile);
+    if (!IsFile)
+      continue;
+
+    llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Text =
+        llvm::MemoryBuffer::getFile(ConfigFile.c_str());
+    if (std::error_code EC = Text.getError()) {
+      llvm::errs() << "Can't read " << ConfigFile << ": " << EC.message()
+                   << "\n";
+      continue;
+    }
+
+    // Skip empty files, e.g. files opened for writing via shell output
+    // redirection.
+    if ((*Text)->getBuffer().empty())
+      continue;
+    llvm::ErrorOr<ClangTidyOptions> ParsedOptions =
+        ConfigHandler.second((*Text)->getBuffer());
+    if (!ParsedOptions) {
+      if (ParsedOptions.getError())
+        llvm::errs() << "Error parsing " << ConfigFile << ": "
+                     << ParsedOptions.getError().message() << "\n";
+      continue;
+    }
+    return OptionsSource(*ParsedOptions, ConfigFile.c_str());
+  }
+  return llvm::None;
+}
+
+/// \brief Parses -line-filter option and stores it to the \c Options.
+std::error_code parseLineFilter(StringRef LineFilter,
+                                clang::tidy::ClangTidyGlobalOptions &Options) {
+  llvm::yaml::Input Input(LineFilter);
+  Input >> Options.LineFilter;
+  return Input.error();
+}
+
+llvm::ErrorOr<ClangTidyOptions> parseConfiguration(StringRef Config) {
+  llvm::yaml::Input Input(Config);
+  ClangTidyOptions Options;
+  Input >> Options;
+  if (Input.error())
+    return Input.error();
+  return Options;
+}
+
+std::string configurationAsText(const ClangTidyOptions &Options) {
+  std::string Text;
+  llvm::raw_string_ostream Stream(Text);
+  llvm::yaml::Output Output(Stream);
+  // We use the same mapping method for input and output, so we need a non-const
+  // reference here.
+  ClangTidyOptions NonConstValue = Options;
+  Output << NonConstValue;
+  return Stream.str();
+}
+
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/ClangTidyOptions.h b/clang-tidy/ClangTidyOptions.h
new file mode 100644 (file)
index 0000000..d3f4de2
--- /dev/null
@@ -0,0 +1,274 @@
+//===--- ClangTidyOptions.h - clang-tidy ------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYOPTIONS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYOPTIONS_H
+
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/ErrorOr.h"
+#include <functional>
+#include <map>
+#include <string>
+#include <system_error>
+#include <utility>
+#include <vector>
+
+namespace clang {
+namespace tidy {
+
+/// \brief Contains a list of line ranges in a single file.
+struct FileFilter {
+  /// \brief File name.
+  std::string Name;
+
+  /// \brief LineRange is a pair<start, end> (inclusive).
+  typedef std::pair<unsigned, unsigned> LineRange;
+
+  /// \brief A list of line ranges in this file, for which we show warnings.
+  std::vector<LineRange> LineRanges;
+};
+
+/// \brief Global options. These options are neither stored nor read from
+/// configuration files.
+struct ClangTidyGlobalOptions {
+  /// \brief Output warnings from certain line ranges of certain files only.
+  /// If empty, no warnings will be filtered.
+  std::vector<FileFilter> LineFilter;
+};
+
+/// \brief Contains options for clang-tidy. These options may be read from
+/// configuration files, and may be different for different translation units.
+struct ClangTidyOptions {
+  /// \brief These options are used for all settings that haven't been
+  /// overridden by the \c OptionsProvider.
+  ///
+  /// Allow no checks and no headers by default. This method initializes
+  /// check-specific options by calling \c ClangTidyModule::getModuleOptions()
+  /// of each registered \c ClangTidyModule.
+  static ClangTidyOptions getDefaults();
+
+  /// \brief Creates a new \c ClangTidyOptions instance combined from all fields
+  /// of this instance overridden by the fields of \p Other that have a value.
+  ClangTidyOptions mergeWith(const ClangTidyOptions &Other) const;
+
+  /// \brief Checks filter.
+  llvm::Optional<std::string> Checks;
+
+  /// \brief WarningsAsErrors filter.
+  llvm::Optional<std::string> WarningsAsErrors;
+
+  /// \brief Output warnings from headers matching this filter. Warnings from
+  /// main files will always be displayed.
+  llvm::Optional<std::string> HeaderFilterRegex;
+
+  /// \brief Output warnings from system headers matching \c HeaderFilterRegex.
+  llvm::Optional<bool> SystemHeaders;
+
+  /// \brief Turns on temporary destructor-based analysis.
+  llvm::Optional<bool> AnalyzeTemporaryDtors;
+
+  /// \brief Format code around applied fixes with clang-format using this
+  /// style.
+  ///
+  /// Can be one of:
+  ///   * 'none' - don't format code around applied fixes;
+  ///   * 'llvm', 'google', 'mozilla' or other predefined clang-format style
+  ///     names;
+  ///   * 'file' - use the .clang-format file in the closest parent directory of
+  ///     each source file;
+  ///   * '{inline-formatting-style-in-yaml-format}'.
+  ///
+  /// See clang-format documentation for more about configuring format style.
+  llvm::Optional<std::string> FormatStyle;
+
+  /// \brief Specifies the name or e-mail of the user running clang-tidy.
+  ///
+  /// This option is used, for example, to place the correct user name in TODO()
+  /// comments in the relevant check.
+  llvm::Optional<std::string> User;
+
+  typedef std::pair<std::string, std::string> StringPair;
+  typedef std::map<std::string, std::string> OptionMap;
+
+  /// \brief Key-value mapping used to store check-specific options.
+  OptionMap CheckOptions;
+
+  typedef std::vector<std::string> ArgList;
+
+  /// \brief Add extra compilation arguments to the end of the list.
+  llvm::Optional<ArgList> ExtraArgs;
+
+  /// \brief Add extra compilation arguments to the start of the list.
+  llvm::Optional<ArgList> ExtraArgsBefore;
+};
+
+/// \brief Abstract interface for retrieving various ClangTidy options.
+class ClangTidyOptionsProvider {
+public:
+  static const char OptionsSourceTypeDefaultBinary[];
+  static const char OptionsSourceTypeCheckCommandLineOption[];
+  static const char OptionsSourceTypeConfigCommandLineOption[];
+
+  virtual ~ClangTidyOptionsProvider() {}
+
+  /// \brief Returns global options, which are independent of the file.
+  virtual const ClangTidyGlobalOptions &getGlobalOptions() = 0;
+
+  /// \brief ClangTidyOptions and its source.
+  //
+  /// clang-tidy has 3 types of the sources in order of increasing priority:
+  ///    * clang-tidy binary.
+  ///    * '-config' commandline option or a specific configuration file. If the
+  ///       commandline option is specified, clang-tidy will ignore the
+  ///       configuration file.
+  ///    * '-checks' commandline option.
+  typedef std::pair<ClangTidyOptions, std::string> OptionsSource;
+
+  /// \brief Returns an ordered vector of OptionsSources, in order of increasing
+  /// priority.
+  virtual std::vector<OptionsSource>
+  getRawOptions(llvm::StringRef FileName) = 0;
+
+  /// \brief Returns options applying to a specific translation unit with the
+  /// specified \p FileName.
+  ClangTidyOptions getOptions(llvm::StringRef FileName);
+};
+
+/// \brief Implementation of the \c ClangTidyOptionsProvider interface, which
+/// returns the same options for all files.
+class DefaultOptionsProvider : public ClangTidyOptionsProvider {
+public:
+  DefaultOptionsProvider(const ClangTidyGlobalOptions &GlobalOptions,
+                         const ClangTidyOptions &Options)
+      : GlobalOptions(GlobalOptions), DefaultOptions(Options) {}
+  const ClangTidyGlobalOptions &getGlobalOptions() override {
+    return GlobalOptions;
+  }
+  std::vector<OptionsSource> getRawOptions(llvm::StringRef FileName) override;
+
+private:
+  ClangTidyGlobalOptions GlobalOptions;
+  ClangTidyOptions DefaultOptions;
+};
+
+/// \brief Implementation of ClangTidyOptions interface, which is used for
+/// '-config' command-line option.
+class ConfigOptionsProvider : public DefaultOptionsProvider {
+public:
+  ConfigOptionsProvider(const ClangTidyGlobalOptions &GlobalOptions,
+                        const ClangTidyOptions &DefaultOptions,
+                        const ClangTidyOptions &ConfigOptions,
+                        const ClangTidyOptions &OverrideOptions);
+  std::vector<OptionsSource> getRawOptions(llvm::StringRef FileName) override;
+
+private:
+  ClangTidyOptions ConfigOptions;
+  ClangTidyOptions OverrideOptions;
+};
+
+/// \brief Implementation of the \c ClangTidyOptionsProvider interface, which
+/// tries to find a configuration file in the closest parent directory of each
+/// source file.
+///
+/// By default, files named ".clang-tidy" will be considered, and the
+/// \c clang::tidy::parseConfiguration function will be used for parsing, but a
+/// custom set of configuration file names and parsing functions can be
+/// specified using the appropriate constructor.
+class FileOptionsProvider : public DefaultOptionsProvider {
+public:
+  // \brief A pair of configuration file base name and a function parsing
+  // configuration from text in the corresponding format.
+  typedef std::pair<std::string, std::function<llvm::ErrorOr<ClangTidyOptions>(
+                                     llvm::StringRef)>>
+      ConfigFileHandler;
+
+  /// \brief Configuration file handlers listed in the order of priority.
+  ///
+  /// Custom configuration file formats can be supported by constructing the
+  /// list of handlers and passing it to the appropriate \c FileOptionsProvider
+  /// constructor. E.g. initialization of a \c FileOptionsProvider with support
+  /// of a custom configuration file format for files named ".my-tidy-config"
+  /// could look similar to this:
+  /// \code
+  /// FileOptionsProvider::ConfigFileHandlers ConfigHandlers;
+  /// ConfigHandlers.emplace_back(".my-tidy-config", parseMyConfigFormat);
+  /// ConfigHandlers.emplace_back(".clang-tidy", parseConfiguration);
+  /// return llvm::make_unique<FileOptionsProvider>(
+  ///     GlobalOptions, DefaultOptions, OverrideOptions, ConfigHandlers);
+  /// \endcode
+  ///
+  /// With the order of handlers shown above, the ".my-tidy-config" file would
+  /// take precedence over ".clang-tidy" if both reside in the same directory.
+  typedef std::vector<ConfigFileHandler> ConfigFileHandlers;
+
+  /// \brief Initializes the \c FileOptionsProvider instance.
+  ///
+  /// \param GlobalOptions are just stored and returned to the caller of
+  /// \c getGlobalOptions.
+  ///
+  /// \param DefaultOptions are used for all settings not specified in a
+  /// configuration file.
+  ///
+  /// If any of the \param OverrideOptions fields are set, they will override
+  /// whatever options are read from the configuration file.
+  FileOptionsProvider(const ClangTidyGlobalOptions &GlobalOptions,
+                      const ClangTidyOptions &DefaultOptions,
+                      const ClangTidyOptions &OverrideOptions);
+
+  /// \brief Initializes the \c FileOptionsProvider instance with a custom set
+  /// of configuration file handlers.
+  ///
+  /// \param GlobalOptions are just stored and returned to the caller of
+  /// \c getGlobalOptions.
+  ///
+  /// \param DefaultOptions are used for all settings not specified in a
+  /// configuration file.
+  ///
+  /// If any of the \param OverrideOptions fields are set, they will override
+  /// whatever options are read from the configuration file.
+  ///
+  /// \param ConfigHandlers specifies a custom set of configuration file
+  /// handlers. Each handler is a pair of configuration file name and a function
+  /// that can parse configuration from this file type. The configuration files
+  /// in each directory are searched for in the order of appearance in
+  /// \p ConfigHandlers.
+  FileOptionsProvider(const ClangTidyGlobalOptions &GlobalOptions,
+                      const ClangTidyOptions &DefaultOptions,
+                      const ClangTidyOptions &OverrideOptions,
+                      const ConfigFileHandlers &ConfigHandlers);
+
+  std::vector<OptionsSource> getRawOptions(llvm::StringRef FileName) override;
+
+protected:
+  /// \brief Try to read configuration files from \p Directory using registered
+  /// \c ConfigHandlers.
+  llvm::Optional<OptionsSource> tryReadConfigFile(llvm::StringRef Directory);
+
+  llvm::StringMap<OptionsSource> CachedOptions;
+  ClangTidyOptions OverrideOptions;
+  ConfigFileHandlers ConfigHandlers;
+};
+
+/// \brief Parses LineFilter from JSON and stores it to the \p Options.
+std::error_code parseLineFilter(llvm::StringRef LineFilter,
+                                ClangTidyGlobalOptions &Options);
+
+/// \brief Parses configuration from JSON and returns \c ClangTidyOptions or an
+/// error.
+llvm::ErrorOr<ClangTidyOptions> parseConfiguration(llvm::StringRef Config);
+
+/// \brief Serializes configuration to a YAML-encoded string.
+std::string configurationAsText(const ClangTidyOptions &Options);
+
+} // end namespace tidy
+} // end namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYOPTIONS_H
diff --git a/clang-tidy/add_new_check.py b/clang-tidy/add_new_check.py
new file mode 100755 (executable)
index 0000000..c7d212e
--- /dev/null
@@ -0,0 +1,342 @@
+#!/usr/bin/env python
+#
+#===- add_new_check.py - clang-tidy check generator ----------*- python -*--===#
+#
+#                     The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+#
+#===------------------------------------------------------------------------===#
+
+import os
+import re
+import sys
+
+
+# Adapts the module's CMakelist file. Returns 'True' if it could add a new entry
+# and 'False' if the entry already existed.
+def adapt_cmake(module_path, check_name_camel):
+  filename = os.path.join(module_path, 'CMakeLists.txt')
+  with open(filename, 'r') as f:
+    lines = f.readlines()
+
+  cpp_file = check_name_camel + '.cpp'
+
+  # Figure out whether this check already exists.
+  for line in lines:
+    if line.strip() == cpp_file:
+      return False
+
+  print('Updating %s...' % filename)
+  with open(filename, 'wb') as f:
+    cpp_found = False
+    file_added = False
+    for line in lines:
+      cpp_line = line.strip().endswith('.cpp')
+      if (not file_added) and (cpp_line or cpp_found):
+        cpp_found = True
+        if (line.strip() > cpp_file) or (not cpp_line):
+          f.write('  ' + cpp_file + '\n')
+          file_added = True
+      f.write(line)
+
+  return True
+
+
+# Adds a header for the new check.
+def write_header(module_path, module, check_name, check_name_camel):
+  check_name_dashes = module + '-' + check_name
+  filename = os.path.join(module_path, check_name_camel) + '.h'
+  print('Creating %s...' % filename)
+  with open(filename, 'wb') as f:
+    header_guard = ('LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_' + module.upper() + '_'
+                    + check_name.upper().replace('-', '_') + '_H')
+    f.write('//===--- ')
+    f.write(os.path.basename(filename))
+    f.write(' - clang-tidy')
+    f.write('-' * max(0, 43 - len(os.path.basename(filename))))
+    f.write('*- C++ -*-===//')
+    f.write("""
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef %(header_guard)s
+#define %(header_guard)s
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace %(module)s {
+
+/// FIXME: Write a short description.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/%(check_name_dashes)s.html
+class %(check_name)s : public ClangTidyCheck {
+public:
+  %(check_name)s(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace %(module)s
+} // namespace tidy
+} // namespace clang
+
+#endif // %(header_guard)s
+""" % {'header_guard': header_guard,
+       'check_name': check_name_camel,
+       'check_name_dashes': check_name_dashes,
+       'module': module})
+
+
+# Adds the implementation of the new check.
+def write_implementation(module_path, module, check_name_camel):
+  filename = os.path.join(module_path, check_name_camel) + '.cpp'
+  print('Creating %s...' % filename)
+  with open(filename, 'wb') as f:
+    f.write('//===--- ')
+    f.write(os.path.basename(filename))
+    f.write(' - clang-tidy')
+    f.write('-' * max(0, 52 - len(os.path.basename(filename))))
+    f.write('-===//')
+    f.write("""
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "%(check_name)s.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace %(module)s {
+
+void %(check_name)s::registerMatchers(MatchFinder *Finder) {
+  // FIXME: Add matchers.
+  Finder->addMatcher(functionDecl().bind("x"), this);
+}
+
+void %(check_name)s::check(const MatchFinder::MatchResult &Result) {
+  // FIXME: Add callback implementation.
+  const auto *MatchedDecl = Result.Nodes.getNodeAs<FunctionDecl>("x");
+  if (MatchedDecl->getName().startswith("awesome_"))
+    return;
+  diag(MatchedDecl->getLocation(), "function %%0 is insufficiently awesome")
+      << MatchedDecl
+      << FixItHint::CreateInsertion(MatchedDecl->getLocation(), "awesome_");
+}
+
+} // namespace %(module)s
+} // namespace tidy
+} // namespace clang
+""" % {'check_name': check_name_camel,
+       'module': module})
+
+
+# Modifies the module to include the new check.
+def adapt_module(module_path, module, check_name, check_name_camel):
+  modulecpp = filter(lambda p: p.lower() == module.lower() + 'tidymodule.cpp',
+                     os.listdir(module_path))[0]
+  filename = os.path.join(module_path, modulecpp)
+  with open(filename, 'r') as f:
+    lines = f.readlines()
+
+  print('Updating %s...' % filename)
+  with open(filename, 'wb') as f:
+    header_added = False
+    header_found = False
+    check_added = False
+    check_decl = ('    CheckFactories.registerCheck<' + check_name_camel +
+                  '>(\n        "' + module + '-' + check_name + '");\n')
+
+    for line in lines:
+      if not header_added:
+        match = re.search('#include "(.*)"', line)
+        if match:
+          header_found = True
+          if match.group(1) > check_name_camel:
+            header_added = True
+            f.write('#include "' + check_name_camel + '.h"\n')
+        elif header_found:
+          header_added = True
+          f.write('#include "' + check_name_camel + '.h"\n')
+
+      if not check_added:
+        if line.strip() == '}':
+          check_added = True
+          f.write(check_decl)
+        else:
+          match = re.search('registerCheck<(.*)>', line)
+          if match and match.group(1) > check_name_camel:
+            check_added = True
+            f.write(check_decl)
+      f.write(line)
+
+
+# Adds a release notes entry.
+def add_release_notes(module_path, module, check_name):
+  check_name_dashes = module + '-' + check_name
+  filename = os.path.normpath(os.path.join(module_path,
+                                           '../../docs/ReleaseNotes.rst'))
+  with open(filename, 'r') as f:
+    lines = f.readlines()
+
+  print('Updating %s...' % filename)
+  with open(filename, 'wb') as f:
+    note_added = False
+    header_found = False
+
+    for line in lines:
+      if not note_added:
+        match = re.search('Improvements to clang-tidy', line)
+        if match:
+          header_found = True
+        elif header_found:
+          if not line.startswith('----'):
+            f.write("""
+- New `%s
+  <http://clang.llvm.org/extra/clang-tidy/checks/%s.html>`_ check
+
+  FIXME: add release notes.
+""" % (check_name_dashes, check_name_dashes))
+            note_added = True
+
+      f.write(line)
+
+
+# Adds a test for the check.
+def write_test(module_path, module, check_name):
+  check_name_dashes = module + '-' + check_name
+  filename = os.path.normpath(os.path.join(module_path, '../../test/clang-tidy',
+                                           check_name_dashes + '.cpp'))
+  print('Creating %s...' % filename)
+  with open(filename, 'wb') as f:
+    f.write("""// RUN: %%check_clang_tidy %%s %(check_name_dashes)s %%t
+
+// FIXME: Add something that triggers the check here.
+void f();
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'f' is insufficiently awesome [%(check_name_dashes)s]
+
+// FIXME: Verify the applied fix.
+//   * Make the CHECK patterns specific enough and try to make verified lines
+//     unique to avoid incorrect matches.
+//   * Use {{}} for regular expressions.
+// CHECK-FIXES: {{^}}void awesome_f();{{$}}
+
+// FIXME: Add something that doesn't trigger the check here.
+void awesome_f2();
+""" % {'check_name_dashes': check_name_dashes})
+
+
+# Recreates the list of checks in the docs/clang-tidy/checks directory.
+def update_checks_list(clang_tidy_path):
+  docs_dir = os.path.join(clang_tidy_path, '../docs/clang-tidy/checks')
+  filename = os.path.normpath(os.path.join(docs_dir, 'list.rst'))
+  with open(filename, 'r') as f:
+    lines = f.readlines()
+  doc_files = filter(lambda s: s.endswith('.rst') and s != 'list.rst',
+                     os.listdir(docs_dir))
+  doc_files.sort()
+
+  def format_link(doc_file):
+    check_name = doc_file.replace('.rst', '')
+    with open(os.path.join(docs_dir, doc_file), 'r') as doc:
+      content = doc.read()
+      match = re.search('.*:orphan:.*', content)
+      if match:
+        return ''
+
+      match = re.search('.*:http-equiv=refresh: \d+;URL=(.*).html.*',
+                        content)
+      if match:
+        return '   %(check)s (redirects to %(target)s) <%(check)s>\n' % {
+            'check': check_name,
+            'target': match.group(1)
+        }
+      return '   %s\n' % check_name
+
+  checks = map(format_link, doc_files)
+
+  print('Updating %s...' % filename)
+  with open(filename, 'wb') as f:
+    for line in lines:
+      f.write(line)
+      if line.startswith('.. toctree::'):
+        f.writelines(checks)
+        break
+
+
+# Adds a documentation for the check.
+def write_docs(module_path, module, check_name):
+  check_name_dashes = module + '-' + check_name
+  filename = os.path.normpath(os.path.join(
+      module_path, '../../docs/clang-tidy/checks/', check_name_dashes + '.rst'))
+  print('Creating %s...' % filename)
+  with open(filename, 'wb') as f:
+    f.write(""".. title:: clang-tidy - %(check_name_dashes)s
+
+%(check_name_dashes)s
+%(underline)s
+
+FIXME: Describe what patterns does the check detect and why. Give examples.
+""" % {'check_name_dashes': check_name_dashes,
+       'underline': '=' * len(check_name_dashes)})
+
+
+def main():
+  if len(sys.argv) == 2 and sys.argv[1] == '--update-docs':
+    update_checks_list(os.path.dirname(sys.argv[0]))
+    return
+
+  if len(sys.argv) != 3:
+    print """\
+Usage: add_new_check.py <module> <check>, e.g.
+  add_new_check.py misc awesome-functions
+
+Alternatively, run 'add_new_check.py --update-docs' to just update the list of
+documentation files."""
+
+    return
+
+  module = sys.argv[1]
+  check_name = sys.argv[2]
+
+  if check_name.startswith(module):
+    print 'Check name "%s" must not start with the module "%s". Exiting.' % (
+        check_name, module)
+    return
+  check_name_camel = ''.join(map(lambda elem: elem.capitalize(),
+                                 check_name.split('-'))) + 'Check'
+  clang_tidy_path = os.path.dirname(sys.argv[0])
+  module_path = os.path.join(clang_tidy_path, module)
+
+  if not adapt_cmake(module_path, check_name_camel):
+    return
+  write_header(module_path, module, check_name, check_name_camel)
+  write_implementation(module_path, module, check_name_camel)
+  adapt_module(module_path, module, check_name, check_name_camel)
+  add_release_notes(module_path, module, check_name)
+  write_test(module_path, module, check_name)
+  write_docs(module_path, module, check_name)
+  update_checks_list(clang_tidy_path)
+  print('Done. Now it\'s your turn!')
+
+
+if __name__ == '__main__':
+  main()
diff --git a/clang-tidy/android/AndroidTidyModule.cpp b/clang-tidy/android/AndroidTidyModule.cpp
new file mode 100644 (file)
index 0000000..119f3c0
--- /dev/null
@@ -0,0 +1,46 @@
+//===--- AndroidTidyModule.cpp - clang-tidy--------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "../ClangTidy.h"
+#include "../ClangTidyModule.h"
+#include "../ClangTidyModuleRegistry.h"
+#include "CloexecCreatCheck.h"
+#include "CloexecFopenCheck.h"
+#include "CloexecOpenCheck.h"
+#include "CloexecSocketCheck.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace android {
+
+/// This module is for Android specific checks.
+class AndroidModule : public ClangTidyModule {
+public:
+  void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
+    CheckFactories.registerCheck<CloexecCreatCheck>("android-cloexec-creat");
+    CheckFactories.registerCheck<CloexecFopenCheck>("android-cloexec-fopen");
+    CheckFactories.registerCheck<CloexecOpenCheck>("android-cloexec-open");
+    CheckFactories.registerCheck<CloexecSocketCheck>("android-cloexec-socket");
+  }
+};
+
+// Register the AndroidTidyModule using this statically initialized variable.
+static ClangTidyModuleRegistry::Add<AndroidModule>
+    X("android-module", "Adds Android platform checks.");
+
+} // namespace android
+
+// This anchor is used to force the linker to link in the generated object file
+// and thus register the AndroidModule.
+volatile int AndroidModuleAnchorSource = 0;
+
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/android/CMakeLists.txt b/clang-tidy/android/CMakeLists.txt
new file mode 100644 (file)
index 0000000..a68c2d2
--- /dev/null
@@ -0,0 +1,17 @@
+set(LLVM_LINK_COMPONENTS support)
+
+add_clang_library(clangTidyAndroidModule
+  AndroidTidyModule.cpp
+  CloexecCreatCheck.cpp
+  CloexecFopenCheck.cpp
+  CloexecOpenCheck.cpp
+  CloexecSocketCheck.cpp
+
+  LINK_LIBS
+  clangAST
+  clangASTMatchers
+  clangBasic
+  clangLex
+  clangTidy
+  clangTidyUtils
+  )
diff --git a/clang-tidy/android/CloexecCreatCheck.cpp b/clang-tidy/android/CloexecCreatCheck.cpp
new file mode 100644 (file)
index 0000000..9b6ccf2
--- /dev/null
@@ -0,0 +1,59 @@
+//===--- CloexecCreatCheck.cpp - clang-tidy--------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CloexecCreatCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace android {
+
+void CloexecCreatCheck::registerMatchers(MatchFinder *Finder) {
+  auto CharPointerType = hasType(pointerType(pointee(isAnyCharacter())));
+  auto MODETType = hasType(namedDecl(hasName("mode_t")));
+
+  Finder->addMatcher(
+      callExpr(callee(functionDecl(isExternC(), returns(isInteger()),
+                                   hasName("creat"),
+                                   hasParameter(0, CharPointerType),
+                                   hasParameter(1, MODETType))
+                          .bind("funcDecl")))
+          .bind("creatFn"),
+      this);
+}
+
+void CloexecCreatCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *MatchedCall = Result.Nodes.getNodeAs<CallExpr>("creatFn");
+  const SourceManager &SM = *Result.SourceManager;
+
+  const std::string &ReplacementText =
+      (Twine("open (") +
+       Lexer::getSourceText(CharSourceRange::getTokenRange(
+                                MatchedCall->getArg(0)->getSourceRange()),
+                            SM, Result.Context->getLangOpts()) +
+       ", O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, " +
+       Lexer::getSourceText(CharSourceRange::getTokenRange(
+                                MatchedCall->getArg(1)->getSourceRange()),
+                            SM, Result.Context->getLangOpts()) +
+       ")")
+          .str();
+
+  diag(MatchedCall->getLocStart(),
+       "prefer open() to creat() because open() allows O_CLOEXEC")
+      << FixItHint::CreateReplacement(MatchedCall->getSourceRange(),
+                                      ReplacementText);
+}
+
+} // namespace android
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/android/CloexecCreatCheck.h b/clang-tidy/android/CloexecCreatCheck.h
new file mode 100644 (file)
index 0000000..bb015bb
--- /dev/null
@@ -0,0 +1,35 @@
+//===--- CloexecCreatCheck.h - clang-tidy------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ANDROID_CLOEXEC_CREAT_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ANDROID_CLOEXEC_CREAT_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace android {
+
+/// creat() is better to be replaced by open().
+/// Find the usage of creat() and redirect user to use open().
+
+/// http://clang.llvm.org/extra/clang-tidy/checks/android-cloexec-creat.html
+class CloexecCreatCheck : public ClangTidyCheck {
+public:
+  CloexecCreatCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace android
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ANDROID_CLOEXEC_CREAT_H
diff --git a/clang-tidy/android/CloexecFopenCheck.cpp b/clang-tidy/android/CloexecFopenCheck.cpp
new file mode 100644 (file)
index 0000000..06dc5bd
--- /dev/null
@@ -0,0 +1,74 @@
+//===--- CloexecFopenCheck.cpp - clang-tidy--------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.  //
+//===----------------------------------------------------------------------===//
+
+#include "CloexecFopenCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Type.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace android {
+
+namespace {
+static const char MODE = 'e';
+
+// Build the replace text. If it's string constant, add 'e' directly in the end
+// of the string. Else, add "e".
+std::string BuildReplaceText(const Expr *Arg, const SourceManager &SM,
+                             const LangOptions &LangOpts) {
+  if (Arg->getLocStart().isMacroID())
+    return (Lexer::getSourceText(
+                CharSourceRange::getTokenRange(Arg->getSourceRange()), SM,
+                LangOpts) +
+            " \"" + Twine(MODE) + "\"")
+        .str();
+
+  StringRef SR = cast<StringLiteral>(Arg->IgnoreParenCasts())->getString();
+  return ("\"" + SR + Twine(MODE) + "\"").str();
+}
+} // namespace
+
+void CloexecFopenCheck::registerMatchers(MatchFinder *Finder) {
+  auto CharPointerType = hasType(pointerType(pointee(isAnyCharacter())));
+
+  Finder->addMatcher(
+      callExpr(callee(functionDecl(isExternC(), returns(asString("FILE *")),
+                                   hasName("fopen"),
+                                   hasParameter(0, CharPointerType),
+                                   hasParameter(1, CharPointerType))
+                          .bind("funcDecl")))
+          .bind("fopenFn"),
+      this);
+}
+
+void CloexecFopenCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *MatchedCall = Result.Nodes.getNodeAs<CallExpr>("fopenFn");
+  const auto *FD = Result.Nodes.getNodeAs<FunctionDecl>("funcDecl");
+  const Expr *ModeArg = MatchedCall->getArg(1);
+
+  // Check if the 'e' may be in the mode string.
+  const auto *ModeStr = dyn_cast<StringLiteral>(ModeArg->IgnoreParenCasts());
+  if (!ModeStr || (ModeStr->getString().find(MODE) != StringRef::npos))
+    return;
+
+  const std::string &ReplacementText = BuildReplaceText(
+      ModeArg, *Result.SourceManager, Result.Context->getLangOpts());
+
+  diag(ModeArg->getLocStart(), "use %0 mode 'e' to set O_CLOEXEC")
+      << FD
+      << FixItHint::CreateReplacement(ModeArg->getSourceRange(),
+                                      ReplacementText);
+}
+
+} // namespace android
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/android/CloexecFopenCheck.h b/clang-tidy/android/CloexecFopenCheck.h
new file mode 100644 (file)
index 0000000..71f015a
--- /dev/null
@@ -0,0 +1,38 @@
+//===--- CloexecFopenCheck.h - clang-tidy------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source //
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ANDROID_CLOEXEC_FOPEN_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ANDROID_CLOEXEC_FOPEN_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace android {
+
+/// fopen() is suggested to include "e" in their mode string; like "re" would be
+/// better than "r".
+///
+/// This check only works when corresponding argument is StringLiteral. No
+/// constant propagation.
+///
+/// http://clang.llvm.org/extra/clang-tidy/checks/android-cloexec-fopen.html
+class CloexecFopenCheck : public ClangTidyCheck {
+public:
+  CloexecFopenCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace android
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ANDROID_CLOEXEC_FOPEN_H
diff --git a/clang-tidy/android/CloexecOpenCheck.cpp b/clang-tidy/android/CloexecOpenCheck.cpp
new file mode 100644 (file)
index 0000000..6d944ae
--- /dev/null
@@ -0,0 +1,74 @@
+//===--- CloexecOpenCheck.cpp - clang-tidy---------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CloexecOpenCheck.h"
+#include "../utils/ASTUtils.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace android {
+
+static constexpr const char *O_CLOEXEC = "O_CLOEXEC";
+
+void CloexecOpenCheck::registerMatchers(MatchFinder *Finder) {
+  auto CharPointerType = hasType(pointerType(pointee(isAnyCharacter())));
+
+  Finder->addMatcher(
+      callExpr(callee(functionDecl(isExternC(), returns(isInteger()),
+                                   hasAnyName("open", "open64"),
+                                   hasParameter(0, CharPointerType),
+                                   hasParameter(1, hasType(isInteger())))
+                          .bind("funcDecl")))
+          .bind("openFn"),
+      this);
+  Finder->addMatcher(
+      callExpr(callee(functionDecl(isExternC(), returns(isInteger()),
+                                   hasName("openat"),
+                                   hasParameter(0, hasType(isInteger())),
+                                   hasParameter(1, CharPointerType),
+                                   hasParameter(2, hasType(isInteger())))
+                          .bind("funcDecl")))
+          .bind("openatFn"),
+      this);
+}
+
+void CloexecOpenCheck::check(const MatchFinder::MatchResult &Result) {
+  const Expr *FlagArg = nullptr;
+  if (const auto *OpenFnCall = Result.Nodes.getNodeAs<CallExpr>("openFn"))
+    FlagArg = OpenFnCall->getArg(1);
+  else if (const auto *OpenFnCall =
+               Result.Nodes.getNodeAs<CallExpr>("openatFn"))
+    FlagArg = OpenFnCall->getArg(2);
+  assert(FlagArg);
+
+  const auto *FD = Result.Nodes.getNodeAs<FunctionDecl>("funcDecl");
+
+  // Check the required flag.
+  SourceManager &SM = *Result.SourceManager;
+  if (utils::exprHasBitFlagWithSpelling(FlagArg->IgnoreParenCasts(), SM,
+                     Result.Context->getLangOpts(), O_CLOEXEC))
+    return;
+
+  SourceLocation EndLoc =
+      Lexer::getLocForEndOfToken(SM.getFileLoc(FlagArg->getLocEnd()), 0, SM,
+                                 Result.Context->getLangOpts());
+
+  diag(EndLoc, "%0 should use %1 where possible")
+      << FD << O_CLOEXEC
+      << FixItHint::CreateInsertion(EndLoc, (Twine(" | ") + O_CLOEXEC).str());
+}
+
+} // namespace android
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/android/CloexecOpenCheck.h b/clang-tidy/android/CloexecOpenCheck.h
new file mode 100644 (file)
index 0000000..9928177
--- /dev/null
@@ -0,0 +1,40 @@
+//===--- CloexecOpenCheck.h - clang-tidy-----------------------------------===//
+//
+//                      The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ANDROID_CLOEXEC_OPEN_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ANDROID_CLOEXEC_OPEN_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace android {
+
+/// Finds code that opens file without using the O_CLOEXEC flag.
+///
+/// open(), openat(), and open64() had better to include O_CLOEXEC in their
+/// flags argument. Only consider simple cases that the corresponding argument
+/// is constant or binary operation OR among constants like 'O_CLOEXEC' or
+/// 'O_CLOEXEC | O_RDONLY'. No constant propagation is performed.
+///
+/// Only the symbolic 'O_CLOEXEC' macro definition is checked, not the concrete
+/// value.
+class CloexecOpenCheck : public ClangTidyCheck {
+public:
+  CloexecOpenCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace android
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ANDROID_CLOEXEC_OPEN_H
diff --git a/clang-tidy/android/CloexecSocketCheck.cpp b/clang-tidy/android/CloexecSocketCheck.cpp
new file mode 100644 (file)
index 0000000..66a77b8
--- /dev/null
@@ -0,0 +1,57 @@
+//===--- CloexecSocketCheck.cpp - clang-tidy-------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CloexecSocketCheck.h"
+#include "../utils/ASTUtils.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace android {
+
+static constexpr const char *SOCK_CLOEXEC = "SOCK_CLOEXEC";
+
+void CloexecSocketCheck::registerMatchers(MatchFinder *Finder) {
+  Finder->addMatcher(
+      callExpr(callee(functionDecl(isExternC(), returns(isInteger()),
+                                   hasName("socket"),
+                                   hasParameter(0, hasType(isInteger())),
+                                   hasParameter(1, hasType(isInteger())),
+                                   hasParameter(2, hasType(isInteger())))
+                          .bind("funcDecl")))
+          .bind("socketFn"),
+      this);
+}
+
+void CloexecSocketCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *MatchedCall = Result.Nodes.getNodeAs<CallExpr>("socketFn");
+  const auto *FD = Result.Nodes.getNodeAs<FunctionDecl>("funcDecl");
+  const Expr *FlagArg = MatchedCall->getArg(1);
+  SourceManager &SM = *Result.SourceManager;
+
+  if (utils::exprHasBitFlagWithSpelling(FlagArg->IgnoreParenCasts(), SM,
+                     Result.Context->getLangOpts(), SOCK_CLOEXEC))
+    return;
+
+  SourceLocation EndLoc =
+      Lexer::getLocForEndOfToken(SM.getFileLoc(FlagArg->getLocEnd()), 0, SM,
+                                 Result.Context->getLangOpts());
+
+  diag(EndLoc, "%0 should use %1 where possible")
+      << FD << SOCK_CLOEXEC
+      << FixItHint::CreateInsertion(EndLoc,
+                                    (Twine(" | ") + SOCK_CLOEXEC).str());
+}
+
+} // namespace android
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/android/CloexecSocketCheck.h b/clang-tidy/android/CloexecSocketCheck.h
new file mode 100644 (file)
index 0000000..9334a64
--- /dev/null
@@ -0,0 +1,35 @@
+//===--- CloexecSocketCheck.h - clang-tidy-----------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ANDROID_CLOEXEC_SOCKET_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ANDROID_CLOEXEC_SOCKET_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace android {
+
+/// Finds code that uses socket() without using the SOCK_CLOEXEC flag.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/android-cloexec-socket.html
+class CloexecSocketCheck : public ClangTidyCheck {
+public:
+  CloexecSocketCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace android
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ANDROID_CLOEXEC_SOCKET_H
diff --git a/clang-tidy/boost/BoostTidyModule.cpp b/clang-tidy/boost/BoostTidyModule.cpp
new file mode 100644 (file)
index 0000000..28eb1d6
--- /dev/null
@@ -0,0 +1,38 @@
+//===------- BoostTidyModule.cpp - clang-tidy -----------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "../ClangTidy.h"
+#include "../ClangTidyModule.h"
+#include "../ClangTidyModuleRegistry.h"
+#include "UseToStringCheck.h"
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace boost {
+
+class BoostModule : public ClangTidyModule {
+public:
+  void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
+    CheckFactories.registerCheck<UseToStringCheck>("boost-use-to-string");
+  }
+};
+
+// Register the BoostModule using this statically initialized variable.
+static ClangTidyModuleRegistry::Add<BoostModule> X("boost-module",
+                                                   "Add boost checks.");
+
+} // namespace boost
+
+// This anchor is used to force the linker to link in the generated object file
+// and thus register the BoostModule.
+volatile int BoostModuleAnchorSource = 0;
+
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/boost/CMakeLists.txt b/clang-tidy/boost/CMakeLists.txt
new file mode 100644 (file)
index 0000000..059f6e9
--- /dev/null
@@ -0,0 +1,14 @@
+set(LLVM_LINK_COMPONENTS support)
+
+add_clang_library(clangTidyBoostModule
+  BoostTidyModule.cpp
+  UseToStringCheck.cpp
+
+  LINK_LIBS
+  clangAST
+  clangASTMatchers
+  clangBasic
+  clangLex
+  clangTidy
+  clangTidyUtils
+  )
diff --git a/clang-tidy/boost/UseToStringCheck.cpp b/clang-tidy/boost/UseToStringCheck.cpp
new file mode 100644 (file)
index 0000000..8ca3656
--- /dev/null
@@ -0,0 +1,73 @@
+//===--- UseToStringCheck.cpp - clang-tidy---------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "UseToStringCheck.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace boost {
+
+AST_MATCHER(Type, isStrictlyInteger) {
+  return Node.isIntegerType() && !Node.isAnyCharacterType() &&
+         !Node.isBooleanType();
+}
+
+void UseToStringCheck::registerMatchers(MatchFinder *Finder) {
+  if (!getLangOpts().CPlusPlus)
+    return;
+
+  Finder->addMatcher(
+      callExpr(
+          hasDeclaration(functionDecl(
+              returns(hasDeclaration(classTemplateSpecializationDecl(
+                  hasName("std::basic_string"),
+                  hasTemplateArgument(0,
+                                      templateArgument().bind("char_type"))))),
+              hasName("boost::lexical_cast"),
+              hasParameter(0, hasType(qualType(has(substTemplateTypeParmType(
+                                  isStrictlyInteger()))))))),
+          argumentCountIs(1), unless(isInTemplateInstantiation()))
+          .bind("to_string"),
+      this);
+}
+
+void UseToStringCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *Call = Result.Nodes.getNodeAs<CallExpr>("to_string");
+  auto CharType =
+      Result.Nodes.getNodeAs<TemplateArgument>("char_type")->getAsType();
+
+  StringRef StringType;
+  if (CharType->isSpecificBuiltinType(BuiltinType::Char_S) ||
+      CharType->isSpecificBuiltinType(BuiltinType::Char_U))
+    StringType = "string";
+  else if (CharType->isSpecificBuiltinType(BuiltinType::WChar_S) ||
+           CharType->isSpecificBuiltinType(BuiltinType::WChar_U))
+    StringType = "wstring";
+  else
+    return;
+
+  auto Loc = Call->getLocStart();
+  auto Diag =
+      diag(Loc, "use std::to_%0 instead of boost::lexical_cast<std::%0>")
+      << StringType;
+
+  if (Loc.isMacroID())
+    return;
+
+  Diag << FixItHint::CreateReplacement(
+      CharSourceRange::getCharRange(Call->getLocStart(),
+                                    Call->getArg(0)->getLocStart()),
+      (llvm::Twine("std::to_") + StringType + "(").str());
+}
+
+} // namespace boost
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/boost/UseToStringCheck.h b/clang-tidy/boost/UseToStringCheck.h
new file mode 100644 (file)
index 0000000..76e7823
--- /dev/null
@@ -0,0 +1,37 @@
+//===--- UseToStringCheck.h - clang-tidy-------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BOOST_USE_TO_STRING_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BOOST_USE_TO_STRING_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace boost {
+
+/// Finds calls to ``boost::lexical_cast<std::string>`` and
+/// ``boost::lexical_cast<std::wstring>`` and replaces them with
+/// ``std::to_string`` and ``std::to_wstring`` calls.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/boost-use-to-string.html
+class UseToStringCheck : public ClangTidyCheck {
+public:
+  UseToStringCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace boost
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BOOST_USE_TO_STRING_H
diff --git a/clang-tidy/bugprone/BugproneTidyModule.cpp b/clang-tidy/bugprone/BugproneTidyModule.cpp
new file mode 100644 (file)
index 0000000..aa5f629
--- /dev/null
@@ -0,0 +1,41 @@
+//===--- BugproneTidyModule.cpp - clang-tidy ------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "../ClangTidy.h"
+#include "../ClangTidyModule.h"
+#include "../ClangTidyModuleRegistry.h"
+#include "SuspiciousMemsetUsageCheck.h"
+#include "UndefinedMemoryManipulationCheck.h"
+
+namespace clang {
+namespace tidy {
+namespace bugprone {
+
+class BugproneModule : public ClangTidyModule {
+public:
+  void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
+    CheckFactories.registerCheck<SuspiciousMemsetUsageCheck>(
+        "bugprone-suspicious-memset-usage");
+    CheckFactories.registerCheck<UndefinedMemoryManipulationCheck>(
+        "bugprone-undefined-memory-manipulation");
+  }
+};
+
+} // namespace bugprone
+
+// Register the BugproneTidyModule using this statically initialized variable.
+static ClangTidyModuleRegistry::Add<bugprone::BugproneModule>
+    X("bugprone-module", "Adds checks for bugprone code constructs.");
+
+// This anchor is used to force the linker to link in the generated object file
+// and thus register the BugproneModule.
+volatile int BugproneModuleAnchorSource = 0;
+
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/bugprone/CMakeLists.txt b/clang-tidy/bugprone/CMakeLists.txt
new file mode 100644 (file)
index 0000000..9ec45c5
--- /dev/null
@@ -0,0 +1,17 @@
+set(LLVM_LINK_COMPONENTS support)
+
+add_clang_library(clangTidyBugproneModule
+  BugproneTidyModule.cpp
+  SuspiciousMemsetUsageCheck.cpp
+  UndefinedMemoryManipulationCheck.cpp
+
+  LINK_LIBS
+  clangAnalysis
+  clangAST
+  clangASTMatchers
+  clangBasic
+  clangLex
+  clangTidy
+  clangTidyUtils
+  clangTooling
+  )
diff --git a/clang-tidy/bugprone/SuspiciousMemsetUsageCheck.cpp b/clang-tidy/bugprone/SuspiciousMemsetUsageCheck.cpp
new file mode 100644 (file)
index 0000000..8e11a43
--- /dev/null
@@ -0,0 +1,127 @@
+//===--- SuspiciousMemsetUsageCheck.cpp - clang-tidy-----------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "SuspiciousMemsetUsageCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Tooling/FixIt.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace bugprone {
+
+void SuspiciousMemsetUsageCheck::registerMatchers(MatchFinder *Finder) {
+  // Note: void *memset(void *buffer, int fill_char, size_t byte_count);
+  // Look for memset(x, '0', z). Probably memset(x, 0, z) was intended.
+  Finder->addMatcher(
+      callExpr(
+          callee(functionDecl(hasName("::memset"))),
+          hasArgument(1, characterLiteral(equals(static_cast<unsigned>('0')))
+                             .bind("char-zero-fill")),
+          unless(
+              eachOf(hasArgument(0, anyOf(hasType(pointsTo(isAnyCharacter())),
+                                          hasType(arrayType(hasElementType(
+                                              isAnyCharacter()))))),
+                     isInTemplateInstantiation()))),
+      this);
+
+  // Look for memset with an integer literal in its fill_char argument.
+  // Will check if it gets truncated.
+  Finder->addMatcher(callExpr(callee(functionDecl(hasName("::memset"))),
+                              hasArgument(1, integerLiteral().bind("num-fill")),
+                              unless(isInTemplateInstantiation())),
+                     this);
+
+  // Look for memset(x, y, 0) as that is most likely an argument swap.
+  Finder->addMatcher(
+      callExpr(callee(functionDecl(hasName("::memset"))),
+               unless(hasArgument(1, anyOf(characterLiteral(equals(
+                                               static_cast<unsigned>('0'))),
+                                           integerLiteral()))),
+               unless(isInTemplateInstantiation()))
+          .bind("call"),
+      this);
+}
+
+void SuspiciousMemsetUsageCheck::check(const MatchFinder::MatchResult &Result) {
+  if (const auto *CharZeroFill =
+          Result.Nodes.getNodeAs<CharacterLiteral>("char-zero-fill")) {
+    // Case 1: fill_char of memset() is a character '0'. Probably an
+    // integer zero was intended.
+
+    SourceRange CharRange = CharZeroFill->getSourceRange();
+    auto Diag =
+        diag(CharZeroFill->getLocStart(), "memset fill value is char '0', "
+                                          "potentially mistaken for int 0");
+
+    // Only suggest a fix if no macros are involved.
+    if (CharRange.getBegin().isMacroID())
+      return;
+    Diag << FixItHint::CreateReplacement(
+        CharSourceRange::getTokenRange(CharRange), "0");
+  }
+
+  else if (const auto *NumFill =
+               Result.Nodes.getNodeAs<IntegerLiteral>("num-fill")) {
+    // Case 2: fill_char of memset() is larger in size than an unsigned char
+    // so it gets truncated during conversion.
+
+    llvm::APSInt NumValue;
+    const auto UCharMax = (1 << Result.Context->getCharWidth()) - 1;
+    if (!NumFill->EvaluateAsInt(NumValue, *Result.Context) ||
+        (NumValue >= 0 && NumValue <= UCharMax))
+      return;
+
+    diag(NumFill->getLocStart(), "memset fill value is out of unsigned "
+                                 "character range, gets truncated");
+  }
+
+  else if (const auto *Call = Result.Nodes.getNodeAs<CallExpr>("call")) {
+    // Case 3: byte_count of memset() is zero. This is most likely an
+    // argument swap.
+
+    const Expr *FillChar = Call->getArg(1);
+    const Expr *ByteCount = Call->getArg(2);
+
+    // Return if `byte_count` is not zero at compile time.
+    llvm::APSInt Value1, Value2;
+    if (ByteCount->isValueDependent() ||
+        !ByteCount->EvaluateAsInt(Value2, *Result.Context) || Value2 != 0)
+      return;
+
+    // Return if `fill_char` is known to be zero or negative at compile
+    // time. In these cases, swapping the args would be a nop, or
+    // introduce a definite bug. The code is likely correct.
+    if (!FillChar->isValueDependent() &&
+        FillChar->EvaluateAsInt(Value1, *Result.Context) &&
+        (Value1 == 0 || Value1.isNegative()))
+      return;
+
+    // `byte_count` is known to be zero at compile time, and `fill_char` is
+    // either not known or known to be a positive integer. Emit a warning
+    // and fix-its to swap the arguments.
+    auto D = diag(Call->getLocStart(),
+                  "memset of size zero, potentially swapped arguments");
+    StringRef RHSString = tooling::fixit::getText(*ByteCount, *Result.Context);
+    StringRef LHSString = tooling::fixit::getText(*FillChar, *Result.Context);
+    if (LHSString.empty() || RHSString.empty())
+      return;
+
+    D << tooling::fixit::createReplacement(*FillChar, RHSString)
+      << tooling::fixit::createReplacement(*ByteCount, LHSString);
+  }
+}
+
+} // namespace bugprone
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/bugprone/SuspiciousMemsetUsageCheck.h b/clang-tidy/bugprone/SuspiciousMemsetUsageCheck.h
new file mode 100644 (file)
index 0000000..1c0d1bc
--- /dev/null
@@ -0,0 +1,35 @@
+//===--- SuspiciousMemsetUsageCheck.h - clang-tidy---------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_SUSPICIOUS_MEMSET_USAGE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_SUSPICIOUS_MEMSET_USAGE_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace bugprone {
+
+/// Finds memset calls with potential mistakes in their arguments.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone-suspicious-memset-usage.html
+class SuspiciousMemsetUsageCheck : public ClangTidyCheck {
+public:
+  SuspiciousMemsetUsageCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace bugprone
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_SUSPICIOUS_MEMSET_USAGE_H
diff --git a/clang-tidy/bugprone/UndefinedMemoryManipulationCheck.cpp b/clang-tidy/bugprone/UndefinedMemoryManipulationCheck.cpp
new file mode 100644 (file)
index 0000000..cbc1f34
--- /dev/null
@@ -0,0 +1,61 @@
+//===--- UndefinedMemoryManipulationCheck.cpp - clang-tidy-----------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "UndefinedMemoryManipulationCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace bugprone {
+
+namespace {
+AST_MATCHER(CXXRecordDecl, isNotTriviallyCopyable) {
+  return !Node.isTriviallyCopyable();
+}
+} // namespace
+
+void UndefinedMemoryManipulationCheck::registerMatchers(MatchFinder *Finder) {
+  const auto NotTriviallyCopyableObject =
+      hasType(pointsTo(cxxRecordDecl(isNotTriviallyCopyable())));
+
+  // Check whether destination object is not TriviallyCopyable.
+  // Applicable to all three memory manipulation functions.
+  Finder->addMatcher(callExpr(callee(functionDecl(hasAnyName(
+                                  "::memset", "::memcpy", "::memmove"))),
+                              hasArgument(0, NotTriviallyCopyableObject))
+                         .bind("dest"),
+                     this);
+
+  // Check whether source object is not TriviallyCopyable.
+  // Only applicable to memcpy() and memmove().
+  Finder->addMatcher(
+      callExpr(callee(functionDecl(hasAnyName("::memcpy", "::memmove"))),
+               hasArgument(1, NotTriviallyCopyableObject))
+          .bind("src"),
+      this);
+}
+
+void UndefinedMemoryManipulationCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  if (const auto *Destination = Result.Nodes.getNodeAs<CallExpr>("dest")) {
+    diag(Destination->getLocStart(), "undefined behavior, destination "
+                                     "object is not TriviallyCopyable");
+  }
+  if (const auto *Source = Result.Nodes.getNodeAs<CallExpr>("src")) {
+    diag(Source->getLocStart(), "undefined behavior, source object is not "
+                                "TriviallyCopyable");
+  }
+}
+
+} // namespace bugprone
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/bugprone/UndefinedMemoryManipulationCheck.h b/clang-tidy/bugprone/UndefinedMemoryManipulationCheck.h
new file mode 100644 (file)
index 0000000..64d6c56
--- /dev/null
@@ -0,0 +1,37 @@
+//===--- UndefinedMemoryManipulationCheck.h - clang-tidy---------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_UNDEFINED_MEMORY_MANIPULATION_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_UNDEFINED_MEMORY_MANIPULATION_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace bugprone {
+
+/// Finds calls of memory manipulation functions ``memset()``, ``memcpy()`` and
+/// ``memmove()`` on not TriviallyCopyable objects resulting in undefined
+/// behavior.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone-undefined-memory-manipulation.html
+class UndefinedMemoryManipulationCheck : public ClangTidyCheck {
+public:
+  UndefinedMemoryManipulationCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace bugprone
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_UNDEFINED_MEMORY_MANIPULATION_H
diff --git a/clang-tidy/cert/CERTTidyModule.cpp b/clang-tidy/cert/CERTTidyModule.cpp
new file mode 100644 (file)
index 0000000..32f6d6c
--- /dev/null
@@ -0,0 +1,90 @@
+//===--- CERTTidyModule.cpp - clang-tidy ----------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "../ClangTidy.h"
+#include "../ClangTidyModule.h"
+#include "../ClangTidyModuleRegistry.h"
+#include "../google/UnnamedNamespaceInHeaderCheck.h"
+#include "../misc/MoveConstructorInitCheck.h"
+#include "../misc/NewDeleteOverloadsCheck.h"
+#include "../misc/NonCopyableObjects.h"
+#include "../misc/StaticAssertCheck.h"
+#include "../misc/ThrowByValueCatchByReferenceCheck.h"
+#include "CommandProcessorCheck.h"
+#include "DontModifyStdNamespaceCheck.h"
+#include "FloatLoopCounter.h"
+#include "LimitedRandomnessCheck.h"
+#include "PostfixOperatorCheck.h"
+#include "SetLongJmpCheck.h"
+#include "StaticObjectExceptionCheck.h"
+#include "StrToNumCheck.h"
+#include "ThrownExceptionTypeCheck.h"
+#include "VariadicFunctionDefCheck.h"
+
+namespace clang {
+namespace tidy {
+namespace cert {
+
+class CERTModule : public ClangTidyModule {
+public:
+  void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
+    // C++ checkers
+    // DCL
+    CheckFactories.registerCheck<PostfixOperatorCheck>(
+        "cert-dcl21-cpp");
+    CheckFactories.registerCheck<VariadicFunctionDefCheck>("cert-dcl50-cpp");
+    CheckFactories.registerCheck<misc::NewDeleteOverloadsCheck>(
+        "cert-dcl54-cpp");
+    CheckFactories.registerCheck<DontModifyStdNamespaceCheck>(
+        "cert-dcl58-cpp");
+    CheckFactories.registerCheck<google::build::UnnamedNamespaceInHeaderCheck>(
+        "cert-dcl59-cpp");
+    // OOP
+    CheckFactories.registerCheck<misc::MoveConstructorInitCheck>(
+        "cert-oop11-cpp");
+    // ERR
+    CheckFactories.registerCheck<misc::ThrowByValueCatchByReferenceCheck>(
+        "cert-err09-cpp");
+    CheckFactories.registerCheck<SetLongJmpCheck>("cert-err52-cpp");
+    CheckFactories.registerCheck<StaticObjectExceptionCheck>("cert-err58-cpp");
+    CheckFactories.registerCheck<ThrownExceptionTypeCheck>("cert-err60-cpp");
+    CheckFactories.registerCheck<misc::ThrowByValueCatchByReferenceCheck>(
+        "cert-err61-cpp");
+    // MSC
+    CheckFactories.registerCheck<LimitedRandomnessCheck>("cert-msc50-cpp");
+
+    // C checkers
+    // DCL
+    CheckFactories.registerCheck<misc::StaticAssertCheck>("cert-dcl03-c");
+    // ENV
+    CheckFactories.registerCheck<CommandProcessorCheck>("cert-env33-c");
+    // FLP
+    CheckFactories.registerCheck<FloatLoopCounter>("cert-flp30-c");
+    // FIO
+    CheckFactories.registerCheck<misc::NonCopyableObjectsCheck>("cert-fio38-c");
+    // ERR
+    CheckFactories.registerCheck<StrToNumCheck>("cert-err34-c");
+    // MSC
+    CheckFactories.registerCheck<LimitedRandomnessCheck>("cert-msc30-c");
+  }
+};
+
+} // namespace cert
+
+// Register the MiscTidyModule using this statically initialized variable.
+static ClangTidyModuleRegistry::Add<cert::CERTModule>
+    X("cert-module",
+      "Adds lint checks corresponding to CERT secure coding guidelines.");
+
+// This anchor is used to force the linker to link in the generated object file
+// and thus register the CERTModule.
+volatile int CERTModuleAnchorSource = 0;
+
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/cert/CMakeLists.txt b/clang-tidy/cert/CMakeLists.txt
new file mode 100644 (file)
index 0000000..83e2195
--- /dev/null
@@ -0,0 +1,26 @@
+set(LLVM_LINK_COMPONENTS support)
+
+add_clang_library(clangTidyCERTModule
+  CERTTidyModule.cpp
+  CommandProcessorCheck.cpp
+  DontModifyStdNamespaceCheck.cpp
+  FloatLoopCounter.cpp
+  LimitedRandomnessCheck.cpp
+  PostfixOperatorCheck.cpp
+  SetLongJmpCheck.cpp
+  StaticObjectExceptionCheck.cpp
+  StrToNumCheck.cpp
+  ThrownExceptionTypeCheck.cpp
+  VariadicFunctionDefCheck.cpp
+
+  LINK_LIBS
+  clangAnalysis
+  clangAST
+  clangASTMatchers
+  clangBasic
+  clangLex
+  clangTidy
+  clangTidyGoogleModule
+  clangTidyMiscModule
+  clangTidyUtils
+  )
diff --git a/clang-tidy/cert/CommandProcessorCheck.cpp b/clang-tidy/cert/CommandProcessorCheck.cpp
new file mode 100644 (file)
index 0000000..e2dbeca
--- /dev/null
@@ -0,0 +1,45 @@
+//===--- Env33CCheck.cpp - clang-tidy--------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CommandProcessorCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace cert {
+
+void CommandProcessorCheck::registerMatchers(MatchFinder *Finder) {
+  Finder->addMatcher(
+      callExpr(
+          callee(functionDecl(anyOf(hasName("::system"), hasName("::popen"),
+                                    hasName("::_popen")))
+                     .bind("func")),
+          // Do not diagnose when the call expression passes a null pointer
+          // constant to system(); that only checks for the presence of a
+          // command processor, which is not a security risk by itself.
+          unless(callExpr(callee(functionDecl(hasName("::system"))),
+                          argumentCountIs(1),
+                          hasArgument(0, nullPointerConstant()))))
+          .bind("expr"),
+      this);
+}
+
+void CommandProcessorCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *Fn = Result.Nodes.getNodeAs<FunctionDecl>("func");
+  const auto *E = Result.Nodes.getNodeAs<CallExpr>("expr");
+
+  diag(E->getExprLoc(), "calling %0 uses a command processor") << Fn;
+}
+
+} // namespace cert
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/cert/CommandProcessorCheck.h b/clang-tidy/cert/CommandProcessorCheck.h
new file mode 100644 (file)
index 0000000..a85a7ed
--- /dev/null
@@ -0,0 +1,38 @@
+//===--- CommandInterpreterCheck.h - clang-tidy------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_COMMAND_PROCESSOR_CHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_COMMAND_PROCESSOR_CHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace cert {
+
+/// Execution of a command processor can lead to security vulnerabilities,
+/// and is generally not required. Instead, prefer to launch executables
+/// directly via mechanisms that give you more control over what executable is
+/// actually launched.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/cert-env33-c.html
+class CommandProcessorCheck : public ClangTidyCheck {
+public:
+  CommandProcessorCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace cert
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_COMMAND_PROCESSOR_CHECK_H
diff --git a/clang-tidy/cert/DontModifyStdNamespaceCheck.cpp b/clang-tidy/cert/DontModifyStdNamespaceCheck.cpp
new file mode 100644 (file)
index 0000000..e5759a5
--- /dev/null
@@ -0,0 +1,49 @@
+//===--- DontModifyStdNamespaceCheck.cpp - clang-tidy----------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "DontModifyStdNamespaceCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace cert {
+
+void DontModifyStdNamespaceCheck::registerMatchers(MatchFinder *Finder) {
+  if (!getLangOpts().CPlusPlus)
+    return;
+
+  Finder->addMatcher(
+      namespaceDecl(unless(isExpansionInSystemHeader()),
+                    anyOf(hasName("std"), hasName("posix")),
+                    has(decl(unless(anyOf(
+                        functionDecl(isExplicitTemplateSpecialization()),
+                        cxxRecordDecl(isExplicitTemplateSpecialization()))))))
+          .bind("nmspc"),
+      this);
+}
+
+void DontModifyStdNamespaceCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  const auto *N = Result.Nodes.getNodeAs<NamespaceDecl>("nmspc");
+
+  // Only consider top level namespaces.
+  if (N->getParent() != Result.Context->getTranslationUnitDecl())
+    return;
+
+  diag(N->getLocation(),
+       "modification of %0 namespace can result in undefined behavior")
+      << N;
+}
+
+} // namespace cert
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/cert/DontModifyStdNamespaceCheck.h b/clang-tidy/cert/DontModifyStdNamespaceCheck.h
new file mode 100644 (file)
index 0000000..0cc23f7
--- /dev/null
@@ -0,0 +1,36 @@
+//===--- DontModifyStdNamespaceCheck.h - clang-tidy--------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_DONT_MODIFY_STD_NAMESPACE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_DONT_MODIFY_STD_NAMESPACE_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace cert {
+
+/// Modification of the std or posix namespace can result in undefined behavior.
+/// This check warns for such modifications.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/cert-msc53-cpp.html
+class DontModifyStdNamespaceCheck : public ClangTidyCheck {
+public:
+  DontModifyStdNamespaceCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace cert
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_DONT_MODIFY_STD_NAMESPACE_H
diff --git a/clang-tidy/cert/FloatLoopCounter.cpp b/clang-tidy/cert/FloatLoopCounter.cpp
new file mode 100644 (file)
index 0000000..e92552e
--- /dev/null
@@ -0,0 +1,35 @@
+//===--- FloatLoopCounter.cpp - clang-tidy---------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "FloatLoopCounter.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace cert {
+
+void FloatLoopCounter::registerMatchers(MatchFinder *Finder) {
+  Finder->addMatcher(
+      forStmt(hasIncrement(expr(hasType(realFloatingPointType())))).bind("for"),
+      this);
+}
+
+void FloatLoopCounter::check(const MatchFinder::MatchResult &Result) {
+  const auto *FS = Result.Nodes.getNodeAs<ForStmt>("for");
+
+  diag(FS->getInc()->getExprLoc(), "loop induction expression should not have "
+                                   "floating-point type");
+}
+
+} // namespace cert
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/cert/FloatLoopCounter.h b/clang-tidy/cert/FloatLoopCounter.h
new file mode 100644 (file)
index 0000000..c66e44a
--- /dev/null
@@ -0,0 +1,37 @@
+//===--- FloatLoopCounter.h - clang-tidy-------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_FLOAT_LOOP_COUNTER_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_FLOAT_LOOP_COUNTER_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace cert {
+
+/// This check diagnoses when the loop induction expression of a for loop has
+/// floating-point type. The check corresponds to:
+/// https://www.securecoding.cert.org/confluence/display/c/FLP30-C.+Do+not+use+floating-point+variables+as+loop+counters
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/cert-flp30-c.html
+class FloatLoopCounter : public ClangTidyCheck {
+public:
+  FloatLoopCounter(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace cert
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_FLOAT_LOOP_COUNTER_H
diff --git a/clang-tidy/cert/LICENSE.TXT b/clang-tidy/cert/LICENSE.TXT
new file mode 100644 (file)
index 0000000..d8395cc
--- /dev/null
@@ -0,0 +1,22 @@
+------------------------------------------------------------------------------
+clang-tidy CERT Files
+------------------------------------------------------------------------------
+All clang-tidy files are licensed under the LLVM license with the following
+additions:
+
+Any file referencing a CERT Secure Coding guideline:
+Please allow this letter to serve as confirmation that open source projects on
+http://llvm.org are permitted to link via hypertext to the CERT(R) secure coding
+guidelines available at https://www.securecoding.cert.org.
+
+The foregoing is permitted by the Terms of Use as follows:
+"Linking to the Service
+Because we update many of our Web documents regularly, we would prefer that you
+link to our Web pages whenever possible rather than reproduce them. It is not
+necessary to request permission to make referential hypertext links to The
+Service."
+http://www.sei.cmu.edu/legal/ip/index.cfm.
+
+Please allow this letter to also confirm that no formal permission is required
+to reproduce the title of the content being linked to, nor to reproduce any
+de Minimis description of such content.
diff --git a/clang-tidy/cert/LimitedRandomnessCheck.cpp b/clang-tidy/cert/LimitedRandomnessCheck.cpp
new file mode 100644 (file)
index 0000000..f319f9f
--- /dev/null
@@ -0,0 +1,38 @@
+//===--- LimitedRandomnessCheck.cpp - clang-tidy---------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "LimitedRandomnessCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace cert {
+
+void LimitedRandomnessCheck::registerMatchers(MatchFinder *Finder) {
+  Finder->addMatcher(callExpr(callee(functionDecl(namedDecl(hasName("::rand")),
+                                                  parameterCountIs(0))))
+                         .bind("randomGenerator"),
+                     this);
+}
+
+void LimitedRandomnessCheck::check(const MatchFinder::MatchResult &Result) {
+  std::string msg = "";
+  if (getLangOpts().CPlusPlus)
+    msg = "; use C++11 random library instead";
+
+  const auto *MatchedDecl = Result.Nodes.getNodeAs<CallExpr>("randomGenerator");
+  diag(MatchedDecl->getLocStart(), "rand() has limited randomness" + msg);
+}
+
+} // namespace cert
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/cert/LimitedRandomnessCheck.h b/clang-tidy/cert/LimitedRandomnessCheck.h
new file mode 100644 (file)
index 0000000..59d511c
--- /dev/null
@@ -0,0 +1,38 @@
+//===--- LimitedRandomnessCheck.h - clang-tidy-------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_LIMITED_RANDOMNESS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_LIMITED_RANDOMNESS_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace cert {
+
+/// Pseudorandom number generators are not genuinely random. The result of the
+/// std::rand() function makes no guarantees as to the quality of the random
+/// sequence produced.
+/// This check warns for the usage of std::rand() function.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/cert-msc50-cpp.html
+class LimitedRandomnessCheck : public ClangTidyCheck {
+public:
+  LimitedRandomnessCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace cert
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_LIMITED_RANDOMNESS_H
diff --git a/clang-tidy/cert/PostfixOperatorCheck.cpp b/clang-tidy/cert/PostfixOperatorCheck.cpp
new file mode 100644 (file)
index 0000000..2b8dbbe
--- /dev/null
@@ -0,0 +1,88 @@
+//===--- PostfixOperatorCheck.cpp - clang-tidy-----------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "PostfixOperatorCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace cert {
+
+void PostfixOperatorCheck::registerMatchers(MatchFinder *Finder) {
+  if (!getLangOpts().CPlusPlus)
+    return;
+
+  Finder->addMatcher(functionDecl(anyOf(hasOverloadedOperatorName("++"),
+                                        hasOverloadedOperatorName("--")))
+                         .bind("decl"),
+                     this);
+}
+
+void PostfixOperatorCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *FuncDecl = Result.Nodes.getNodeAs<FunctionDecl>("decl");
+
+  bool HasThis = false;
+  if (const auto *MethodDecl = dyn_cast<CXXMethodDecl>(FuncDecl))
+    HasThis = MethodDecl->isInstance();
+
+  // Check if the operator is a postfix one.
+  if (FuncDecl->getNumParams() != (HasThis ? 1 : 2))
+    return;
+
+  SourceRange ReturnRange = FuncDecl->getReturnTypeSourceRange();
+  SourceLocation Location = ReturnRange.getBegin();
+  if (!Location.isValid())
+    return;
+
+  QualType ReturnType = FuncDecl->getReturnType();
+
+  // Warn when the operators return a reference.
+  if (const auto *RefType = ReturnType->getAs<ReferenceType>()) {
+    auto Diag = diag(Location, "overloaded %0 returns a reference instead of a "
+                               "constant object type")
+                << FuncDecl;
+
+    if (Location.isMacroID() || ReturnType->getAs<TypedefType>() ||
+        RefType->getPointeeTypeAsWritten()->getAs<TypedefType>())
+      return;
+
+    QualType ReplaceType =
+        ReturnType.getNonReferenceType().getLocalUnqualifiedType();
+    // The getReturnTypeSourceRange omits the qualifiers. We do not want to
+    // duplicate the const.
+    if (!ReturnType->getPointeeType().isConstQualified())
+      ReplaceType.addConst();
+
+    Diag << FixItHint::CreateReplacement(
+        ReturnRange,
+        ReplaceType.getAsString(Result.Context->getPrintingPolicy()) + " ");
+
+    return;
+  }
+
+  if (ReturnType.isConstQualified() || ReturnType->isBuiltinType() ||
+      ReturnType->isPointerType())
+    return;
+
+  auto Diag =
+      diag(Location, "overloaded %0 returns a non-constant object instead of a "
+                     "constant object type")
+      << FuncDecl;
+
+  if (!Location.isMacroID() && !ReturnType->getAs<TypedefType>())
+    Diag << FixItHint::CreateInsertion(Location, "const ");
+}
+
+} // namespace cert
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/cert/PostfixOperatorCheck.h b/clang-tidy/cert/PostfixOperatorCheck.h
new file mode 100644 (file)
index 0000000..29c2306
--- /dev/null
@@ -0,0 +1,36 @@
+//===--- PostfixOperatorCheck.h - clang-tidy---------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_POSTFIX_OPERATOR_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_POSTFIX_OPERATOR_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace cert {
+
+/// Checks if the overloaded postfix ++ and -- operator return a constant
+/// object.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/cert-postfix-operator.html
+class PostfixOperatorCheck : public ClangTidyCheck {
+public:
+  PostfixOperatorCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace cert
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_POSTFIX_OPERATOR_H
diff --git a/clang-tidy/cert/SetLongJmpCheck.cpp b/clang-tidy/cert/SetLongJmpCheck.cpp
new file mode 100644 (file)
index 0000000..89ba5e7
--- /dev/null
@@ -0,0 +1,79 @@
+//===--- SetLongJmpCheck.cpp - clang-tidy----------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "SetLongJmpCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Lex/PPCallbacks.h"
+#include "clang/Lex/Preprocessor.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace cert {
+
+const char SetLongJmpCheck::DiagWording[] =
+    "do not call %0; consider using exception handling instead";
+
+namespace {
+class SetJmpMacroCallbacks : public PPCallbacks {
+  SetLongJmpCheck &Check;
+
+public:
+  explicit SetJmpMacroCallbacks(SetLongJmpCheck &Check) : Check(Check) {}
+
+  void MacroExpands(const Token &MacroNameTok, const MacroDefinition &MD,
+                    SourceRange Range, const MacroArgs *Args) override {
+    const auto *II = MacroNameTok.getIdentifierInfo();
+    if (!II)
+      return;
+
+    if (II->getName() == "setjmp")
+      Check.diag(Range.getBegin(), Check.DiagWording) << II;
+  }
+};
+} // namespace
+
+void SetLongJmpCheck::registerPPCallbacks(CompilerInstance &Compiler) {
+  // This checker only applies to C++, where exception handling is a superior
+  // solution to setjmp/longjmp calls.
+  if (!getLangOpts().CPlusPlus)
+    return;
+
+  // Per [headers]p5, setjmp must be exposed as a macro instead of a function,
+  // despite the allowance in C for setjmp to also be an extern function.
+  Compiler.getPreprocessor().addPPCallbacks(
+      llvm::make_unique<SetJmpMacroCallbacks>(*this));
+}
+
+void SetLongJmpCheck::registerMatchers(MatchFinder *Finder) {
+  // This checker only applies to C++, where exception handling is a superior
+  // solution to setjmp/longjmp calls.
+  if (!getLangOpts().CPlusPlus)
+    return;
+
+  // In case there is an implementation that happens to define setjmp as a
+  // function instead of a macro, this will also catch use of it. However, we
+  // are primarily searching for uses of longjmp.
+  Finder->addMatcher(callExpr(callee(functionDecl(anyOf(hasName("setjmp"),
+                                                        hasName("longjmp")))))
+                         .bind("expr"),
+                     this);
+}
+
+void SetLongJmpCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *E = Result.Nodes.getNodeAs<CallExpr>("expr");
+  diag(E->getExprLoc(), DiagWording) << cast<NamedDecl>(E->getCalleeDecl());
+}
+
+} // namespace cert
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/cert/SetLongJmpCheck.h b/clang-tidy/cert/SetLongJmpCheck.h
new file mode 100644 (file)
index 0000000..1d6c098
--- /dev/null
@@ -0,0 +1,38 @@
+//===--- SetLongJmpCheck.h - clang-tidy--------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_SETLONGJMPCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_SETLONGJMPCHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace cert {
+
+/// Guards against use of setjmp/longjmp in C++ code
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/cert-err52-cpp.html
+class SetLongJmpCheck : public ClangTidyCheck {
+public:
+  SetLongJmpCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+  void registerPPCallbacks(CompilerInstance &Compiler) override;
+
+  static const char DiagWording[];
+};
+
+} // namespace cert
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_SETLONGJMPCHECK_H
diff --git a/clang-tidy/cert/StaticObjectExceptionCheck.cpp b/clang-tidy/cert/StaticObjectExceptionCheck.cpp
new file mode 100644 (file)
index 0000000..45f9433
--- /dev/null
@@ -0,0 +1,60 @@
+//===--- StaticObjectExceptionCheck.cpp - clang-tidy-----------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "StaticObjectExceptionCheck.h"
+#include "../utils/Matchers.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace cert {
+
+void StaticObjectExceptionCheck::registerMatchers(MatchFinder *Finder) {
+  if ((!getLangOpts().CPlusPlus) || (!getLangOpts().CXXExceptions))
+    return;
+
+  // Match any static or thread_local variable declaration that has an
+  // initializer that can throw.
+  Finder->addMatcher(
+      varDecl(anyOf(hasThreadStorageDuration(), hasStaticStorageDuration()),
+              unless(hasAncestor(functionDecl())),
+              anyOf(hasDescendant(cxxConstructExpr(hasDeclaration(
+                        cxxConstructorDecl(unless(isNoThrow())).bind("func")))),
+                    hasDescendant(cxxNewExpr(hasDeclaration(
+                        functionDecl(unless(isNoThrow())).bind("func")))),
+                    hasDescendant(callExpr(hasDeclaration(
+                        functionDecl(unless(isNoThrow())).bind("func"))))))
+          .bind("var"),
+      this);
+}
+
+void StaticObjectExceptionCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *VD = Result.Nodes.getNodeAs<VarDecl>("var");
+  const auto *Func = Result.Nodes.getNodeAs<FunctionDecl>("func");
+
+  diag(VD->getLocation(),
+       "initialization of %0 with %select{static|thread_local}1 storage "
+       "duration may throw an exception that cannot be caught")
+      << VD << (VD->getStorageDuration() == SD_Static ? 0 : 1);
+
+  SourceLocation FuncLocation = Func->getLocation();
+  if (FuncLocation.isValid()) {
+    diag(FuncLocation,
+         "possibly throwing %select{constructor|function}0 declared here",
+         DiagnosticIDs::Note)
+        << (isa<CXXConstructorDecl>(Func) ? 0 : 1);
+  }
+}
+
+} // namespace cert
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/cert/StaticObjectExceptionCheck.h b/clang-tidy/cert/StaticObjectExceptionCheck.h
new file mode 100644 (file)
index 0000000..463f433
--- /dev/null
@@ -0,0 +1,36 @@
+//===--- StaticObjectExceptionCheck.h - clang-tidy---------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_ERR58_CPP_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_ERR58_CPP_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace cert {
+
+/// Checks whether the constructor for a static or thread_local object will
+/// throw.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/cert-err58-cpp.html
+class StaticObjectExceptionCheck : public ClangTidyCheck {
+public:
+  StaticObjectExceptionCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace cert
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_ERR58_CPP_H
diff --git a/clang-tidy/cert/StrToNumCheck.cpp b/clang-tidy/cert/StrToNumCheck.cpp
new file mode 100644 (file)
index 0000000..bd84cf2
--- /dev/null
@@ -0,0 +1,235 @@
+//===--- Err34CCheck.cpp - clang-tidy--------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "StrToNumCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Analysis/Analyses/FormatString.h"
+#include "llvm/ADT/StringSwitch.h"
+#include <cassert>
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace cert {
+
+void StrToNumCheck::registerMatchers(MatchFinder *Finder) {
+  // Match any function call to the C standard library string conversion
+  // functions that do no error checking.
+  Finder->addMatcher(
+      callExpr(
+          callee(functionDecl(anyOf(
+              functionDecl(hasAnyName("::atoi", "::atof", "::atol", "::atoll"))
+                  .bind("converter"),
+              functionDecl(hasAnyName("::scanf", "::sscanf", "::fscanf",
+                                      "::vfscanf", "::vscanf", "::vsscanf"))
+                  .bind("formatted")))))
+          .bind("expr"),
+      this);
+}
+
+namespace {
+enum class ConversionKind {
+  None,
+  ToInt,
+  ToUInt,
+  ToLongInt,
+  ToLongUInt,
+  ToIntMax,
+  ToUIntMax,
+  ToFloat,
+  ToDouble,
+  ToLongDouble
+};
+
+ConversionKind ClassifyConversionFunc(const FunctionDecl *FD) {
+  return llvm::StringSwitch<ConversionKind>(FD->getName())
+      .Cases("atoi", "atol", ConversionKind::ToInt)
+      .Case("atoll", ConversionKind::ToLongInt)
+      .Case("atof", ConversionKind::ToDouble)
+      .Default(ConversionKind::None);
+}
+
+ConversionKind ClassifyFormatString(StringRef Fmt, const LangOptions &LO,
+                                    const TargetInfo &TI) {
+  // Scan the format string for the first problematic format specifier, then
+  // report that as the conversion type. This will miss additional conversion
+  // specifiers, but that is acceptable behavior.
+
+  class Handler : public analyze_format_string::FormatStringHandler {
+    ConversionKind CK;
+
+    bool HandleScanfSpecifier(const analyze_scanf::ScanfSpecifier &FS,
+                              const char *startSpecifier,
+                              unsigned specifierLen) override {
+      // If we just consume the argument without assignment, we don't care
+      // about it having conversion errors.
+      if (!FS.consumesDataArgument())
+        return true;
+
+      // Get the conversion specifier and use it to determine the conversion
+      // kind.
+      analyze_scanf::ScanfConversionSpecifier SCS = FS.getConversionSpecifier();
+      if (SCS.isIntArg()) {
+        switch (FS.getLengthModifier().getKind()) {
+        case analyze_scanf::LengthModifier::AsLongLong:
+          CK = ConversionKind::ToLongInt;
+          break;
+        case analyze_scanf::LengthModifier::AsIntMax:
+          CK = ConversionKind::ToIntMax;
+          break;
+        default:
+          CK = ConversionKind::ToInt;
+          break;
+        }
+      } else if (SCS.isUIntArg()) {
+        switch (FS.getLengthModifier().getKind()) {
+        case analyze_scanf::LengthModifier::AsLongLong:
+          CK = ConversionKind::ToLongUInt;
+          break;
+        case analyze_scanf::LengthModifier::AsIntMax:
+          CK = ConversionKind::ToUIntMax;
+          break;
+        default:
+          CK = ConversionKind::ToUInt;
+          break;
+        }
+      } else if (SCS.isDoubleArg()) {
+        switch (FS.getLengthModifier().getKind()) {
+        case analyze_scanf::LengthModifier::AsLongDouble:
+          CK = ConversionKind::ToLongDouble;
+          break;
+        case analyze_scanf::LengthModifier::AsLong:
+          CK = ConversionKind::ToDouble;
+          break;
+        default:
+          CK = ConversionKind::ToFloat;
+          break;
+        }
+      }
+
+      // Continue if we have yet to find a conversion kind that we care about.
+      return CK == ConversionKind::None;
+    }
+
+  public:
+    Handler() : CK(ConversionKind::None) {}
+
+    ConversionKind get() const { return CK; }
+  };
+
+  Handler H;
+  analyze_format_string::ParseScanfString(H, Fmt.begin(), Fmt.end(), LO, TI);
+
+  return H.get();
+}
+
+StringRef ClassifyConversionType(ConversionKind K) {
+  switch (K) {
+  case ConversionKind::None:
+    assert(false && "Unexpected conversion kind");
+  case ConversionKind::ToInt:
+  case ConversionKind::ToLongInt:
+  case ConversionKind::ToIntMax:
+    return "an integer value";
+  case ConversionKind::ToUInt:
+  case ConversionKind::ToLongUInt:
+  case ConversionKind::ToUIntMax:
+    return "an unsigned integer value";
+  case ConversionKind::ToFloat:
+  case ConversionKind::ToDouble:
+  case ConversionKind::ToLongDouble:
+    return "a floating-point value";
+  }
+  llvm_unreachable("Unknown conversion kind");
+}
+
+StringRef ClassifyReplacement(ConversionKind K) {
+  switch (K) {
+  case ConversionKind::None:
+    assert(false && "Unexpected conversion kind");
+  case ConversionKind::ToInt:
+    return "strtol";
+  case ConversionKind::ToUInt:
+    return "strtoul";
+  case ConversionKind::ToIntMax:
+    return "strtoimax";
+  case ConversionKind::ToLongInt:
+    return "strtoll";
+  case ConversionKind::ToLongUInt:
+    return "strtoull";
+  case ConversionKind::ToUIntMax:
+    return "strtoumax";
+  case ConversionKind::ToFloat:
+    return "strtof";
+  case ConversionKind::ToDouble:
+    return "strtod";
+  case ConversionKind::ToLongDouble:
+    return "strtold";
+  }
+  llvm_unreachable("Unknown conversion kind");
+}
+} // unnamed namespace
+
+void StrToNumCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *Call = Result.Nodes.getNodeAs<CallExpr>("expr");
+  const FunctionDecl *FuncDecl = nullptr;
+  ConversionKind Conversion;
+
+  if (const auto *ConverterFunc =
+          Result.Nodes.getNodeAs<FunctionDecl>("converter")) {
+    // Converter functions are always incorrect to use.
+    FuncDecl = ConverterFunc;
+    Conversion = ClassifyConversionFunc(ConverterFunc);
+  } else if (const auto *FFD =
+                 Result.Nodes.getNodeAs<FunctionDecl>("formatted")) {
+    StringRef FmtStr;
+    // The format string comes from the call expression and depends on which
+    // flavor of scanf is called.
+    // Index 0: scanf, vscanf, Index 1: fscanf, sscanf, vfscanf, vsscanf.
+    unsigned Idx =
+        (FFD->getName() == "scanf" || FFD->getName() == "vscanf") ? 0 : 1;
+
+    // Given the index, see if the call expression argument at that index is
+    // a string literal.
+    if (Call->getNumArgs() < Idx)
+      return;
+
+    if (const Expr *Arg = Call->getArg(Idx)->IgnoreParenImpCasts()) {
+      if (const auto *SL = dyn_cast<StringLiteral>(Arg)) {
+        FmtStr = SL->getString();
+      }
+    }
+
+    // If we could not get the format string, bail out.
+    if (FmtStr.empty())
+      return;
+
+    // Formatted input functions need further checking of the format string to
+    // determine whether a problematic conversion may be happening.
+    Conversion = ClassifyFormatString(FmtStr, getLangOpts(),
+                                      Result.Context->getTargetInfo());
+    if (Conversion != ConversionKind::None)
+      FuncDecl = FFD;
+  }
+
+  if (!FuncDecl)
+    return;
+
+  diag(Call->getExprLoc(),
+       "%0 used to convert a string to %1, but function will not report "
+       "conversion errors; consider using '%2' instead")
+      << FuncDecl << ClassifyConversionType(Conversion)
+      << ClassifyReplacement(Conversion);
+}
+
+} // namespace cert
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/cert/StrToNumCheck.h b/clang-tidy/cert/StrToNumCheck.h
new file mode 100644 (file)
index 0000000..55f13be
--- /dev/null
@@ -0,0 +1,36 @@
+//===--- StrToNumCheck.h - clang-tidy----------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_STRTONUMCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_STRTONUMCHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace cert {
+
+/// Guards against use of string conversion functions that do not have
+/// reasonable error handling for conversion errors.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/cert-err34-c.html
+class StrToNumCheck : public ClangTidyCheck {
+public:
+  StrToNumCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace cert
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_STRTONUMCHECK_H
diff --git a/clang-tidy/cert/ThrownExceptionTypeCheck.cpp b/clang-tidy/cert/ThrownExceptionTypeCheck.cpp
new file mode 100644 (file)
index 0000000..37fb355
--- /dev/null
@@ -0,0 +1,41 @@
+//===--- ThrownExceptionTypeCheck.cpp - clang-tidy-------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ThrownExceptionTypeCheck.h"
+#include "../utils/Matchers.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace cert {
+
+void ThrownExceptionTypeCheck::registerMatchers(MatchFinder *Finder) {
+  if (!getLangOpts().CPlusPlus)
+    return;
+
+  Finder->addMatcher(
+      cxxThrowExpr(has(ignoringParenImpCasts(
+          cxxConstructExpr(hasDeclaration(cxxConstructorDecl(
+                               isCopyConstructor(), unless(isNoThrow()))))
+              .bind("expr")))),
+      this);
+}
+
+void ThrownExceptionTypeCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *E = Result.Nodes.getNodeAs<Expr>("expr");
+  diag(E->getExprLoc(),
+       "thrown exception type is not nothrow copy constructible");
+}
+
+} // namespace cert
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/cert/ThrownExceptionTypeCheck.h b/clang-tidy/cert/ThrownExceptionTypeCheck.h
new file mode 100644 (file)
index 0000000..2f9d887
--- /dev/null
@@ -0,0 +1,35 @@
+//===--- ThrownExceptionTypeCheck.h - clang-tidy-----------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_THROWNEXCEPTIONTYPECHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_THROWNEXCEPTIONTYPECHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace cert {
+
+/// Checks whether a thrown object is nothrow copy constructible.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/cert-err60-cpp.html
+class ThrownExceptionTypeCheck : public ClangTidyCheck {
+public:
+  ThrownExceptionTypeCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace cert
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_THROWNEXCEPTIONTYPECHECK_H
diff --git a/clang-tidy/cert/VariadicFunctionDefCheck.cpp b/clang-tidy/cert/VariadicFunctionDefCheck.cpp
new file mode 100644 (file)
index 0000000..ea6112a
--- /dev/null
@@ -0,0 +1,42 @@
+//===--- VariadicfunctiondefCheck.cpp - clang-tidy-------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "VariadicFunctionDefCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace cert {
+
+void VariadicFunctionDefCheck::registerMatchers(MatchFinder *Finder) {
+  if (!getLangOpts().CPlusPlus)
+    return;
+
+  // We only care about function *definitions* that are variadic, and do not
+  // have extern "C" language linkage.
+  Finder->addMatcher(
+      functionDecl(isDefinition(), isVariadic(), unless(isExternC()))
+          .bind("func"),
+      this);
+}
+
+void VariadicFunctionDefCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *FD = Result.Nodes.getNodeAs<FunctionDecl>("func");
+
+  diag(FD->getLocation(),
+       "do not define a C-style variadic function; consider using a function "
+       "parameter pack or currying instead");
+}
+
+} // namespace cert
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/cert/VariadicFunctionDefCheck.h b/clang-tidy/cert/VariadicFunctionDefCheck.h
new file mode 100644 (file)
index 0000000..e215e8d
--- /dev/null
@@ -0,0 +1,35 @@
+//===--- VariadicFunctionDefCheck.h - clang-tidy-----------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_VARIADICFUNCTIONDEF_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_VARIADICFUNCTIONDEF_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace cert {
+
+/// Guards against any C-style variadic function definitions (not declarations).
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/cert-dcl50-cpp.html
+class VariadicFunctionDefCheck : public ClangTidyCheck {
+public:
+  VariadicFunctionDefCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace cert
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_VARIADICFUNCTIONDEF_H
diff --git a/clang-tidy/cppcoreguidelines/CMakeLists.txt b/clang-tidy/cppcoreguidelines/CMakeLists.txt
new file mode 100644 (file)
index 0000000..43ac55d
--- /dev/null
@@ -0,0 +1,29 @@
+set(LLVM_LINK_COMPONENTS support)
+
+add_clang_library(clangTidyCppCoreGuidelinesModule
+  CppCoreGuidelinesTidyModule.cpp
+  InterfacesGlobalInitCheck.cpp
+  NoMallocCheck.cpp
+  ProBoundsArrayToPointerDecayCheck.cpp
+  ProBoundsConstantArrayIndexCheck.cpp
+  ProBoundsPointerArithmeticCheck.cpp
+  ProTypeConstCastCheck.cpp
+  ProTypeCstyleCastCheck.cpp
+  ProTypeMemberInitCheck.cpp
+  ProTypeReinterpretCastCheck.cpp
+  ProTypeStaticCastDowncastCheck.cpp
+  ProTypeUnionAccessCheck.cpp
+  ProTypeVarargCheck.cpp
+  SpecialMemberFunctionsCheck.cpp
+  SlicingCheck.cpp
+
+  LINK_LIBS
+  clangAST
+  clangASTMatchers
+  clangBasic
+  clangLex
+  clangTidy
+  clangTidyMiscModule
+  clangTidyUtils
+  clangTooling
+  )
diff --git a/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp b/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp
new file mode 100644 (file)
index 0000000..6b9de8d
--- /dev/null
@@ -0,0 +1,79 @@
+//===--- CppCoreGuidelinesModule.cpp - clang-tidy -------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "../ClangTidy.h"
+#include "../ClangTidyModule.h"
+#include "../ClangTidyModuleRegistry.h"
+#include "../misc/UnconventionalAssignOperatorCheck.h"
+#include "InterfacesGlobalInitCheck.h"
+#include "NoMallocCheck.h"
+#include "ProBoundsArrayToPointerDecayCheck.h"
+#include "ProBoundsConstantArrayIndexCheck.h"
+#include "ProBoundsPointerArithmeticCheck.h"
+#include "ProTypeConstCastCheck.h"
+#include "ProTypeCstyleCastCheck.h"
+#include "ProTypeMemberInitCheck.h"
+#include "ProTypeReinterpretCastCheck.h"
+#include "ProTypeStaticCastDowncastCheck.h"
+#include "ProTypeUnionAccessCheck.h"
+#include "ProTypeVarargCheck.h"
+#include "SlicingCheck.h"
+#include "SpecialMemberFunctionsCheck.h"
+
+namespace clang {
+namespace tidy {
+namespace cppcoreguidelines {
+
+/// A module containing checks of the C++ Core Guidelines
+class CppCoreGuidelinesModule : public ClangTidyModule {
+public:
+  void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
+    CheckFactories.registerCheck<InterfacesGlobalInitCheck>(
+        "cppcoreguidelines-interfaces-global-init");
+    CheckFactories.registerCheck<NoMallocCheck>("cppcoreguidelines-no-malloc");
+    CheckFactories.registerCheck<ProBoundsArrayToPointerDecayCheck>(
+        "cppcoreguidelines-pro-bounds-array-to-pointer-decay");
+    CheckFactories.registerCheck<ProBoundsConstantArrayIndexCheck>(
+        "cppcoreguidelines-pro-bounds-constant-array-index");
+    CheckFactories.registerCheck<ProBoundsPointerArithmeticCheck>(
+        "cppcoreguidelines-pro-bounds-pointer-arithmetic");
+    CheckFactories.registerCheck<ProTypeConstCastCheck>(
+        "cppcoreguidelines-pro-type-const-cast");
+    CheckFactories.registerCheck<ProTypeCstyleCastCheck>(
+        "cppcoreguidelines-pro-type-cstyle-cast");
+    CheckFactories.registerCheck<ProTypeMemberInitCheck>(
+        "cppcoreguidelines-pro-type-member-init");
+    CheckFactories.registerCheck<ProTypeReinterpretCastCheck>(
+        "cppcoreguidelines-pro-type-reinterpret-cast");
+    CheckFactories.registerCheck<ProTypeStaticCastDowncastCheck>(
+        "cppcoreguidelines-pro-type-static-cast-downcast");
+    CheckFactories.registerCheck<ProTypeUnionAccessCheck>(
+        "cppcoreguidelines-pro-type-union-access");
+    CheckFactories.registerCheck<ProTypeVarargCheck>(
+        "cppcoreguidelines-pro-type-vararg");
+    CheckFactories.registerCheck<SpecialMemberFunctionsCheck>(
+        "cppcoreguidelines-special-member-functions");
+    CheckFactories.registerCheck<SlicingCheck>("cppcoreguidelines-slicing");
+    CheckFactories.registerCheck<misc::UnconventionalAssignOperatorCheck>(
+        "cppcoreguidelines-c-copy-assignment-signature");
+  }
+};
+
+// Register the LLVMTidyModule using this statically initialized variable.
+static ClangTidyModuleRegistry::Add<CppCoreGuidelinesModule>
+    X("cppcoreguidelines-module", "Adds checks for the C++ Core Guidelines.");
+
+} // namespace cppcoreguidelines
+
+// This anchor is used to force the linker to link in the generated object file
+// and thus register the CppCoreGuidelinesModule.
+volatile int CppCoreGuidelinesModuleAnchorSource = 0;
+
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/cppcoreguidelines/InterfacesGlobalInitCheck.cpp b/clang-tidy/cppcoreguidelines/InterfacesGlobalInitCheck.cpp
new file mode 100644 (file)
index 0000000..f601b24
--- /dev/null
@@ -0,0 +1,59 @@
+//===--- InterfacesGlobalInitCheck.cpp - clang-tidy------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "InterfacesGlobalInitCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace cppcoreguidelines {
+
+void InterfacesGlobalInitCheck::registerMatchers(MatchFinder *Finder) {
+  const auto IsGlobal =
+      allOf(hasGlobalStorage(),
+            hasDeclContext(anyOf(translationUnitDecl(), // Global scope.
+                                 namespaceDecl(),       // Namespace scope.
+                                 recordDecl())),        // Class scope.
+            unless(isConstexpr()));
+
+  const auto ReferencesUndefinedGlobalVar = declRefExpr(hasDeclaration(
+      varDecl(IsGlobal, unless(isDefinition())).bind("referencee")));
+
+  Finder->addMatcher(
+      varDecl(IsGlobal, isDefinition(),
+              hasInitializer(expr(hasDescendant(ReferencesUndefinedGlobalVar))))
+          .bind("var"),
+      this);
+}
+
+void InterfacesGlobalInitCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *const Var = Result.Nodes.getNodeAs<VarDecl>("var");
+  // For now assume that people who write macros know what they're doing.
+  if (Var->getLocation().isMacroID())
+    return;
+  const auto *const Referencee = Result.Nodes.getNodeAs<VarDecl>("referencee");
+  // If the variable has been defined, we're good.
+  const auto *const ReferenceeDef = Referencee->getDefinition();
+  if (ReferenceeDef != nullptr &&
+      Result.SourceManager->isBeforeInTranslationUnit(
+          ReferenceeDef->getLocation(), Var->getLocation())) {
+    return;
+  }
+  diag(Var->getLocation(),
+       "initializing non-local variable with non-const expression depending on "
+       "uninitialized non-local variable %0")
+      << Referencee;
+}
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/cppcoreguidelines/InterfacesGlobalInitCheck.h b/clang-tidy/cppcoreguidelines/InterfacesGlobalInitCheck.h
new file mode 100644 (file)
index 0000000..13712d1
--- /dev/null
@@ -0,0 +1,35 @@
+//===--- InterfacesGlobalInitCheck.h - clang-tidy----------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_INTERFACES_GLOBAL_INIT_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_INTERFACES_GLOBAL_INIT_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace cppcoreguidelines {
+
+/// Flags possible initialization order issues of static variables.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-interfaces-global-init.html
+class InterfacesGlobalInitCheck : public ClangTidyCheck {
+public:
+  InterfacesGlobalInitCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_INTERFACES_GLOBAL_INIT_H
diff --git a/clang-tidy/cppcoreguidelines/NoMallocCheck.cpp b/clang-tidy/cppcoreguidelines/NoMallocCheck.cpp
new file mode 100644 (file)
index 0000000..eda8907
--- /dev/null
@@ -0,0 +1,82 @@
+//===--- NoMallocCheck.cpp - clang-tidy------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "NoMallocCheck.h"
+#include "../utils/Matchers.h"
+#include "../utils/OptionsUtils.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include <algorithm>
+#include <string>
+#include <vector>
+
+using namespace clang::ast_matchers;
+using namespace clang::ast_matchers::internal;
+
+namespace clang {
+namespace tidy {
+namespace cppcoreguidelines {
+
+namespace {
+Matcher<FunctionDecl> hasAnyListedName(const std::string &FunctionNames) {
+  const std::vector<std::string> NameList =
+      utils::options::parseStringList(FunctionNames);
+  return hasAnyName(std::vector<StringRef>(NameList.begin(), NameList.end()));
+}
+}
+
+void NoMallocCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "Allocations", AllocList);
+  Options.store(Opts, "Reallocations", ReallocList);
+  Options.store(Opts, "Deallocations", DeallocList);
+}
+
+void NoMallocCheck::registerMatchers(MatchFinder *Finder) {
+  // C-style memory management is only problematic in C++.
+  if (!getLangOpts().CPlusPlus)
+    return;
+
+  // Registering malloc, will suggest RAII.
+  Finder->addMatcher(callExpr(callee(functionDecl(hasAnyListedName(AllocList))))
+                         .bind("allocation"),
+                     this);
+
+  // Registering realloc calls, suggest std::vector or std::string.
+  Finder->addMatcher(
+      callExpr(callee(functionDecl(hasAnyListedName(ReallocList))))
+          .bind("realloc"),
+      this);
+
+  // Registering free calls, will suggest RAII instead.
+  Finder->addMatcher(
+      callExpr(callee(functionDecl(hasAnyListedName(DeallocList))))
+          .bind("free"),
+      this);
+}
+
+void NoMallocCheck::check(const MatchFinder::MatchResult &Result) {
+  const CallExpr *Call = nullptr;
+  StringRef Recommendation;
+
+  if ((Call = Result.Nodes.getNodeAs<CallExpr>("allocation")))
+    Recommendation = "consider a container or a smart pointer";
+  else if ((Call = Result.Nodes.getNodeAs<CallExpr>("realloc")))
+    Recommendation = "consider std::vector or std::string";
+  else if ((Call = Result.Nodes.getNodeAs<CallExpr>("free")))
+    Recommendation = "use RAII";
+
+  assert(Call && "Unhandled binding in the Matcher");
+
+  diag(Call->getLocStart(), "do not manage memory manually; %0")
+      << Recommendation << SourceRange(Call->getLocStart(), Call->getLocEnd());
+}
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/cppcoreguidelines/NoMallocCheck.h b/clang-tidy/cppcoreguidelines/NoMallocCheck.h
new file mode 100644 (file)
index 0000000..4cec8a8
--- /dev/null
@@ -0,0 +1,62 @@
+//===--- NoMallocCheck.h - clang-tidy----------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_NO_MALLOC_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_NO_MALLOC_H
+
+#include "../ClangTidy.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+namespace clang {
+namespace tidy {
+namespace cppcoreguidelines {
+
+/// This checker is concerned with C-style memory management and suggest modern
+/// alternatives to it.
+/// The check is only enabled in C++. For analyzing malloc calls see Clang
+/// Static Analyzer - unix.Malloc.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-no-malloc.html
+class NoMallocCheck : public ClangTidyCheck {
+public:
+  /// Construct Checker and read in configuration for function names.
+  NoMallocCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context),
+        AllocList(Options.get("Allocations", "::malloc;::calloc")),
+        ReallocList(Options.get("Reallocations", "::realloc")),
+        DeallocList(Options.get("Deallocations", "::free")) {}
+
+  /// Make configuration of checker discoverable.
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+
+  /// Registering for malloc, calloc, realloc and free calls.
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+
+  /// Checks matched function calls and gives suggestion to modernize the code.
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+  /// Semicolon-separated list of fully qualified names of memory allocation
+  /// functions the check warns about. Defaults to `::malloc;::calloc`.
+  const std::string AllocList;
+  /// Semicolon-separated list of fully qualified names of memory reallocation
+  /// functions the check warns about. Defaults to `::realloc`.
+  const std::string ReallocList;
+  /// Semicolon-separated list of fully qualified names of memory deallocation
+  /// functions the check warns about. Defaults to `::free`.
+  const std::string DeallocList;
+};
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_NO_MALLOC_H
diff --git a/clang-tidy/cppcoreguidelines/ProBoundsArrayToPointerDecayCheck.cpp b/clang-tidy/cppcoreguidelines/ProBoundsArrayToPointerDecayCheck.cpp
new file mode 100644 (file)
index 0000000..bfcef89
--- /dev/null
@@ -0,0 +1,80 @@
+//===--- ProBoundsArrayToPointerDecayCheck.cpp - clang-tidy----------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ProBoundsArrayToPointerDecayCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace cppcoreguidelines {
+
+AST_MATCHER_P(CXXForRangeStmt, hasRangeBeginEndStmt,
+              ast_matchers::internal::Matcher<DeclStmt>, InnerMatcher) {
+  for (const DeclStmt *Stmt : {Node.getBeginStmt(), Node.getEndStmt()})
+    if (Stmt != nullptr && InnerMatcher.matches(*Stmt, Finder, Builder))
+      return true;
+  return false;
+}
+
+AST_MATCHER(Stmt, isInsideOfRangeBeginEndStmt) {
+  return stmt(hasAncestor(cxxForRangeStmt(
+                  hasRangeBeginEndStmt(hasDescendant(equalsNode(&Node))))))
+      .matches(Node, Finder, Builder);
+}
+
+AST_MATCHER_P(Expr, hasParentIgnoringImpCasts,
+              ast_matchers::internal::Matcher<Expr>, InnerMatcher) {
+  const Expr *E = &Node;
+  do {
+    ASTContext::DynTypedNodeList Parents =
+        Finder->getASTContext().getParents(*E);
+    if (Parents.size() != 1)
+      return false;
+    E = Parents[0].get<Expr>();
+    if (!E)
+      return false;
+  } while (isa<ImplicitCastExpr>(E));
+
+  return InnerMatcher.matches(*E, Finder, Builder);
+}
+
+void ProBoundsArrayToPointerDecayCheck::registerMatchers(MatchFinder *Finder) {
+  if (!getLangOpts().CPlusPlus)
+    return;
+
+  // The only allowed array to pointer decay
+  // 1) just before array subscription
+  // 2) inside a range-for over an array
+  // 3) if it converts a string literal to a pointer
+  Finder->addMatcher(
+      implicitCastExpr(unless(hasParent(arraySubscriptExpr())),
+                       unless(hasParentIgnoringImpCasts(explicitCastExpr())),
+                       unless(isInsideOfRangeBeginEndStmt()),
+                       unless(hasSourceExpression(stringLiteral())))
+          .bind("cast"),
+      this);
+}
+
+void ProBoundsArrayToPointerDecayCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  const auto *MatchedCast = Result.Nodes.getNodeAs<ImplicitCastExpr>("cast");
+  if (MatchedCast->getCastKind() != CK_ArrayToPointerDecay)
+    return;
+
+  diag(MatchedCast->getExprLoc(), "do not implicitly decay an array into a "
+                                  "pointer; consider using gsl::array_view or "
+                                  "an explicit cast instead");
+}
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/cppcoreguidelines/ProBoundsArrayToPointerDecayCheck.h b/clang-tidy/cppcoreguidelines/ProBoundsArrayToPointerDecayCheck.h
new file mode 100644 (file)
index 0000000..0afffb6
--- /dev/null
@@ -0,0 +1,35 @@
+//===--- ProBoundsArrayToPointerDecayCheck.h - clang-tidy--------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_BOUNDS_ARRAY_TO_POINTER_DECAY_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_BOUNDS_ARRAY_TO_POINTER_DECAY_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace cppcoreguidelines {
+
+/// This check flags all array to pointer decays
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-pro-bounds-array-to-pointer-decay.html
+class ProBoundsArrayToPointerDecayCheck : public ClangTidyCheck {
+public:
+  ProBoundsArrayToPointerDecayCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_BOUNDS_ARRAY_TO_POINTER_DECAY_H
diff --git a/clang-tidy/cppcoreguidelines/ProBoundsConstantArrayIndexCheck.cpp b/clang-tidy/cppcoreguidelines/ProBoundsConstantArrayIndexCheck.cpp
new file mode 100644 (file)
index 0000000..3844f68
--- /dev/null
@@ -0,0 +1,141 @@
+//===--- ProBoundsConstantArrayIndexCheck.cpp - clang-tidy-----------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ProBoundsConstantArrayIndexCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Lex/Preprocessor.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace cppcoreguidelines {
+
+ProBoundsConstantArrayIndexCheck::ProBoundsConstantArrayIndexCheck(
+    StringRef Name, ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context), GslHeader(Options.get("GslHeader", "")),
+      IncludeStyle(utils::IncludeSorter::parseIncludeStyle(
+          Options.get("IncludeStyle", "llvm"))) {}
+
+void ProBoundsConstantArrayIndexCheck::storeOptions(
+    ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "GslHeader", GslHeader);
+  Options.store(Opts, "IncludeStyle", IncludeStyle);
+}
+
+void ProBoundsConstantArrayIndexCheck::registerPPCallbacks(
+    CompilerInstance &Compiler) {
+  if (!getLangOpts().CPlusPlus)
+    return;
+
+  Inserter.reset(new utils::IncludeInserter(
+      Compiler.getSourceManager(), Compiler.getLangOpts(), IncludeStyle));
+  Compiler.getPreprocessor().addPPCallbacks(Inserter->CreatePPCallbacks());
+}
+
+void ProBoundsConstantArrayIndexCheck::registerMatchers(MatchFinder *Finder) {
+  if (!getLangOpts().CPlusPlus)
+    return;
+
+  // Note: if a struct contains an array member, the compiler-generated
+  // constructor has an arraySubscriptExpr.
+  Finder->addMatcher(
+      arraySubscriptExpr(
+          hasBase(ignoringImpCasts(hasType(constantArrayType().bind("type")))),
+          hasIndex(expr().bind("index")), unless(hasAncestor(isImplicit())))
+          .bind("expr"),
+      this);
+
+  Finder->addMatcher(
+      cxxOperatorCallExpr(
+          hasOverloadedOperatorName("[]"),
+          hasArgument(
+              0, hasType(cxxRecordDecl(hasName("::std::array")).bind("type"))),
+          hasArgument(1, expr().bind("index")))
+          .bind("expr"),
+      this);
+}
+
+void ProBoundsConstantArrayIndexCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  const auto *Matched = Result.Nodes.getNodeAs<Expr>("expr");
+  const auto *IndexExpr = Result.Nodes.getNodeAs<Expr>("index");
+
+  if (IndexExpr->isValueDependent())
+    return; // We check in the specialization.
+
+  llvm::APSInt Index;
+  if (!IndexExpr->isIntegerConstantExpr(Index, *Result.Context, nullptr,
+                                        /*isEvaluated=*/true)) {
+    SourceRange BaseRange;
+    if (const auto *ArraySubscriptE = dyn_cast<ArraySubscriptExpr>(Matched))
+      BaseRange = ArraySubscriptE->getBase()->getSourceRange();
+    else
+      BaseRange =
+          dyn_cast<CXXOperatorCallExpr>(Matched)->getArg(0)->getSourceRange();
+    SourceRange IndexRange = IndexExpr->getSourceRange();
+
+    auto Diag = diag(Matched->getExprLoc(),
+                     "do not use array subscript when the index is "
+                     "not an integer constant expression; use gsl::at() "
+                     "instead");
+    if (!GslHeader.empty()) {
+      Diag << FixItHint::CreateInsertion(BaseRange.getBegin(), "gsl::at(")
+           << FixItHint::CreateReplacement(
+                  SourceRange(BaseRange.getEnd().getLocWithOffset(1),
+                              IndexRange.getBegin().getLocWithOffset(-1)),
+                  ", ")
+           << FixItHint::CreateReplacement(Matched->getLocEnd(), ")");
+
+      Optional<FixItHint> Insertion = Inserter->CreateIncludeInsertion(
+          Result.SourceManager->getMainFileID(), GslHeader,
+          /*IsAngled=*/false);
+      if (Insertion)
+        Diag << Insertion.getValue();
+    }
+    return;
+  }
+
+  const auto *StdArrayDecl =
+      Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>("type");
+
+  // For static arrays, this is handled in clang-diagnostic-array-bounds.
+  if (!StdArrayDecl)
+    return;
+
+  if (Index.isSigned() && Index.isNegative()) {
+    diag(Matched->getExprLoc(), "std::array<> index %0 is negative")
+        << Index.toString(10);
+    return;
+  }
+
+  const TemplateArgumentList &TemplateArgs = StdArrayDecl->getTemplateArgs();
+  if (TemplateArgs.size() < 2)
+    return;
+  // First template arg of std::array is the type, second arg is the size.
+  const auto &SizeArg = TemplateArgs[1];
+  if (SizeArg.getKind() != TemplateArgument::Integral)
+    return;
+  llvm::APInt ArraySize = SizeArg.getAsIntegral();
+
+  // Get uint64_t values, because different bitwidths would lead to an assertion
+  // in APInt::uge.
+  if (Index.getZExtValue() >= ArraySize.getZExtValue()) {
+    diag(Matched->getExprLoc(),
+         "std::array<> index %0 is past the end of the array "
+         "(which contains %1 elements)")
+        << Index.toString(10) << ArraySize.toString(10, false);
+  }
+}
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/cppcoreguidelines/ProBoundsConstantArrayIndexCheck.h b/clang-tidy/cppcoreguidelines/ProBoundsConstantArrayIndexCheck.h
new file mode 100644 (file)
index 0000000..28b24a6
--- /dev/null
@@ -0,0 +1,42 @@
+//===--- ProBoundsConstantArrayIndexCheck.h - clang-tidy---------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_BOUNDS_CONSTANT_ARRAY_INDEX_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_BOUNDS_CONSTANT_ARRAY_INDEX_H
+
+#include "../ClangTidy.h"
+#include "../utils/IncludeInserter.h"
+
+namespace clang {
+namespace tidy {
+namespace cppcoreguidelines {
+
+/// This checks that all array subscriptions on static arrays and std::arrays
+/// have a constant index and are within bounds
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-pro-bounds-constant-array-index.html
+class ProBoundsConstantArrayIndexCheck : public ClangTidyCheck {
+  const std::string GslHeader;
+  const utils::IncludeSorter::IncludeStyle IncludeStyle;
+  std::unique_ptr<utils::IncludeInserter> Inserter;
+
+public:
+  ProBoundsConstantArrayIndexCheck(StringRef Name, ClangTidyContext *Context);
+  void registerPPCallbacks(CompilerInstance &Compiler) override;
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_BOUNDS_CONSTANT_ARRAY_INDEX_H
diff --git a/clang-tidy/cppcoreguidelines/ProBoundsPointerArithmeticCheck.cpp b/clang-tidy/cppcoreguidelines/ProBoundsPointerArithmeticCheck.cpp
new file mode 100644 (file)
index 0000000..d664b64
--- /dev/null
@@ -0,0 +1,59 @@
+//===--- ProBoundsPointerArithmeticCheck.cpp - clang-tidy------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ProBoundsPointerArithmeticCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace cppcoreguidelines {
+
+void ProBoundsPointerArithmeticCheck::registerMatchers(MatchFinder *Finder) {
+  if (!getLangOpts().CPlusPlus)
+    return;
+
+  // Flag all operators +, -, +=, -=, ++, -- that result in a pointer
+  Finder->addMatcher(
+      binaryOperator(
+          anyOf(hasOperatorName("+"), hasOperatorName("-"),
+                hasOperatorName("+="), hasOperatorName("-=")),
+          hasType(pointerType()),
+          unless(hasLHS(ignoringImpCasts(declRefExpr(to(isImplicit()))))))
+          .bind("expr"),
+      this);
+
+  Finder->addMatcher(
+      unaryOperator(anyOf(hasOperatorName("++"), hasOperatorName("--")),
+                    hasType(pointerType()))
+          .bind("expr"),
+      this);
+
+  // Array subscript on a pointer (not an array) is also pointer arithmetic
+  Finder->addMatcher(
+      arraySubscriptExpr(
+          hasBase(ignoringImpCasts(
+              anyOf(hasType(pointerType()),
+                    hasType(decayedType(hasDecayedType(pointerType())))))))
+          .bind("expr"),
+      this);
+}
+
+void ProBoundsPointerArithmeticCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  const auto *MatchedExpr = Result.Nodes.getNodeAs<Expr>("expr");
+
+  diag(MatchedExpr->getExprLoc(), "do not use pointer arithmetic");
+}
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/cppcoreguidelines/ProBoundsPointerArithmeticCheck.h b/clang-tidy/cppcoreguidelines/ProBoundsPointerArithmeticCheck.h
new file mode 100644 (file)
index 0000000..5ecf93c
--- /dev/null
@@ -0,0 +1,37 @@
+//===--- ProBoundsPointerArithmeticCheck.h - clang-tidy----------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_BOUNDS_POINTER_ARITHMETIC_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_BOUNDS_POINTER_ARITHMETIC_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace cppcoreguidelines {
+
+/// Flags all kinds of pointer arithmetic that have result of pointer type, i.e.
+/// +, -, +=, -=, ++, --. In addition, the [] operator on pointers (not on
+/// arrays) is flagged.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-pro-bounds-pointer-arithmetic.html
+class ProBoundsPointerArithmeticCheck : public ClangTidyCheck {
+public:
+  ProBoundsPointerArithmeticCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_BOUNDS_POINTER_ARITHMETIC_H
diff --git a/clang-tidy/cppcoreguidelines/ProTypeConstCastCheck.cpp b/clang-tidy/cppcoreguidelines/ProTypeConstCastCheck.cpp
new file mode 100644 (file)
index 0000000..4b6fb42
--- /dev/null
@@ -0,0 +1,34 @@
+//===--- ProTypeConstCastCheck.cpp - clang-tidy----------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ProTypeConstCastCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace cppcoreguidelines {
+
+void ProTypeConstCastCheck::registerMatchers(MatchFinder *Finder) {
+  if (!getLangOpts().CPlusPlus)
+    return;
+
+  Finder->addMatcher(cxxConstCastExpr().bind("cast"), this);
+}
+
+void ProTypeConstCastCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *MatchedCast = Result.Nodes.getNodeAs<CXXConstCastExpr>("cast");
+  diag(MatchedCast->getOperatorLoc(), "do not use const_cast");
+}
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/cppcoreguidelines/ProTypeConstCastCheck.h b/clang-tidy/cppcoreguidelines/ProTypeConstCastCheck.h
new file mode 100644 (file)
index 0000000..92a3a1b
--- /dev/null
@@ -0,0 +1,35 @@
+//===--- ProTypeConstCastCheck.h - clang-tidy--------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_TYPE_CONST_CAST_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_TYPE_CONST_CAST_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace cppcoreguidelines {
+
+/// This check flags all instances of const_cast
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-pro-type-const-cast.html
+class ProTypeConstCastCheck : public ClangTidyCheck {
+public:
+  ProTypeConstCastCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_TYPE_CONST_CAST_H
diff --git a/clang-tidy/cppcoreguidelines/ProTypeCstyleCastCheck.cpp b/clang-tidy/cppcoreguidelines/ProTypeCstyleCastCheck.cpp
new file mode 100644 (file)
index 0000000..52156ad
--- /dev/null
@@ -0,0 +1,108 @@
+//===--- ProTypeCstyleCastCheck.cpp - clang-tidy---------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ProTypeCstyleCastCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace cppcoreguidelines {
+
+static bool needsConstCast(QualType SourceType, QualType DestType) {
+  SourceType = SourceType.getNonReferenceType();
+  DestType = DestType.getNonReferenceType();
+  while (SourceType->isPointerType() && DestType->isPointerType()) {
+    SourceType = SourceType->getPointeeType();
+    DestType = DestType->getPointeeType();
+    if (SourceType.isConstQualified() && !DestType.isConstQualified())
+      return true;
+  }
+  return false;
+}
+
+void ProTypeCstyleCastCheck::registerMatchers(MatchFinder *Finder) {
+  if (!getLangOpts().CPlusPlus)
+    return;
+
+  Finder->addMatcher(
+      cStyleCastExpr(unless(isInTemplateInstantiation())).bind("cast"), this);
+}
+
+void ProTypeCstyleCastCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *MatchedCast = Result.Nodes.getNodeAs<CStyleCastExpr>("cast");
+
+  if (MatchedCast->getCastKind() == CK_BitCast ||
+      MatchedCast->getCastKind() == CK_LValueBitCast ||
+      MatchedCast->getCastKind() == CK_IntegralToPointer ||
+      MatchedCast->getCastKind() == CK_PointerToIntegral ||
+      MatchedCast->getCastKind() == CK_ReinterpretMemberPointer) {
+    diag(MatchedCast->getLocStart(),
+         "do not use C-style cast to convert between unrelated types");
+    return;
+  }
+
+  QualType SourceType = MatchedCast->getSubExpr()->getType();
+
+  if (MatchedCast->getCastKind() == CK_BaseToDerived) {
+    const auto *SourceDecl = SourceType->getPointeeCXXRecordDecl();
+    if (!SourceDecl) // The cast is from object to reference.
+      SourceDecl = SourceType->getAsCXXRecordDecl();
+    if (!SourceDecl)
+      return;
+
+    if (SourceDecl->isPolymorphic()) {
+      // Leave type spelling exactly as it was (unlike
+      // getTypeAsWritten().getAsString() which would spell enum types 'enum
+      // X').
+      StringRef DestTypeString = Lexer::getSourceText(
+          CharSourceRange::getTokenRange(
+              MatchedCast->getLParenLoc().getLocWithOffset(1),
+              MatchedCast->getRParenLoc().getLocWithOffset(-1)),
+          *Result.SourceManager, getLangOpts());
+
+      auto diag_builder = diag(
+          MatchedCast->getLocStart(),
+          "do not use C-style cast to downcast from a base to a derived class; "
+          "use dynamic_cast instead");
+
+      const Expr *SubExpr =
+          MatchedCast->getSubExprAsWritten()->IgnoreImpCasts();
+      std::string CastText = ("dynamic_cast<" + DestTypeString + ">").str();
+      if (!isa<ParenExpr>(SubExpr)) {
+        CastText.push_back('(');
+        diag_builder << FixItHint::CreateInsertion(
+            Lexer::getLocForEndOfToken(SubExpr->getLocEnd(), 0,
+                                       *Result.SourceManager, getLangOpts()),
+            ")");
+      }
+      auto ParenRange = CharSourceRange::getTokenRange(
+          MatchedCast->getLParenLoc(), MatchedCast->getRParenLoc());
+      diag_builder << FixItHint::CreateReplacement(ParenRange, CastText);
+    } else {
+      diag(
+          MatchedCast->getLocStart(),
+          "do not use C-style cast to downcast from a base to a derived class");
+    }
+    return;
+  }
+
+  if (MatchedCast->getCastKind() == CK_NoOp &&
+      needsConstCast(SourceType, MatchedCast->getType())) {
+    diag(MatchedCast->getLocStart(),
+         "do not use C-style cast to cast away constness");
+  }
+}
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/cppcoreguidelines/ProTypeCstyleCastCheck.h b/clang-tidy/cppcoreguidelines/ProTypeCstyleCastCheck.h
new file mode 100644 (file)
index 0000000..c08b883
--- /dev/null
@@ -0,0 +1,36 @@
+//===--- ProTypeCstyleCastCheck.h - clang-tidy-------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_TYPE_CSTYLE_CAST_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_TYPE_CSTYLE_CAST_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace cppcoreguidelines {
+
+/// This check flags all use of C-style casts that perform a static_cast
+/// downcast, const_cast, or reinterpret_cast.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-pro-type-cstyle-cast.html
+class ProTypeCstyleCastCheck : public ClangTidyCheck {
+public:
+  ProTypeCstyleCastCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_TYPE_CSTYLE_CAST_H
diff --git a/clang-tidy/cppcoreguidelines/ProTypeMemberInitCheck.cpp b/clang-tidy/cppcoreguidelines/ProTypeMemberInitCheck.cpp
new file mode 100644 (file)
index 0000000..7fbc896
--- /dev/null
@@ -0,0 +1,485 @@
+//===--- ProTypeMemberInitCheck.cpp - clang-tidy---------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ProTypeMemberInitCheck.h"
+#include "../utils/LexerUtils.h"
+#include "../utils/Matchers.h"
+#include "../utils/TypeTraits.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+#include "llvm/ADT/SmallPtrSet.h"
+
+using namespace clang::ast_matchers;
+using namespace clang::tidy::matchers;
+using llvm::SmallPtrSet;
+using llvm::SmallPtrSetImpl;
+
+namespace clang {
+namespace tidy {
+namespace cppcoreguidelines {
+
+namespace {
+
+AST_MATCHER(CXXRecordDecl, hasDefaultConstructor) {
+  return Node.hasDefaultConstructor();
+}
+
+// Iterate over all the fields in a record type, both direct and indirect (e.g.
+// if the record contains an anonmyous struct). If OneFieldPerUnion is true and
+// the record type (or indirect field) is a union, forEachField will stop after
+// the first field.
+template <typename T, typename Func>
+void forEachField(const RecordDecl &Record, const T &Fields,
+                  bool OneFieldPerUnion, Func &&Fn) {
+  for (const FieldDecl *F : Fields) {
+    if (F->isAnonymousStructOrUnion()) {
+      if (const CXXRecordDecl *R = F->getType()->getAsCXXRecordDecl())
+        forEachField(*R, R->fields(), OneFieldPerUnion, Fn);
+    } else {
+      Fn(F);
+    }
+
+    if (OneFieldPerUnion && Record.isUnion())
+      break;
+  }
+}
+
+void removeFieldsInitializedInBody(
+    const Stmt &Stmt, ASTContext &Context,
+    SmallPtrSetImpl<const FieldDecl *> &FieldDecls) {
+  auto Matches =
+      match(findAll(binaryOperator(
+                hasOperatorName("="),
+                hasLHS(memberExpr(member(fieldDecl().bind("fieldDecl")))))),
+            Stmt, Context);
+  for (const auto &Match : Matches)
+    FieldDecls.erase(Match.getNodeAs<FieldDecl>("fieldDecl"));
+}
+
+StringRef getName(const FieldDecl *Field) { return Field->getName(); }
+
+StringRef getName(const RecordDecl *Record) {
+  // Get the typedef name if this is a C-style anonymous struct and typedef.
+  if (const TypedefNameDecl *Typedef = Record->getTypedefNameForAnonDecl())
+    return Typedef->getName();
+  return Record->getName();
+}
+
+// Creates comma separated list of decls requiring initialization in order of
+// declaration.
+template <typename R, typename T>
+std::string
+toCommaSeparatedString(const R &OrderedDecls,
+                       const SmallPtrSetImpl<const T *> &DeclsToInit) {
+  SmallVector<StringRef, 16> Names;
+  for (const T *Decl : OrderedDecls) {
+    if (DeclsToInit.count(Decl))
+      Names.emplace_back(getName(Decl));
+  }
+  return llvm::join(Names.begin(), Names.end(), ", ");
+}
+
+SourceLocation getLocationForEndOfToken(const ASTContext &Context,
+                                        SourceLocation Location) {
+  return Lexer::getLocForEndOfToken(Location, 0, Context.getSourceManager(),
+                                    Context.getLangOpts());
+}
+
+// There are 3 kinds of insertion placements:
+enum class InitializerPlacement {
+  // 1. The fields are inserted after an existing CXXCtorInitializer stored in
+  // Where. This will be the case whenever there is a written initializer before
+  // the fields available.
+  After,
+
+  // 2. The fields are inserted before the first existing initializer stored in
+  // Where.
+  Before,
+
+  // 3. There are no written initializers and the fields will be inserted before
+  // the constructor's body creating a new initializer list including the ':'.
+  New
+};
+
+// An InitializerInsertion contains a list of fields and/or base classes to
+// insert into the initializer list of a constructor. We use this to ensure
+// proper absolute ordering according to the class declaration relative to the
+// (perhaps improper) ordering in the existing initializer list, if any.
+struct IntializerInsertion {
+  IntializerInsertion(InitializerPlacement Placement,
+                      const CXXCtorInitializer *Where)
+      : Placement(Placement), Where(Where) {}
+
+  SourceLocation getLocation(const ASTContext &Context,
+                             const CXXConstructorDecl &Constructor) const {
+    assert((Where != nullptr || Placement == InitializerPlacement::New) &&
+           "Location should be relative to an existing initializer or this "
+           "insertion represents a new initializer list.");
+    SourceLocation Location;
+    switch (Placement) {
+    case InitializerPlacement::New:
+      Location = utils::lexer::getPreviousToken(
+                     Context, Constructor.getBody()->getLocStart())
+                     .getLocation();
+      break;
+    case InitializerPlacement::Before:
+      Location = utils::lexer::getPreviousToken(
+                     Context, Where->getSourceRange().getBegin())
+                     .getLocation();
+      break;
+    case InitializerPlacement::After:
+      Location = Where->getRParenLoc();
+      break;
+    }
+    return getLocationForEndOfToken(Context, Location);
+  }
+
+  std::string codeToInsert() const {
+    assert(!Initializers.empty() && "No initializers to insert");
+    std::string Code;
+    llvm::raw_string_ostream Stream(Code);
+    std::string joined =
+        llvm::join(Initializers.begin(), Initializers.end(), "(), ");
+    switch (Placement) {
+    case InitializerPlacement::New:
+      Stream << " : " << joined << "()";
+      break;
+    case InitializerPlacement::Before:
+      Stream << " " << joined << "(),";
+      break;
+    case InitializerPlacement::After:
+      Stream << ", " << joined << "()";
+      break;
+    }
+    return Stream.str();
+  }
+
+  InitializerPlacement Placement;
+  const CXXCtorInitializer *Where;
+  SmallVector<std::string, 4> Initializers;
+};
+
+// Convenience utility to get a RecordDecl from a QualType.
+const RecordDecl *getCanonicalRecordDecl(const QualType &Type) {
+  if (const auto *RT = Type.getCanonicalType()->getAs<RecordType>())
+    return RT->getDecl();
+  return nullptr;
+}
+
+template <typename R, typename T>
+SmallVector<IntializerInsertion, 16>
+computeInsertions(const CXXConstructorDecl::init_const_range &Inits,
+                  const R &OrderedDecls,
+                  const SmallPtrSetImpl<const T *> &DeclsToInit) {
+  SmallVector<IntializerInsertion, 16> Insertions;
+  Insertions.emplace_back(InitializerPlacement::New, nullptr);
+
+  typename R::const_iterator Decl = std::begin(OrderedDecls);
+  for (const CXXCtorInitializer *Init : Inits) {
+    if (Init->isWritten()) {
+      if (Insertions.size() == 1)
+        Insertions.emplace_back(InitializerPlacement::Before, Init);
+
+      // Gets either the field or base class being initialized by the provided
+      // initializer.
+      const auto *InitDecl =
+          Init->isAnyMemberInitializer()
+              ? static_cast<const NamedDecl *>(Init->getAnyMember())
+              : Init->getBaseClass()->getAsCXXRecordDecl();
+
+      // Add all fields between current field up until the next intializer.
+      for (; Decl != std::end(OrderedDecls) && *Decl != InitDecl; ++Decl) {
+        if (const auto *D = dyn_cast<T>(*Decl)) {
+          if (DeclsToInit.count(D) > 0)
+            Insertions.back().Initializers.emplace_back(getName(D));
+        }
+      }
+
+      Insertions.emplace_back(InitializerPlacement::After, Init);
+    }
+  }
+
+  // Add remaining decls that require initialization.
+  for (; Decl != std::end(OrderedDecls); ++Decl) {
+    if (const auto *D = dyn_cast<T>(*Decl)) {
+      if (DeclsToInit.count(D) > 0)
+        Insertions.back().Initializers.emplace_back(getName(D));
+    }
+  }
+  return Insertions;
+}
+
+// Gets the list of bases and members that could possibly be initialized, in
+// order as they appear in the class declaration.
+void getInitializationsInOrder(const CXXRecordDecl &ClassDecl,
+                               SmallVectorImpl<const NamedDecl *> &Decls) {
+  Decls.clear();
+  for (const auto &Base : ClassDecl.bases()) {
+    // Decl may be null if the base class is a template parameter.
+    if (const NamedDecl *Decl = getCanonicalRecordDecl(Base.getType())) {
+      Decls.emplace_back(Decl);
+    }
+  }
+  forEachField(ClassDecl, ClassDecl.fields(), false,
+               [&](const FieldDecl *F) { Decls.push_back(F); });
+}
+
+template <typename T>
+void fixInitializerList(const ASTContext &Context, DiagnosticBuilder &Diag,
+                        const CXXConstructorDecl *Ctor,
+                        const SmallPtrSetImpl<const T *> &DeclsToInit) {
+  // Do not propose fixes in macros since we cannot place them correctly.
+  if (Ctor->getLocStart().isMacroID())
+    return;
+
+  SmallVector<const NamedDecl *, 16> OrderedDecls;
+  getInitializationsInOrder(*Ctor->getParent(), OrderedDecls);
+
+  for (const auto &Insertion :
+       computeInsertions(Ctor->inits(), OrderedDecls, DeclsToInit)) {
+    if (!Insertion.Initializers.empty())
+      Diag << FixItHint::CreateInsertion(Insertion.getLocation(Context, *Ctor),
+                                         Insertion.codeToInsert());
+  }
+}
+
+} // anonymous namespace
+
+ProTypeMemberInitCheck::ProTypeMemberInitCheck(StringRef Name,
+                                               ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      IgnoreArrays(Options.get("IgnoreArrays", false)) {}
+
+void ProTypeMemberInitCheck::registerMatchers(MatchFinder *Finder) {
+  if (!getLangOpts().CPlusPlus)
+    return;
+
+  auto IsUserProvidedNonDelegatingConstructor =
+      allOf(isUserProvided(),
+            unless(anyOf(isInstantiated(), isDelegatingConstructor())));
+  auto IsNonTrivialDefaultConstructor = allOf(
+      isDefaultConstructor(), unless(isUserProvided()),
+      hasParent(cxxRecordDecl(unless(isTriviallyDefaultConstructible()))));
+  Finder->addMatcher(
+      cxxConstructorDecl(isDefinition(),
+                         anyOf(IsUserProvidedNonDelegatingConstructor,
+                               IsNonTrivialDefaultConstructor))
+          .bind("ctor"),
+      this);
+
+  // Match classes with a default constructor that is defaulted or is not in the
+  // AST.
+  Finder->addMatcher(
+      cxxRecordDecl(
+          isDefinition(), unless(isInstantiated()), hasDefaultConstructor(),
+          anyOf(has(cxxConstructorDecl(isDefaultConstructor(), isDefaulted(),
+                                       unless(isImplicit()))),
+                unless(has(cxxConstructorDecl()))),
+          unless(isTriviallyDefaultConstructible()))
+          .bind("record"),
+      this);
+
+  auto HasDefaultConstructor = hasInitializer(
+      cxxConstructExpr(unless(requiresZeroInitialization()),
+                       hasDeclaration(cxxConstructorDecl(
+                           isDefaultConstructor(), unless(isUserProvided())))));
+  Finder->addMatcher(
+      varDecl(isDefinition(), HasDefaultConstructor,
+              hasAutomaticStorageDuration(),
+              hasType(recordDecl(has(fieldDecl()),
+                                 isTriviallyDefaultConstructible())))
+          .bind("var"),
+      this);
+}
+
+void ProTypeMemberInitCheck::check(const MatchFinder::MatchResult &Result) {
+  if (const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructorDecl>("ctor")) {
+    // Skip declarations delayed by late template parsing without a body.
+    if (!Ctor->getBody())
+      return;
+    checkMissingMemberInitializer(*Result.Context, *Ctor->getParent(), Ctor);
+    checkMissingBaseClassInitializer(*Result.Context, *Ctor->getParent(), Ctor);
+  } else if (const auto *Record =
+                 Result.Nodes.getNodeAs<CXXRecordDecl>("record")) {
+    assert(Record->hasDefaultConstructor() &&
+           "Matched record should have a default constructor");
+    checkMissingMemberInitializer(*Result.Context, *Record, nullptr);
+    checkMissingBaseClassInitializer(*Result.Context, *Record, nullptr);
+  } else if (const auto *Var = Result.Nodes.getNodeAs<VarDecl>("var")) {
+    checkUninitializedTrivialType(*Result.Context, Var);
+  }
+}
+
+void ProTypeMemberInitCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "IgnoreArrays", IgnoreArrays);
+}
+
+// FIXME: Copied from clang/lib/Sema/SemaDeclCXX.cpp.
+static bool isIncompleteOrZeroLengthArrayType(ASTContext &Context, QualType T) {
+  if (T->isIncompleteArrayType())
+    return true;
+
+  while (const ConstantArrayType *ArrayT = Context.getAsConstantArrayType(T)) {
+    if (!ArrayT->getSize())
+      return true;
+
+    T = ArrayT->getElementType();
+  }
+
+  return false;
+}
+
+static bool isEmpty(ASTContext &Context, const QualType &Type) {
+  if (const CXXRecordDecl *ClassDecl = Type->getAsCXXRecordDecl()) {
+    return ClassDecl->isEmpty();
+  }
+  return isIncompleteOrZeroLengthArrayType(Context, Type);
+}
+
+void ProTypeMemberInitCheck::checkMissingMemberInitializer(
+    ASTContext &Context, const CXXRecordDecl &ClassDecl,
+    const CXXConstructorDecl *Ctor) {
+  bool IsUnion = ClassDecl.isUnion();
+
+  if (IsUnion && ClassDecl.hasInClassInitializer())
+    return;
+
+  // Gather all fields (direct and indirect) that need to be initialized.
+  SmallPtrSet<const FieldDecl *, 16> FieldsToInit;
+  forEachField(ClassDecl, ClassDecl.fields(), false, [&](const FieldDecl *F) {
+    if (!F->hasInClassInitializer() &&
+        utils::type_traits::isTriviallyDefaultConstructible(F->getType(),
+                                                            Context) &&
+        !isEmpty(Context, F->getType()) && !F->isUnnamedBitfield())
+      FieldsToInit.insert(F);
+  });
+  if (FieldsToInit.empty())
+    return;
+
+  if (Ctor) {
+    for (const CXXCtorInitializer *Init : Ctor->inits()) {
+      // Remove any fields that were explicitly written in the initializer list
+      // or in-class.
+      if (Init->isAnyMemberInitializer() && Init->isWritten()) {
+        if (IsUnion)
+          return; // We can only initialize one member of a union.
+        FieldsToInit.erase(Init->getAnyMember());
+      }
+    }
+    removeFieldsInitializedInBody(*Ctor->getBody(), Context, FieldsToInit);
+  }
+
+  // Collect all fields in order, both direct fields and indirect fields from
+  // anonmyous record types.
+  SmallVector<const FieldDecl *, 16> OrderedFields;
+  forEachField(ClassDecl, ClassDecl.fields(), false,
+               [&](const FieldDecl *F) { OrderedFields.push_back(F); });
+
+  // Collect all the fields we need to initialize, including indirect fields.
+  SmallPtrSet<const FieldDecl *, 16> AllFieldsToInit;
+  forEachField(ClassDecl, FieldsToInit, false,
+               [&](const FieldDecl *F) { AllFieldsToInit.insert(F); });
+  if (AllFieldsToInit.empty())
+    return;
+
+  DiagnosticBuilder Diag =
+      diag(Ctor ? Ctor->getLocStart() : ClassDecl.getLocation(),
+           IsUnion
+               ? "union constructor should initialize one of these fields: %0"
+               : "constructor does not initialize these fields: %0")
+      << toCommaSeparatedString(OrderedFields, AllFieldsToInit);
+
+  // Do not propose fixes for constructors in macros since we cannot place them
+  // correctly.
+  if (Ctor && Ctor->getLocStart().isMacroID())
+    return;
+
+  // Collect all fields but only suggest a fix for the first member of unions,
+  // as initializing more than one union member is an error.
+  SmallPtrSet<const FieldDecl *, 16> FieldsToFix;
+  forEachField(ClassDecl, FieldsToInit, true, [&](const FieldDecl *F) {
+    // Don't suggest fixes for enums because we don't know a good default.
+    // Don't suggest fixes for bitfields because in-class initialization is not
+    // possible.
+    if (!F->getType()->isEnumeralType() && !F->isBitField())
+      FieldsToFix.insert(F);
+  });
+  if (FieldsToFix.empty())
+    return;
+
+  // Use in-class initialization if possible.
+  if (Context.getLangOpts().CPlusPlus11) {
+    for (const FieldDecl *Field : FieldsToFix) {
+      Diag << FixItHint::CreateInsertion(
+          getLocationForEndOfToken(Context, Field->getSourceRange().getEnd()),
+          "{}");
+    }
+  } else if (Ctor) {
+    // Otherwise, rewrite the constructor's initializer list.
+    fixInitializerList(Context, Diag, Ctor, FieldsToFix);
+  }
+}
+
+void ProTypeMemberInitCheck::checkMissingBaseClassInitializer(
+    const ASTContext &Context, const CXXRecordDecl &ClassDecl,
+    const CXXConstructorDecl *Ctor) {
+
+  // Gather any base classes that need to be initialized.
+  SmallVector<const RecordDecl *, 4> AllBases;
+  SmallPtrSet<const RecordDecl *, 4> BasesToInit;
+  for (const CXXBaseSpecifier &Base : ClassDecl.bases()) {
+    if (const auto *BaseClassDecl = getCanonicalRecordDecl(Base.getType())) {
+      AllBases.emplace_back(BaseClassDecl);
+      if (!BaseClassDecl->field_empty() &&
+          utils::type_traits::isTriviallyDefaultConstructible(Base.getType(),
+                                                              Context))
+        BasesToInit.insert(BaseClassDecl);
+    }
+  }
+
+  if (BasesToInit.empty())
+    return;
+
+  // Remove any bases that were explicitly written in the initializer list.
+  if (Ctor) {
+    if (Ctor->isImplicit())
+      return;
+
+    for (const CXXCtorInitializer *Init : Ctor->inits()) {
+      if (Init->isBaseInitializer() && Init->isWritten())
+        BasesToInit.erase(Init->getBaseClass()->getAsCXXRecordDecl());
+    }
+  }
+
+  if (BasesToInit.empty())
+    return;
+
+  DiagnosticBuilder Diag =
+      diag(Ctor ? Ctor->getLocStart() : ClassDecl.getLocation(),
+           "constructor does not initialize these bases: %0")
+      << toCommaSeparatedString(AllBases, BasesToInit);
+
+  if (Ctor)
+    fixInitializerList(Context, Diag, Ctor, BasesToInit);
+}
+
+void ProTypeMemberInitCheck::checkUninitializedTrivialType(
+    const ASTContext &Context, const VarDecl *Var) {
+  DiagnosticBuilder Diag =
+      diag(Var->getLocStart(), "uninitialized record type: %0") << Var;
+
+  Diag << FixItHint::CreateInsertion(
+      getLocationForEndOfToken(Context, Var->getSourceRange().getEnd()),
+      Context.getLangOpts().CPlusPlus11 ? "{}" : " = {}");
+}
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/cppcoreguidelines/ProTypeMemberInitCheck.h b/clang-tidy/cppcoreguidelines/ProTypeMemberInitCheck.h
new file mode 100644 (file)
index 0000000..20d3f60
--- /dev/null
@@ -0,0 +1,74 @@
+//===--- ProTypeMemberInitCheck.h - clang-tidy-------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_TYPE_MEMBER_INIT_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_TYPE_MEMBER_INIT_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace cppcoreguidelines {
+
+/// \brief Implements C++ Core Guidelines Type.6.
+///
+/// Checks that every user-provided constructor value-initializes all class
+/// members and base classes that would have undefined behavior otherwise. Also
+/// check that any record types without user-provided default constructors are
+/// value-initialized where used.
+///
+/// Members initialized through function calls in the body of the constructor
+/// will result in false positives.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-pro-type-member-init.html
+/// TODO: See if 'fixes' for false positives are optimized away by the compiler.
+/// TODO: For classes with multiple constructors, make sure that we don't offer
+///     multiple in-class initializer fixits for the same  member.
+class ProTypeMemberInitCheck : public ClangTidyCheck {
+public:
+  ProTypeMemberInitCheck(StringRef Name, ClangTidyContext *Context);
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+
+private:
+  // Checks Type.6 part 1:
+  // Issue a diagnostic for any constructor of a non-trivially-constructible
+  // type that does not initialize all member variables.
+  //
+  // To fix: Write a data member initializer, or mention it in the member
+  // initializer list.
+  void checkMissingMemberInitializer(ASTContext &Context,
+                                     const CXXRecordDecl &ClassDecl,
+                                     const CXXConstructorDecl *Ctor);
+
+  // A subtle side effect of Type.6 part 2:
+  // Make sure to initialize trivially constructible base classes.
+  void checkMissingBaseClassInitializer(const ASTContext &Context,
+                                        const CXXRecordDecl &ClassDecl,
+                                        const CXXConstructorDecl *Ctor);
+
+  // Checks Type.6 part 2:
+  // Issue a diagnostic when constructing an object of a trivially constructible
+  // type without () or {} to initialize its members.
+  //
+  // To fix: Add () or {}.
+  void checkUninitializedTrivialType(const ASTContext &Context,
+                                     const VarDecl *Var);
+
+  // Whether arrays need to be initialized or not. Default is false.
+  bool IgnoreArrays;
+};
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_TYPE_MEMBER_INIT_H
diff --git a/clang-tidy/cppcoreguidelines/ProTypeReinterpretCastCheck.cpp b/clang-tidy/cppcoreguidelines/ProTypeReinterpretCastCheck.cpp
new file mode 100644 (file)
index 0000000..e56e638
--- /dev/null
@@ -0,0 +1,36 @@
+//===--- ProTypeReinterpretCastCheck.cpp - clang-tidy----------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ProTypeReinterpretCastCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace cppcoreguidelines {
+
+void ProTypeReinterpretCastCheck::registerMatchers(MatchFinder *Finder) {
+  if (!getLangOpts().CPlusPlus)
+    return;
+
+  Finder->addMatcher(cxxReinterpretCastExpr().bind("cast"), this);
+}
+
+void ProTypeReinterpretCastCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  const auto *MatchedCast =
+      Result.Nodes.getNodeAs<CXXReinterpretCastExpr>("cast");
+  diag(MatchedCast->getOperatorLoc(), "do not use reinterpret_cast");
+}
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/cppcoreguidelines/ProTypeReinterpretCastCheck.h b/clang-tidy/cppcoreguidelines/ProTypeReinterpretCastCheck.h
new file mode 100644 (file)
index 0000000..9610546
--- /dev/null
@@ -0,0 +1,35 @@
+//===--- ProTypeReinterpretCast.h - clang-tidy------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_TYPE_REINTERPRETCAST_CHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_TYPE_REINTERPRETCAST_CHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace cppcoreguidelines {
+
+/// Flags all occurrences of reinterpret_cast
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-pro-type-reinterpret-cast.html
+class ProTypeReinterpretCastCheck : public ClangTidyCheck {
+public:
+  ProTypeReinterpretCastCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_TYPE_REINTERPRETCAST_CHECK_H
diff --git a/clang-tidy/cppcoreguidelines/ProTypeStaticCastDowncastCheck.cpp b/clang-tidy/cppcoreguidelines/ProTypeStaticCastDowncastCheck.cpp
new file mode 100644 (file)
index 0000000..f43e4fa
--- /dev/null
@@ -0,0 +1,55 @@
+//===--- ProTypeStaticCastDowncastCheck.cpp - clang-tidy-------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ProTypeStaticCastDowncastCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace cppcoreguidelines {
+
+void ProTypeStaticCastDowncastCheck::registerMatchers(MatchFinder *Finder) {
+  if (!getLangOpts().CPlusPlus)
+    return;
+
+  Finder->addMatcher(
+      cxxStaticCastExpr(unless(isInTemplateInstantiation())).bind("cast"),
+      this);
+}
+
+void ProTypeStaticCastDowncastCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  const auto *MatchedCast = Result.Nodes.getNodeAs<CXXStaticCastExpr>("cast");
+  if (MatchedCast->getCastKind() != CK_BaseToDerived)
+    return;
+
+  QualType SourceType = MatchedCast->getSubExpr()->getType();
+  const auto *SourceDecl = SourceType->getPointeeCXXRecordDecl();
+  if (!SourceDecl) // The cast is from object to reference
+    SourceDecl = SourceType->getAsCXXRecordDecl();
+  if (!SourceDecl)
+    return;
+
+  if (SourceDecl->isPolymorphic())
+    diag(MatchedCast->getOperatorLoc(),
+         "do not use static_cast to downcast from a base to a derived class; "
+         "use dynamic_cast instead")
+        << FixItHint::CreateReplacement(MatchedCast->getOperatorLoc(),
+                                        "dynamic_cast");
+  else
+    diag(MatchedCast->getOperatorLoc(),
+         "do not use static_cast to downcast from a base to a derived class");
+}
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/cppcoreguidelines/ProTypeStaticCastDowncastCheck.h b/clang-tidy/cppcoreguidelines/ProTypeStaticCastDowncastCheck.h
new file mode 100644 (file)
index 0000000..b6d76a6
--- /dev/null
@@ -0,0 +1,36 @@
+//===--- ProTypeStaticCastDowncastCheck.h - clang-tidy-----------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_TYPE_STATIC_CAST_DOWNCAST_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_TYPE_STATIC_CAST_DOWNCAST_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace cppcoreguidelines {
+
+/// Checks for usages of static_cast, where a base class is downcasted to a
+/// derived class.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-pro-type-static-cast-downcast.html
+class ProTypeStaticCastDowncastCheck : public ClangTidyCheck {
+public:
+  ProTypeStaticCastDowncastCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_TYPE_STATIC_CAST_DOWNCAST_H
diff --git a/clang-tidy/cppcoreguidelines/ProTypeUnionAccessCheck.cpp b/clang-tidy/cppcoreguidelines/ProTypeUnionAccessCheck.cpp
new file mode 100644 (file)
index 0000000..09752f6
--- /dev/null
@@ -0,0 +1,38 @@
+//===--- ProTypeUnionAccessCheck.cpp - clang-tidy--------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ProTypeUnionAccessCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace cppcoreguidelines {
+
+void ProTypeUnionAccessCheck::registerMatchers(MatchFinder *Finder) {
+  if (!getLangOpts().CPlusPlus)
+    return;
+
+  Finder->addMatcher(
+      memberExpr(hasObjectExpression(hasType(recordDecl(isUnion()))))
+          .bind("expr"),
+      this);
+}
+
+void ProTypeUnionAccessCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *Matched = Result.Nodes.getNodeAs<MemberExpr>("expr");
+  diag(Matched->getMemberLoc(),
+       "do not access members of unions; use (boost::)variant instead");
+}
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/cppcoreguidelines/ProTypeUnionAccessCheck.h b/clang-tidy/cppcoreguidelines/ProTypeUnionAccessCheck.h
new file mode 100644 (file)
index 0000000..fc7dd67
--- /dev/null
@@ -0,0 +1,36 @@
+//===--- ProTypeUnionAccessCheck.h - clang-tidy------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_TYPE_UNION_ACCESS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_TYPE_UNION_ACCESS_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace cppcoreguidelines {
+
+/// This check flags all access to members of unions.
+/// Access to a union as a whole (e.g. passing to a function) is not flagged.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-pro-type-union-access.html
+class ProTypeUnionAccessCheck : public ClangTidyCheck {
+public:
+  ProTypeUnionAccessCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_TYPE_UNION_ACCESS_H
diff --git a/clang-tidy/cppcoreguidelines/ProTypeVarargCheck.cpp b/clang-tidy/cppcoreguidelines/ProTypeVarargCheck.cpp
new file mode 100644 (file)
index 0000000..56227b2
--- /dev/null
@@ -0,0 +1,76 @@
+//===--- ProTypeVarargCheck.cpp - clang-tidy-------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ProTypeVarargCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace cppcoreguidelines {
+
+const internal::VariadicDynCastAllOfMatcher<Stmt, VAArgExpr> vAArgExpr;
+
+void ProTypeVarargCheck::registerMatchers(MatchFinder *Finder) {
+  if (!getLangOpts().CPlusPlus)
+    return;
+
+  Finder->addMatcher(vAArgExpr().bind("va_use"), this);
+
+  Finder->addMatcher(
+      callExpr(callee(functionDecl(isVariadic()))).bind("callvararg"), this);
+}
+
+static bool hasSingleVariadicArgumentWithValue(const CallExpr *C, uint64_t I) {
+  const auto *FDecl = dyn_cast<FunctionDecl>(C->getCalleeDecl());
+  if (!FDecl)
+    return false;
+
+  auto N = FDecl->getNumParams(); // Number of parameters without '...'
+  if (C->getNumArgs() != N + 1)
+    return false; // more/less than one argument passed to '...'
+
+  const auto *IntLit =
+      dyn_cast<IntegerLiteral>(C->getArg(N)->IgnoreParenImpCasts());
+  if (!IntLit)
+    return false;
+
+  if (IntLit->getValue() != I)
+    return false;
+
+  return true;
+}
+
+void ProTypeVarargCheck::check(const MatchFinder::MatchResult &Result) {
+  if (const auto *Matched = Result.Nodes.getNodeAs<CallExpr>("callvararg")) {
+    if (hasSingleVariadicArgumentWithValue(Matched, 0))
+      return;
+    diag(Matched->getExprLoc(), "do not call c-style vararg functions");
+  }
+
+  if (const auto *Matched = Result.Nodes.getNodeAs<Expr>("va_use")) {
+    diag(Matched->getExprLoc(),
+         "do not use va_start/va_arg to define c-style vararg functions; "
+         "use variadic templates instead");
+  }
+
+  if (const auto *Matched = Result.Nodes.getNodeAs<VarDecl>("va_list")) {
+    auto SR = Matched->getSourceRange();
+    if (SR.isInvalid())
+      return; // some implicitly generated builtins take va_list
+    diag(SR.getBegin(), "do not declare variables of type va_list; "
+                        "use variadic templates instead");
+  }
+}
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/cppcoreguidelines/ProTypeVarargCheck.h b/clang-tidy/cppcoreguidelines/ProTypeVarargCheck.h
new file mode 100644 (file)
index 0000000..558c856
--- /dev/null
@@ -0,0 +1,36 @@
+//===--- ProTypeVarargCheck.h - clang-tidy--------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_TYPE_VARARG_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_TYPE_VARARG_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace cppcoreguidelines {
+
+/// This check flags all calls to c-style variadic functions and all use
+/// of va_arg.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-pro-type-vararg.html
+class ProTypeVarargCheck : public ClangTidyCheck {
+public:
+  ProTypeVarargCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PRO_TYPE_VARARG_H
diff --git a/clang-tidy/cppcoreguidelines/SlicingCheck.cpp b/clang-tidy/cppcoreguidelines/SlicingCheck.cpp
new file mode 100644 (file)
index 0000000..53b2f72
--- /dev/null
@@ -0,0 +1,136 @@
+//===--- SlicingCheck.cpp - clang-tidy-------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "SlicingCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/RecordLayout.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace cppcoreguidelines {
+
+void SlicingCheck::registerMatchers(MatchFinder *Finder) {
+  // When we see:
+  //   class B : public A { ... };
+  //   A a;
+  //   B b;
+  //   a = b;
+  // The assignment is OK if:
+  //   - the assignment operator is defined as taking a B as second parameter,
+  //   or
+  //   - B does not define any additional members (either variables or
+  //   overrides) wrt A.
+  //
+  // The same holds for copy ctor calls. This also captures stuff like:
+  //   void f(A a);
+  //   f(b);
+
+  //  Helpers.
+  const auto OfBaseClass = ofClass(cxxRecordDecl().bind("BaseDecl"));
+  const auto IsDerivedFromBaseDecl =
+      cxxRecordDecl(isDerivedFrom(equalsBoundNode("BaseDecl")))
+          .bind("DerivedDecl");
+  const auto HasTypeDerivedFromBaseDecl =
+      anyOf(hasType(IsDerivedFromBaseDecl),
+            hasType(references(IsDerivedFromBaseDecl)));
+  const auto IsWithinDerivedCtor =
+      hasParent(cxxConstructorDecl(ofClass(equalsBoundNode("DerivedDecl"))));
+
+  // Assignement slicing: "a = b;" and "a = std::move(b);" variants.
+  const auto SlicesObjectInAssignment =
+      callExpr(callee(cxxMethodDecl(anyOf(isCopyAssignmentOperator(),
+                                          isMoveAssignmentOperator()),
+                                    OfBaseClass)),
+               hasArgument(1, HasTypeDerivedFromBaseDecl));
+
+  // Construction slicing: "A a{b};" and "f(b);" variants. Note that in case of
+  // slicing the letter will create a temporary and therefore call a ctor.
+  const auto SlicesObjectInCtor = cxxConstructExpr(
+      hasDeclaration(cxxConstructorDecl(
+          anyOf(isCopyConstructor(), isMoveConstructor()), OfBaseClass)),
+      hasArgument(0, HasTypeDerivedFromBaseDecl),
+      // We need to disable matching on the call to the base copy/move
+      // constructor in DerivedDecl's constructors.
+      unless(IsWithinDerivedCtor));
+
+  Finder->addMatcher(
+      expr(anyOf(SlicesObjectInAssignment, SlicesObjectInCtor)).bind("Call"),
+      this);
+}
+
+/// Warns on methods overridden in DerivedDecl with respect to BaseDecl.
+/// FIXME: this warns on all overrides outside of the sliced path in case of
+/// multiple inheritance.
+void SlicingCheck::DiagnoseSlicedOverriddenMethods(
+    const Expr &Call, const CXXRecordDecl &DerivedDecl,
+    const CXXRecordDecl &BaseDecl) {
+  if (DerivedDecl.getCanonicalDecl() == BaseDecl.getCanonicalDecl())
+    return;
+  for (const auto &Method : DerivedDecl.methods()) {
+    // Virtual destructors are OK. We're ignoring constructors since they are
+    // tagged as overrides.
+    if (isa<CXXConstructorDecl>(Method) || isa<CXXDestructorDecl>(Method))
+      continue;
+    if (Method->size_overridden_methods() > 0) {
+      diag(Call.getExprLoc(),
+           "slicing object from type %0 to %1 discards override %2")
+          << &DerivedDecl << &BaseDecl << Method;
+    }
+  }
+  // Recursively process bases.
+  for (const auto &Base : DerivedDecl.bases()) {
+    if (const auto *BaseRecordType = Base.getType()->getAs<RecordType>()) {
+      if (const auto *BaseRecord = cast_or_null<CXXRecordDecl>(
+              BaseRecordType->getDecl()->getDefinition()))
+        DiagnoseSlicedOverriddenMethods(Call, *BaseRecord, BaseDecl);
+    }
+  }
+}
+
+void SlicingCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *BaseDecl = Result.Nodes.getNodeAs<CXXRecordDecl>("BaseDecl");
+  const auto *DerivedDecl =
+      Result.Nodes.getNodeAs<CXXRecordDecl>("DerivedDecl");
+  const auto *Call = Result.Nodes.getNodeAs<Expr>("Call");
+  assert(BaseDecl != nullptr);
+  assert(DerivedDecl != nullptr);
+  assert(Call != nullptr);
+
+  // Warn when slicing the vtable.
+  // We're looking through all the methods in the derived class and see if they
+  // override some methods in the base class.
+  // It's not enough to just test whether the class is polymorphic because we
+  // would be fine slicing B to A if no method in B (or its bases) overrides
+  // anything in A:
+  //   class A { virtual void f(); };
+  //   class B : public A {};
+  // because in that case calling A::f is the same as calling B::f.
+  DiagnoseSlicedOverriddenMethods(*Call, *DerivedDecl, *BaseDecl);
+
+  // Warn when slicing member variables.
+  const auto &BaseLayout =
+      BaseDecl->getASTContext().getASTRecordLayout(BaseDecl);
+  const auto &DerivedLayout =
+      DerivedDecl->getASTContext().getASTRecordLayout(DerivedDecl);
+  const CharUnits StateSize =
+      DerivedLayout.getDataSize() - BaseLayout.getDataSize();
+  if (StateSize.isPositive()) {
+    diag(Call->getExprLoc(), "slicing object from type %0 to %1 discards "
+                             "%2 bytes of state")
+        << DerivedDecl << BaseDecl << static_cast<int>(StateSize.getQuantity());
+  }
+}
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/cppcoreguidelines/SlicingCheck.h b/clang-tidy/cppcoreguidelines/SlicingCheck.h
new file mode 100644 (file)
index 0000000..9ee91bc
--- /dev/null
@@ -0,0 +1,45 @@
+//===--- SlicingCheck.h - clang-tidy-----------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_SLICING_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_SLICING_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace cppcoreguidelines {
+
+/// Flags slicing (incomplete copying of an object's state) of member variables
+/// or vtable. See:
+///   - https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#es63-dont-slice
+///     for the former, and
+///   - https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#c145-access-polymorphic-objects-through-pointers-and-references
+///     for the latter
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-slicing.html
+class SlicingCheck : public ClangTidyCheck {
+public:
+  SlicingCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+  void DiagnoseSlicedOverriddenMethods(const Expr &call,
+                                       const CXXRecordDecl &DerivedDecl,
+                                       const CXXRecordDecl &BaseDecl);
+};
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_SLICING_H
diff --git a/clang-tidy/cppcoreguidelines/SpecialMemberFunctionsCheck.cpp b/clang-tidy/cppcoreguidelines/SpecialMemberFunctionsCheck.cpp
new file mode 100644 (file)
index 0000000..df3c279
--- /dev/null
@@ -0,0 +1,191 @@
+//===--- SpecialMemberFunctionsCheck.cpp - clang-tidy----------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "SpecialMemberFunctionsCheck.h"
+
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "llvm/ADT/DenseMapInfo.h"
+#include "llvm/ADT/StringExtras.h"
+
+#define DEBUG_TYPE "clang-tidy"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace cppcoreguidelines {
+
+SpecialMemberFunctionsCheck::SpecialMemberFunctionsCheck(
+    StringRef Name, ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      AllowMissingMoveFunctions(Options.get("AllowMissingMoveFunctions", 0)),
+      AllowSoleDefaultDtor(Options.get("AllowSoleDefaultDtor", 0)) {}
+
+void SpecialMemberFunctionsCheck::storeOptions(
+    ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "AllowMissingMoveFunctions", AllowMissingMoveFunctions);
+  Options.store(Opts, "AllowSoleDefaultDtor", AllowSoleDefaultDtor);
+}
+
+void SpecialMemberFunctionsCheck::registerMatchers(MatchFinder *Finder) {
+  if (!getLangOpts().CPlusPlus)
+    return;
+  Finder->addMatcher(
+      cxxRecordDecl(
+          eachOf(
+              has(cxxDestructorDecl(unless(isImplicit())).bind("dtor")),
+              has(cxxConstructorDecl(isCopyConstructor(), unless(isImplicit()))
+                      .bind("copy-ctor")),
+              has(cxxMethodDecl(isCopyAssignmentOperator(),
+                                unless(isImplicit()))
+                      .bind("copy-assign")),
+              has(cxxConstructorDecl(isMoveConstructor(), unless(isImplicit()))
+                      .bind("move-ctor")),
+              has(cxxMethodDecl(isMoveAssignmentOperator(),
+                                unless(isImplicit()))
+                      .bind("move-assign"))))
+          .bind("class-def"),
+      this);
+}
+
+static llvm::StringRef
+toString(SpecialMemberFunctionsCheck::SpecialMemberFunctionKind K) {
+  switch (K) {
+  case SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::Destructor:
+    return "a destructor";
+  case SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::
+      DefaultDestructor:
+    return "a default destructor";
+  case SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::
+      NonDefaultDestructor:
+    return "a non-default destructor";
+  case SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::CopyConstructor:
+    return "a copy constructor";
+  case SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::CopyAssignment:
+    return "a copy assignment operator";
+  case SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::MoveConstructor:
+    return "a move constructor";
+  case SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::MoveAssignment:
+    return "a move assignment operator";
+  }
+  llvm_unreachable("Unhandled SpecialMemberFunctionKind");
+}
+
+static std::string
+join(ArrayRef<SpecialMemberFunctionsCheck::SpecialMemberFunctionKind> SMFS,
+     llvm::StringRef AndOr) {
+
+  assert(!SMFS.empty() &&
+         "List of defined or undefined members should never be empty.");
+  std::string Buffer;
+  llvm::raw_string_ostream Stream(Buffer);
+
+  Stream << toString(SMFS[0]);
+  size_t LastIndex = SMFS.size() - 1;
+  for (size_t i = 1; i < LastIndex; ++i) {
+    Stream << ", " << toString(SMFS[i]);
+  }
+  if (LastIndex != 0) {
+    Stream << AndOr << toString(SMFS[LastIndex]);
+  }
+  return Stream.str();
+}
+
+void SpecialMemberFunctionsCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  const auto *MatchedDecl = Result.Nodes.getNodeAs<CXXRecordDecl>("class-def");
+  if (!MatchedDecl)
+    return;
+
+  ClassDefId ID(MatchedDecl->getLocation(), MatchedDecl->getName());
+
+  auto StoreMember = [this, &ID](SpecialMemberFunctionKind Kind) {
+    llvm::SmallVectorImpl<SpecialMemberFunctionKind> &Members =
+        ClassWithSpecialMembers[ID];
+    if (!llvm::is_contained(Members, Kind))
+      Members.push_back(Kind);
+  };
+
+  if (const auto *Dtor = Result.Nodes.getNodeAs<CXXMethodDecl>("dtor")) {
+    StoreMember(Dtor->isDefaulted()
+                    ? SpecialMemberFunctionKind::DefaultDestructor
+                    : SpecialMemberFunctionKind::NonDefaultDestructor);
+  }
+
+  std::initializer_list<std::pair<std::string, SpecialMemberFunctionKind>>
+      Matchers = {{"copy-ctor", SpecialMemberFunctionKind::CopyConstructor},
+                  {"copy-assign", SpecialMemberFunctionKind::CopyAssignment},
+                  {"move-ctor", SpecialMemberFunctionKind::MoveConstructor},
+                  {"move-assign", SpecialMemberFunctionKind::MoveAssignment}};
+
+  for (const auto &KV : Matchers)
+    if (Result.Nodes.getNodeAs<CXXMethodDecl>(KV.first)) {
+      StoreMember(KV.second);
+    }
+}
+
+void SpecialMemberFunctionsCheck::onEndOfTranslationUnit() {
+  for (const auto &C : ClassWithSpecialMembers) {
+    checkForMissingMembers(C.first, C.second);
+  }
+}
+
+void SpecialMemberFunctionsCheck::checkForMissingMembers(
+    const ClassDefId &ID,
+    llvm::ArrayRef<SpecialMemberFunctionKind> DefinedMembers) {
+  llvm::SmallVector<SpecialMemberFunctionKind, 5> MissingMembers;
+
+  auto HasMember = [&](SpecialMemberFunctionKind Kind) {
+    return llvm::is_contained(DefinedMembers, Kind);
+  };
+
+  auto RequireMember = [&](SpecialMemberFunctionKind Kind) {
+    if (!HasMember(Kind))
+      MissingMembers.push_back(Kind);
+  };
+
+  bool RequireThree =
+      HasMember(SpecialMemberFunctionKind::NonDefaultDestructor) ||
+      (!AllowSoleDefaultDtor &&
+       HasMember(SpecialMemberFunctionKind::DefaultDestructor)) ||
+      HasMember(SpecialMemberFunctionKind::CopyConstructor) ||
+      HasMember(SpecialMemberFunctionKind::CopyAssignment) ||
+      HasMember(SpecialMemberFunctionKind::MoveConstructor) ||
+      HasMember(SpecialMemberFunctionKind::MoveAssignment);
+
+  bool RequireFive = (!AllowMissingMoveFunctions && RequireThree &&
+                      getLangOpts().CPlusPlus11) ||
+                     HasMember(SpecialMemberFunctionKind::MoveConstructor) ||
+                     HasMember(SpecialMemberFunctionKind::MoveAssignment);
+
+  if (RequireThree) {
+    if (!HasMember(SpecialMemberFunctionKind::DefaultDestructor) &&
+        !HasMember(SpecialMemberFunctionKind::NonDefaultDestructor))
+      MissingMembers.push_back(SpecialMemberFunctionKind::Destructor);
+
+    RequireMember(SpecialMemberFunctionKind::CopyConstructor);
+    RequireMember(SpecialMemberFunctionKind::CopyAssignment);
+  }
+
+  if (RequireFive) {
+    assert(RequireThree);
+    RequireMember(SpecialMemberFunctionKind::MoveConstructor);
+    RequireMember(SpecialMemberFunctionKind::MoveAssignment);
+  }
+
+  if (!MissingMembers.empty())
+    diag(ID.first, "class '%0' defines %1 but does not define %2")
+        << ID.second << cppcoreguidelines::join(DefinedMembers, " and ")
+        << cppcoreguidelines::join(MissingMembers, " or ");
+}
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/cppcoreguidelines/SpecialMemberFunctionsCheck.h b/clang-tidy/cppcoreguidelines/SpecialMemberFunctionsCheck.h
new file mode 100644 (file)
index 0000000..8ab0c92
--- /dev/null
@@ -0,0 +1,105 @@
+//===--- SpecialMemberFunctionsCheck.h - clang-tidy--------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_SPECIAL_MEMBER_FUNCTIONS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_SPECIAL_MEMBER_FUNCTIONS_H
+
+#include "../ClangTidy.h"
+
+#include "llvm/ADT/DenseMapInfo.h"
+
+namespace clang {
+namespace tidy {
+namespace cppcoreguidelines {
+
+/// Checks for classes where some, but not all, of the special member functions
+/// are defined.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-special-member-functions.html
+class SpecialMemberFunctionsCheck : public ClangTidyCheck {
+public:
+  SpecialMemberFunctionsCheck(StringRef Name, ClangTidyContext *Context);
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+  void onEndOfTranslationUnit() override;
+
+  enum class SpecialMemberFunctionKind : uint8_t {
+    Destructor,
+    DefaultDestructor,
+    NonDefaultDestructor,
+    CopyConstructor,
+    CopyAssignment,
+    MoveConstructor,
+    MoveAssignment
+  };
+
+  using ClassDefId = std::pair<SourceLocation, std::string>;
+
+  using ClassDefiningSpecialMembersMap =
+      llvm::DenseMap<ClassDefId,
+                     llvm::SmallVector<SpecialMemberFunctionKind, 5>>;
+
+private:
+  void checkForMissingMembers(
+      const ClassDefId &ID,
+      llvm::ArrayRef<SpecialMemberFunctionKind> DefinedSpecialMembers);
+
+  const bool AllowMissingMoveFunctions;
+  const bool AllowSoleDefaultDtor;
+  ClassDefiningSpecialMembersMap ClassWithSpecialMembers;
+};
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
+
+namespace llvm {
+/// Specialisation of DenseMapInfo to allow ClassDefId objects in DenseMaps
+/// FIXME: Move this to the corresponding cpp file as is done for
+/// clang-tidy/readability/IdentifierNamingCheck.cpp.
+template <>
+struct DenseMapInfo<
+    clang::tidy::cppcoreguidelines::SpecialMemberFunctionsCheck::ClassDefId> {
+  using ClassDefId =
+      clang::tidy::cppcoreguidelines::SpecialMemberFunctionsCheck::ClassDefId;
+
+  static inline ClassDefId getEmptyKey() {
+    return ClassDefId(
+        clang::SourceLocation::getFromRawEncoding(static_cast<unsigned>(-1)),
+        "EMPTY");
+  }
+
+  static inline ClassDefId getTombstoneKey() {
+    return ClassDefId(
+        clang::SourceLocation::getFromRawEncoding(static_cast<unsigned>(-2)),
+        "TOMBSTONE");
+  }
+
+  static unsigned getHashValue(ClassDefId Val) {
+    assert(Val != getEmptyKey() && "Cannot hash the empty key!");
+    assert(Val != getTombstoneKey() && "Cannot hash the tombstone key!");
+
+    std::hash<ClassDefId::second_type> SecondHash;
+    return Val.first.getRawEncoding() + SecondHash(Val.second);
+  }
+
+  static bool isEqual(const ClassDefId &LHS, const ClassDefId &RHS) {
+    if (RHS == getEmptyKey())
+      return LHS == getEmptyKey();
+    if (RHS == getTombstoneKey())
+      return LHS == getTombstoneKey();
+    return LHS == RHS;
+  }
+};
+
+} // namespace llvm
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_SPECIAL_MEMBER_FUNCTIONS_H
diff --git a/clang-tidy/google/AvoidCStyleCastsCheck.cpp b/clang-tidy/google/AvoidCStyleCastsCheck.cpp
new file mode 100644 (file)
index 0000000..80cc475
--- /dev/null
@@ -0,0 +1,220 @@
+//===--- AvoidCStyleCastsCheck.cpp - clang-tidy -----------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "AvoidCStyleCastsCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace google {
+namespace readability {
+
+void AvoidCStyleCastsCheck::registerMatchers(
+    ast_matchers::MatchFinder *Finder) {
+  Finder->addMatcher(
+      cStyleCastExpr(
+          // Filter out (EnumType)IntegerLiteral construct, which is generated
+          // for non-type template arguments of enum types.
+          // FIXME: Remove this once this is fixed in the AST.
+          unless(hasParent(substNonTypeTemplateParmExpr())),
+          // Avoid matches in template instantiations.
+          unless(isInTemplateInstantiation()))
+          .bind("cast"),
+      this);
+}
+
+static bool needsConstCast(QualType SourceType, QualType DestType) {
+  while ((SourceType->isPointerType() && DestType->isPointerType()) ||
+         (SourceType->isReferenceType() && DestType->isReferenceType())) {
+    SourceType = SourceType->getPointeeType();
+    DestType = DestType->getPointeeType();
+    if (SourceType.isConstQualified() && !DestType.isConstQualified()) {
+      return (SourceType->isPointerType() == DestType->isPointerType()) &&
+             (SourceType->isReferenceType() == DestType->isReferenceType());
+    }
+  }
+  return false;
+}
+
+static bool pointedUnqualifiedTypesAreEqual(QualType T1, QualType T2) {
+  while ((T1->isPointerType() && T2->isPointerType()) ||
+         (T1->isReferenceType() && T2->isReferenceType())) {
+    T1 = T1->getPointeeType();
+    T2 = T2->getPointeeType();
+  }
+  return T1.getUnqualifiedType() == T2.getUnqualifiedType();
+}
+
+void AvoidCStyleCastsCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *CastExpr = Result.Nodes.getNodeAs<CStyleCastExpr>("cast");
+
+  // Ignore casts in macros.
+  if (CastExpr->getExprLoc().isMacroID())
+    return;
+
+  // Casting to void is an idiomatic way to mute "unused variable" and similar
+  // warnings.
+  if (CastExpr->getCastKind() == CK_ToVoid)
+    return;
+
+  auto isFunction = [](QualType T) {
+    T = T.getCanonicalType().getNonReferenceType();
+    return T->isFunctionType() || T->isFunctionPointerType() ||
+           T->isMemberFunctionPointerType();
+  };
+
+  const QualType DestTypeAsWritten =
+      CastExpr->getTypeAsWritten().getUnqualifiedType();
+  const QualType SourceTypeAsWritten =
+      CastExpr->getSubExprAsWritten()->getType().getUnqualifiedType();
+  const QualType SourceType = SourceTypeAsWritten.getCanonicalType();
+  const QualType DestType = DestTypeAsWritten.getCanonicalType();
+
+  auto ReplaceRange = CharSourceRange::getCharRange(
+      CastExpr->getLParenLoc(), CastExpr->getSubExprAsWritten()->getLocStart());
+
+  bool FnToFnCast =
+      isFunction(SourceTypeAsWritten) && isFunction(DestTypeAsWritten);
+
+  if (CastExpr->getCastKind() == CK_NoOp && !FnToFnCast) {
+    // Function pointer/reference casts may be needed to resolve ambiguities in
+    // case of overloaded functions, so detection of redundant casts is trickier
+    // in this case. Don't emit "redundant cast" warnings for function
+    // pointer/reference types.
+    if (SourceTypeAsWritten == DestTypeAsWritten) {
+      diag(CastExpr->getLocStart(), "redundant cast to the same type")
+          << FixItHint::CreateRemoval(ReplaceRange);
+      return;
+    }
+  }
+
+  // The rest of this check is only relevant to C++.
+  if (!getLangOpts().CPlusPlus)
+    return;
+  // Ignore code inside extern "C" {} blocks.
+  if (!match(expr(hasAncestor(linkageSpecDecl())), *CastExpr, *Result.Context)
+           .empty())
+    return;
+  // Ignore code in .c files and headers included from them, even if they are
+  // compiled as C++.
+  if (getCurrentMainFile().endswith(".c"))
+    return;
+
+  SourceManager &SM = *Result.SourceManager;
+
+  // Ignore code in .c files #included in other files (which shouldn't be done,
+  // but people still do this for test and other purposes).
+  if (SM.getFilename(SM.getSpellingLoc(CastExpr->getLocStart())).endswith(".c"))
+    return;
+
+  // Leave type spelling exactly as it was (unlike
+  // getTypeAsWritten().getAsString() which would spell enum types 'enum X').
+  StringRef DestTypeString =
+      Lexer::getSourceText(CharSourceRange::getTokenRange(
+                               CastExpr->getLParenLoc().getLocWithOffset(1),
+                               CastExpr->getRParenLoc().getLocWithOffset(-1)),
+                           SM, getLangOpts());
+
+  auto Diag =
+      diag(CastExpr->getLocStart(), "C-style casts are discouraged; use %0");
+
+  auto ReplaceWithCast = [&](std::string CastText) {
+    const Expr *SubExpr = CastExpr->getSubExprAsWritten()->IgnoreImpCasts();
+    if (!isa<ParenExpr>(SubExpr)) {
+      CastText.push_back('(');
+      Diag << FixItHint::CreateInsertion(
+          Lexer::getLocForEndOfToken(SubExpr->getLocEnd(), 0, SM,
+                                     getLangOpts()),
+          ")");
+    }
+    Diag << FixItHint::CreateReplacement(ReplaceRange, CastText);
+  };
+  auto ReplaceWithNamedCast = [&](StringRef CastType) {
+    Diag << CastType;
+    ReplaceWithCast((CastType + "<" + DestTypeString + ">").str());
+  };
+
+  // Suggest appropriate C++ cast. See [expr.cast] for cast notation semantics.
+  switch (CastExpr->getCastKind()) {
+  case CK_FunctionToPointerDecay:
+    ReplaceWithNamedCast("static_cast");
+    return;
+  case CK_ConstructorConversion:
+    if (!CastExpr->getTypeAsWritten().hasQualifiers() &&
+        DestTypeAsWritten->isRecordType() &&
+        !DestTypeAsWritten->isElaboratedTypeSpecifier()) {
+      Diag << "constructor call syntax";
+      // FIXME: Validate DestTypeString, maybe.
+      ReplaceWithCast(DestTypeString.str());
+    } else {
+      ReplaceWithNamedCast("static_cast");
+    }
+    return;
+  case CK_NoOp:
+    if (FnToFnCast) {
+      ReplaceWithNamedCast("static_cast");
+      return;
+    }
+    if (SourceType == DestType) {
+      Diag << "static_cast (if needed, the cast may be redundant)";
+      ReplaceWithCast(("static_cast<" + DestTypeString + ">").str());
+      return;
+    }
+    if (needsConstCast(SourceType, DestType) &&
+        pointedUnqualifiedTypesAreEqual(SourceType, DestType)) {
+      ReplaceWithNamedCast("const_cast");
+      return;
+    }
+    if (DestType->isReferenceType()) {
+      QualType Dest = DestType.getNonReferenceType();
+      QualType Source = SourceType.getNonReferenceType();
+      if (Source == Dest.withConst() ||
+          SourceType.getNonReferenceType() == DestType.getNonReferenceType()) {
+        ReplaceWithNamedCast("const_cast");
+        return;
+      }
+      break;
+    }
+  // FALLTHROUGH
+  case clang::CK_IntegralCast:
+    // Convert integral and no-op casts between builtin types and enums to
+    // static_cast. A cast from enum to integer may be unnecessary, but it's
+    // still retained.
+    if ((SourceType->isBuiltinType() || SourceType->isEnumeralType()) &&
+        (DestType->isBuiltinType() || DestType->isEnumeralType())) {
+      ReplaceWithNamedCast("static_cast");
+      return;
+    }
+    break;
+  case CK_BitCast:
+    // FIXME: Suggest const_cast<...>(reinterpret_cast<...>(...)) replacement.
+    if (!needsConstCast(SourceType, DestType)) {
+      if (SourceType->isVoidPointerType())
+        ReplaceWithNamedCast("static_cast");
+      else
+        ReplaceWithNamedCast("reinterpret_cast");
+      return;
+    }
+    break;
+  default:
+    break;
+  }
+
+  Diag << "static_cast/const_cast/reinterpret_cast";
+}
+
+} // namespace readability
+} // namespace google
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/google/AvoidCStyleCastsCheck.h b/clang-tidy/google/AvoidCStyleCastsCheck.h
new file mode 100644 (file)
index 0000000..ea7e34c
--- /dev/null
@@ -0,0 +1,42 @@
+//===--- AvoidCStyleCastsCheck.h - clang-tidy -------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_AVOIDCSTYLECASTSCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_AVOIDCSTYLECASTSCHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace google {
+namespace readability {
+
+/// Finds usages of C-style casts.
+///
+/// https://google.github.io/styleguide/cppguide.html#Casting
+///
+/// Corresponding cpplint.py check name: 'readability/casting'.
+///
+/// This check is similar to `-Wold-style-cast`, but it suggests automated fixes
+/// in some cases. The reported locations should not be different from the
+/// ones generated by `-Wold-style-cast`.
+class AvoidCStyleCastsCheck : public ClangTidyCheck {
+public:
+  AvoidCStyleCastsCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace readability
+} // namespace google
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_AVOIDCSTYLECASTSCHECK_H
diff --git a/clang-tidy/google/CMakeLists.txt b/clang-tidy/google/CMakeLists.txt
new file mode 100644 (file)
index 0000000..ff48b63
--- /dev/null
@@ -0,0 +1,26 @@
+set(LLVM_LINK_COMPONENTS support)
+
+add_clang_library(clangTidyGoogleModule
+  AvoidCStyleCastsCheck.cpp
+  DefaultArgumentsCheck.cpp
+  ExplicitConstructorCheck.cpp
+  ExplicitMakePairCheck.cpp
+  GlobalNamesInHeadersCheck.cpp
+  GoogleTidyModule.cpp
+  IntegerTypesCheck.cpp
+  NonConstReferences.cpp
+  OverloadedUnaryAndCheck.cpp
+  StringReferenceMemberCheck.cpp
+  TodoCommentCheck.cpp
+  UnnamedNamespaceInHeaderCheck.cpp
+  UsingNamespaceDirectiveCheck.cpp
+
+  LINK_LIBS
+  clangAST
+  clangASTMatchers
+  clangBasic
+  clangLex
+  clangTidy
+  clangTidyReadabilityModule
+  clangTidyUtils
+  )
diff --git a/clang-tidy/google/DefaultArgumentsCheck.cpp b/clang-tidy/google/DefaultArgumentsCheck.cpp
new file mode 100644 (file)
index 0000000..ccbd870
--- /dev/null
@@ -0,0 +1,36 @@
+//===--- DefaultArgumentsCheck.cpp - clang-tidy----------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "DefaultArgumentsCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace google {
+
+void DefaultArgumentsCheck::registerMatchers(MatchFinder *Finder) {
+  Finder->addMatcher(
+      cxxMethodDecl(anyOf(isOverride(), isVirtual()),
+                    hasAnyParameter(parmVarDecl(hasInitializer(expr()))))
+          .bind("Decl"),
+      this);
+}
+
+void DefaultArgumentsCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *MatchedDecl = Result.Nodes.getNodeAs<CXXMethodDecl>("Decl");
+  diag(MatchedDecl->getLocation(),
+       "default arguments on virtual or override methods are prohibited");
+}
+
+} // namespace google
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/google/DefaultArgumentsCheck.h b/clang-tidy/google/DefaultArgumentsCheck.h
new file mode 100644 (file)
index 0000000..1457a09
--- /dev/null
@@ -0,0 +1,34 @@
+//===--- DefaultArgumentsCheck.h - clang-tidy--------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_DEFAULT_ARGUMENTS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_DEFAULT_ARGUMENTS_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace google {
+
+/// Checks that default parameters are not given for virtual methods.
+///
+/// See https://google.github.io/styleguide/cppguide.html#Default_Arguments
+class DefaultArgumentsCheck : public ClangTidyCheck {
+public:
+  DefaultArgumentsCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace google
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_DEFAULT_ARGUMENTS_H
diff --git a/clang-tidy/google/ExplicitConstructorCheck.cpp b/clang-tidy/google/ExplicitConstructorCheck.cpp
new file mode 100644 (file)
index 0000000..a03fb56
--- /dev/null
@@ -0,0 +1,156 @@
+//===--- ExplicitConstructorCheck.cpp - clang-tidy ------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ExplicitConstructorCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace google {
+
+void ExplicitConstructorCheck::registerMatchers(MatchFinder *Finder) {
+  // Only register the matchers for C++; the functionality currently does not
+  // provide any benefit to other languages, despite being benign.
+  if (!getLangOpts().CPlusPlus)
+    return;
+  Finder->addMatcher(
+      cxxConstructorDecl(unless(anyOf(isImplicit(), // Compiler-generated.
+                                      isDeleted(), isInstantiated())))
+          .bind("ctor"),
+      this);
+  Finder->addMatcher(
+      cxxConversionDecl(unless(anyOf(isExplicit(), // Already marked explicit.
+                                     isImplicit(), // Compiler-generated.
+                                     isDeleted(), isInstantiated())))
+
+          .bind("conversion"),
+      this);
+}
+
+// Looks for the token matching the predicate and returns the range of the found
+// token including trailing whitespace.
+static SourceRange FindToken(const SourceManager &Sources,
+                             const LangOptions &LangOpts,
+                             SourceLocation StartLoc, SourceLocation EndLoc,
+                             bool (*Pred)(const Token &)) {
+  if (StartLoc.isMacroID() || EndLoc.isMacroID())
+    return SourceRange();
+  FileID File = Sources.getFileID(Sources.getSpellingLoc(StartLoc));
+  StringRef Buf = Sources.getBufferData(File);
+  const char *StartChar = Sources.getCharacterData(StartLoc);
+  Lexer Lex(StartLoc, LangOpts, StartChar, StartChar, Buf.end());
+  Lex.SetCommentRetentionState(true);
+  Token Tok;
+  do {
+    Lex.LexFromRawLexer(Tok);
+    if (Pred(Tok)) {
+      Token NextTok;
+      Lex.LexFromRawLexer(NextTok);
+      return SourceRange(Tok.getLocation(), NextTok.getLocation());
+    }
+  } while (Tok.isNot(tok::eof) && Tok.getLocation() < EndLoc);
+
+  return SourceRange();
+}
+
+static bool declIsStdInitializerList(const NamedDecl *D) {
+  // First use the fast getName() method to avoid unnecessary calls to the
+  // slow getQualifiedNameAsString().
+  return D->getName() == "initializer_list" &&
+         D->getQualifiedNameAsString() == "std::initializer_list";
+}
+
+static bool isStdInitializerList(QualType Type) {
+  Type = Type.getCanonicalType();
+  if (const auto *TS = Type->getAs<TemplateSpecializationType>()) {
+    if (const TemplateDecl *TD = TS->getTemplateName().getAsTemplateDecl())
+      return declIsStdInitializerList(TD);
+  }
+  if (const auto *RT = Type->getAs<RecordType>()) {
+    if (const auto *Specialization =
+            dyn_cast<ClassTemplateSpecializationDecl>(RT->getDecl()))
+      return declIsStdInitializerList(Specialization->getSpecializedTemplate());
+  }
+  return false;
+}
+
+void ExplicitConstructorCheck::check(const MatchFinder::MatchResult &Result) {
+  constexpr char WarningMessage[] =
+      "%0 must be marked explicit to avoid unintentional implicit conversions";
+
+  if (const auto *Conversion =
+      Result.Nodes.getNodeAs<CXXConversionDecl>("conversion")) {
+    if (Conversion->isOutOfLine())
+      return;
+    SourceLocation Loc = Conversion->getLocation();
+    // Ignore all macros until we learn to ignore specific ones (e.g. used in
+    // gmock to define matchers).
+    if (Loc.isMacroID())
+      return;
+    diag(Loc, WarningMessage)
+        << Conversion << FixItHint::CreateInsertion(Loc, "explicit ");
+    return;
+  }
+
+  const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructorDecl>("ctor");
+  if (Ctor->isOutOfLine() || Ctor->getNumParams() == 0 ||
+      Ctor->getMinRequiredArguments() > 1)
+    return;
+
+  bool takesInitializerList = isStdInitializerList(
+      Ctor->getParamDecl(0)->getType().getNonReferenceType());
+  if (Ctor->isExplicit() &&
+      (Ctor->isCopyOrMoveConstructor() || takesInitializerList)) {
+    auto isKWExplicit = [](const Token &Tok) {
+      return Tok.is(tok::raw_identifier) &&
+             Tok.getRawIdentifier() == "explicit";
+    };
+    SourceRange ExplicitTokenRange =
+        FindToken(*Result.SourceManager, getLangOpts(),
+                  Ctor->getOuterLocStart(), Ctor->getLocEnd(), isKWExplicit);
+    StringRef ConstructorDescription;
+    if (Ctor->isMoveConstructor())
+      ConstructorDescription = "move";
+    else if (Ctor->isCopyConstructor())
+      ConstructorDescription = "copy";
+    else
+      ConstructorDescription = "initializer-list";
+
+    auto Diag = diag(Ctor->getLocation(),
+                     "%0 constructor should not be declared explicit")
+                << ConstructorDescription;
+    if (ExplicitTokenRange.isValid()) {
+      Diag << FixItHint::CreateRemoval(
+          CharSourceRange::getCharRange(ExplicitTokenRange));
+    }
+    return;
+  }
+
+  if (Ctor->isExplicit() || Ctor->isCopyOrMoveConstructor() ||
+      takesInitializerList)
+    return;
+
+  bool SingleArgument =
+      Ctor->getNumParams() == 1 && !Ctor->getParamDecl(0)->isParameterPack();
+  SourceLocation Loc = Ctor->getLocation();
+  diag(Loc, WarningMessage)
+      << (SingleArgument
+              ? "single-argument constructors"
+              : "constructors that are callable with a single argument")
+      << FixItHint::CreateInsertion(Loc, "explicit ");
+}
+
+} // namespace google
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/google/ExplicitConstructorCheck.h b/clang-tidy/google/ExplicitConstructorCheck.h
new file mode 100644 (file)
index 0000000..81e6679
--- /dev/null
@@ -0,0 +1,34 @@
+//===--- ExplicitConstructorCheck.h - clang-tidy ----------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_EXPLICITCONSTRUCTORCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_EXPLICITCONSTRUCTORCHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace google {
+
+/// Checks that all single-argument constructors are explicit.
+///
+/// See https://google.github.io/styleguide/cppguide.html#Explicit_Constructors
+class ExplicitConstructorCheck : public ClangTidyCheck {
+public:
+  ExplicitConstructorCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace google
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_EXPLICITCONSTRUCTORCHECK_H
diff --git a/clang-tidy/google/ExplicitMakePairCheck.cpp b/clang-tidy/google/ExplicitMakePairCheck.cpp
new file mode 100644 (file)
index 0000000..6c2f0a3
--- /dev/null
@@ -0,0 +1,78 @@
+//===--- ExplicitMakePairCheck.cpp - clang-tidy -----------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ExplicitMakePairCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace {
+AST_MATCHER(DeclRefExpr, hasExplicitTemplateArgs) {
+  return Node.hasExplicitTemplateArgs();
+}
+} // namespace
+
+namespace tidy {
+namespace google {
+namespace build {
+
+void ExplicitMakePairCheck::registerMatchers(
+    ast_matchers::MatchFinder *Finder) {
+  // Only register the matchers for C++; the functionality currently does not
+  // provide any benefit to other languages, despite being benign.
+  if (!getLangOpts().CPlusPlus)
+    return;
+
+  // Look for std::make_pair with explicit template args. Ignore calls in
+  // templates.
+  Finder->addMatcher(
+      callExpr(unless(isInTemplateInstantiation()),
+               callee(expr(ignoringParenImpCasts(
+                   declRefExpr(hasExplicitTemplateArgs(),
+                               to(functionDecl(hasName("::std::make_pair"))))
+                       .bind("declref")))))
+          .bind("call"),
+      this);
+}
+
+void ExplicitMakePairCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *Call = Result.Nodes.getNodeAs<CallExpr>("call");
+  const auto *DeclRef = Result.Nodes.getNodeAs<DeclRefExpr>("declref");
+
+  // Sanity check: The use might have overriden ::std::make_pair.
+  if (Call->getNumArgs() != 2)
+    return;
+
+  const Expr *Arg0 = Call->getArg(0)->IgnoreParenImpCasts();
+  const Expr *Arg1 = Call->getArg(1)->IgnoreParenImpCasts();
+
+  // If types don't match, we suggest replacing with std::pair and explicit
+  // template arguments. Otherwise just remove the template arguments from
+  // make_pair.
+  if (Arg0->getType() != Call->getArg(0)->getType() ||
+      Arg1->getType() != Call->getArg(1)->getType()) {
+    diag(Call->getLocStart(), "for C++11-compatibility, use pair directly")
+        << FixItHint::CreateReplacement(
+               SourceRange(DeclRef->getLocStart(), DeclRef->getLAngleLoc()),
+               "std::pair<");
+  } else {
+    diag(Call->getLocStart(),
+         "for C++11-compatibility, omit template arguments from make_pair")
+        << FixItHint::CreateRemoval(
+               SourceRange(DeclRef->getLAngleLoc(), DeclRef->getRAngleLoc()));
+  }
+}
+
+} // namespace build
+} // namespace google
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/google/ExplicitMakePairCheck.h b/clang-tidy/google/ExplicitMakePairCheck.h
new file mode 100644 (file)
index 0000000..a29825f
--- /dev/null
@@ -0,0 +1,39 @@
+//===--- ExplicitMakePairCheck.h - clang-tidy -------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_EXPLICITMAKEPAIRCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_EXPLICITMAKEPAIRCHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace google {
+namespace build {
+
+/// Check that `make_pair`'s template arguments are deduced.
+///
+/// G++ 4.6 in C++11 mode fails badly if `make_pair`'s template arguments are
+/// specified explicitly, and such use isn't intended in any case.
+///
+/// Corresponding cpplint.py check name: 'build/explicit_make_pair'.
+class ExplicitMakePairCheck : public ClangTidyCheck {
+public:
+  ExplicitMakePairCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace build
+} // namespace google
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_EXPLICITMAKEPAIRCHECK_H
diff --git a/clang-tidy/google/GlobalNamesInHeadersCheck.cpp b/clang-tidy/google/GlobalNamesInHeadersCheck.cpp
new file mode 100644 (file)
index 0000000..71049d9
--- /dev/null
@@ -0,0 +1,80 @@
+//===--- GlobalNamesInHeadersCheck.cpp - clang-tidy -----------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "GlobalNamesInHeadersCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace google {
+namespace readability {
+
+GlobalNamesInHeadersCheck::GlobalNamesInHeadersCheck(StringRef Name,
+                                                     ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      RawStringHeaderFileExtensions(
+          Options.getLocalOrGlobal("HeaderFileExtensions", "h")) {
+  if (!utils::parseHeaderFileExtensions(RawStringHeaderFileExtensions,
+                                        HeaderFileExtensions, ',')) {
+    llvm::errs() << "Invalid header file extension: "
+                 << RawStringHeaderFileExtensions << "\n";
+  }
+}
+
+void GlobalNamesInHeadersCheck::storeOptions(
+    ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "HeaderFileExtensions", RawStringHeaderFileExtensions);
+}
+
+void GlobalNamesInHeadersCheck::registerMatchers(
+    ast_matchers::MatchFinder *Finder) {
+  Finder->addMatcher(decl(anyOf(usingDecl(), usingDirectiveDecl()),
+                          hasDeclContext(translationUnitDecl()))
+                         .bind("using_decl"),
+                     this);
+}
+
+void GlobalNamesInHeadersCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *D = Result.Nodes.getNodeAs<Decl>("using_decl");
+  // If it comes from a macro, we'll assume it is fine.
+  if (D->getLocStart().isMacroID())
+    return;
+
+  // Ignore if it comes from the "main" file ...
+  if (Result.SourceManager->isInMainFile(
+          Result.SourceManager->getExpansionLoc(D->getLocStart()))) {
+    // unless that file is a header.
+    if (!utils::isSpellingLocInHeaderFile(
+            D->getLocStart(), *Result.SourceManager, HeaderFileExtensions))
+      return;
+  }
+
+  if (const auto *UsingDirective = dyn_cast<UsingDirectiveDecl>(D)) {
+    if (UsingDirective->getNominatedNamespace()->isAnonymousNamespace()) {
+      // Anynoumous namespaces inject a using directive into the AST to import
+      // the names into the containing namespace.
+      // We should not have them in headers, but there is another warning for
+      // that.
+      return;
+    }
+  }
+
+  diag(D->getLocStart(),
+       "using declarations in the global namespace in headers are prohibited");
+}
+
+} // namespace readability
+} // namespace google
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/google/GlobalNamesInHeadersCheck.h b/clang-tidy/google/GlobalNamesInHeadersCheck.h
new file mode 100644 (file)
index 0000000..79a6e28
--- /dev/null
@@ -0,0 +1,47 @@
+//===--- GlobalNamesInHeadersCheck.h - clang-tidy ---------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_GLOBALNAMESINHEADERSCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_GLOBALNAMESINHEADERSCHECK_H
+
+#include "../ClangTidy.h"
+#include "../utils/HeaderFileExtensionsUtils.h"
+
+namespace clang {
+namespace tidy {
+namespace google {
+namespace readability {
+
+/// Flag global namespace pollution in header files.
+/// Right now it only triggers on using declarations and directives.
+///
+/// The check supports these options:
+///   - `HeaderFileExtensions`: a comma-separated list of filename extensions
+///     of header files (the filename extensions should not contain "." prefix).
+///     "h" by default.
+///     For extension-less header files, using an empty string or leaving an
+///     empty string between "," if there are other filename extensions.
+class GlobalNamesInHeadersCheck : public ClangTidyCheck {
+public:
+  GlobalNamesInHeadersCheck(StringRef Name, ClangTidyContext *Context);
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+  const std::string RawStringHeaderFileExtensions;
+  utils::HeaderFileExtensionsSet HeaderFileExtensions;
+};
+
+} // namespace readability
+} // namespace google
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_GLOBALNAMESINHEADERSCHECK_H
diff --git a/clang-tidy/google/GoogleTidyModule.cpp b/clang-tidy/google/GoogleTidyModule.cpp
new file mode 100644 (file)
index 0000000..1328463
--- /dev/null
@@ -0,0 +1,99 @@
+//===--- GoogleTidyModule.cpp - clang-tidy --------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "../ClangTidy.h"
+#include "../ClangTidyModule.h"
+#include "../ClangTidyModuleRegistry.h"
+#include "../readability/BracesAroundStatementsCheck.h"
+#include "../readability/FunctionSizeCheck.h"
+#include "../readability/NamespaceCommentCheck.h"
+#include "../readability/RedundantSmartptrGetCheck.h"
+#include "AvoidCStyleCastsCheck.h"
+#include "DefaultArgumentsCheck.h"
+#include "ExplicitConstructorCheck.h"
+#include "ExplicitMakePairCheck.h"
+#include "GlobalNamesInHeadersCheck.h"
+#include "IntegerTypesCheck.h"
+#include "NonConstReferences.h"
+#include "OverloadedUnaryAndCheck.h"
+#include "StringReferenceMemberCheck.h"
+#include "TodoCommentCheck.h"
+#include "UnnamedNamespaceInHeaderCheck.h"
+#include "UsingNamespaceDirectiveCheck.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace google {
+
+class GoogleModule : public ClangTidyModule {
+public:
+  void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
+    CheckFactories.registerCheck<build::ExplicitMakePairCheck>(
+        "google-build-explicit-make-pair");
+    CheckFactories.registerCheck<build::UnnamedNamespaceInHeaderCheck>(
+        "google-build-namespaces");
+    CheckFactories.registerCheck<build::UsingNamespaceDirectiveCheck>(
+        "google-build-using-namespace");
+    CheckFactories.registerCheck<DefaultArgumentsCheck>(
+        "google-default-arguments");
+    CheckFactories.registerCheck<ExplicitConstructorCheck>(
+        "google-explicit-constructor");
+    CheckFactories.registerCheck<runtime::IntegerTypesCheck>(
+        "google-runtime-int");
+    CheckFactories.registerCheck<runtime::OverloadedUnaryAndCheck>(
+        "google-runtime-operator");
+    CheckFactories.registerCheck<runtime::NonConstReferences>(
+        "google-runtime-references");
+    CheckFactories.registerCheck<runtime::StringReferenceMemberCheck>(
+        "google-runtime-member-string-references");
+    CheckFactories.registerCheck<readability::AvoidCStyleCastsCheck>(
+        "google-readability-casting");
+    CheckFactories.registerCheck<readability::TodoCommentCheck>(
+        "google-readability-todo");
+    CheckFactories
+        .registerCheck<clang::tidy::readability::BracesAroundStatementsCheck>(
+            "google-readability-braces-around-statements");
+    CheckFactories.registerCheck<readability::GlobalNamesInHeadersCheck>(
+        "google-global-names-in-headers");
+    CheckFactories.registerCheck<clang::tidy::readability::FunctionSizeCheck>(
+        "google-readability-function-size");
+    CheckFactories
+        .registerCheck<clang::tidy::readability::NamespaceCommentCheck>(
+            "google-readability-namespace-comments");
+    CheckFactories
+        .registerCheck<clang::tidy::readability::RedundantSmartptrGetCheck>(
+            "google-readability-redundant-smartptr-get");
+  }
+
+  ClangTidyOptions getModuleOptions() override {
+    ClangTidyOptions Options;
+    auto &Opts = Options.CheckOptions;
+    Opts["google-readability-braces-around-statements.ShortStatementLines"] =
+        "1";
+    Opts["google-readability-function-size.StatementThreshold"] = "800";
+    Opts["google-readability-namespace-comments.ShortNamespaceLines"] = "10";
+    Opts["google-readability-namespace-comments.SpacesBeforeComments"] = "2";
+    return Options;
+  }
+};
+
+// Register the GoogleTidyModule using this statically initialized variable.
+static ClangTidyModuleRegistry::Add<GoogleModule> X("google-module",
+                                                    "Adds Google lint checks.");
+
+} // namespace google
+
+// This anchor is used to force the linker to link in the generated object file
+// and thus register the GoogleModule.
+volatile int GoogleModuleAnchorSource = 0;
+
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/google/IntegerTypesCheck.cpp b/clang-tidy/google/IntegerTypesCheck.cpp
new file mode 100644 (file)
index 0000000..f6c917c
--- /dev/null
@@ -0,0 +1,144 @@
+//===--- IntegerTypesCheck.cpp - clang-tidy -------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "IntegerTypesCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Basic/CharInfo.h"
+#include "clang/Basic/IdentifierTable.h"
+#include "clang/Basic/TargetInfo.h"
+#include "clang/Lex/Lexer.h"
+
+namespace clang {
+
+using namespace ast_matchers;
+
+static Token getTokenAtLoc(SourceLocation Loc,
+                           const MatchFinder::MatchResult &MatchResult,
+                           IdentifierTable &IdentTable) {
+  Token Tok;
+  if (Lexer::getRawToken(Loc, Tok, *MatchResult.SourceManager,
+                         MatchResult.Context->getLangOpts(), false))
+    return Tok;
+
+  if (Tok.is(tok::raw_identifier)) {
+    IdentifierInfo &Info = IdentTable.get(Tok.getRawIdentifier());
+    Tok.setIdentifierInfo(&Info);
+    Tok.setKind(Info.getTokenID());
+  }
+  return Tok;
+}
+
+namespace tidy {
+namespace google {
+namespace runtime {
+
+IntegerTypesCheck::IntegerTypesCheck(StringRef Name, ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      UnsignedTypePrefix(Options.get("UnsignedTypePrefix", "uint")),
+      SignedTypePrefix(Options.get("SignedTypePrefix", "int")),
+      TypeSuffix(Options.get("TypeSuffix", "")) {}
+
+void IntegerTypesCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "UnsignedTypePrefix", UnsignedTypePrefix);
+  Options.store(Opts, "SignedTypePrefix", SignedTypePrefix);
+  Options.store(Opts, "TypeSuffix", TypeSuffix);
+}
+
+void IntegerTypesCheck::registerMatchers(MatchFinder *Finder) {
+  // Find all TypeLocs. The relevant Style Guide rule only applies to C++.
+  if (!getLangOpts().CPlusPlus)
+    return;
+  Finder->addMatcher(typeLoc(loc(isInteger())).bind("tl"), this);
+  IdentTable = llvm::make_unique<IdentifierTable>(getLangOpts());
+}
+
+void IntegerTypesCheck::check(const MatchFinder::MatchResult &Result) {
+  auto TL = *Result.Nodes.getNodeAs<TypeLoc>("tl");
+  SourceLocation Loc = TL.getLocStart();
+
+  if (Loc.isInvalid() || Loc.isMacroID())
+    return;
+
+  // Look through qualification.
+  if (auto QualLoc = TL.getAs<QualifiedTypeLoc>())
+    TL = QualLoc.getUnqualifiedLoc();
+
+  auto BuiltinLoc = TL.getAs<BuiltinTypeLoc>();
+  if (!BuiltinLoc)
+    return;
+
+  Token Tok = getTokenAtLoc(Loc, Result, *IdentTable);
+  // Ensure the location actually points to one of the builting integral type
+  // names we're interested in. Otherwise, we might be getting this match from
+  // implicit code (e.g. an implicit assignment operator of a class containing
+  // an array of non-POD types).
+  if (!Tok.isOneOf(tok::kw_short, tok::kw_long, tok::kw_unsigned,
+                   tok::kw_signed))
+    return;
+
+  bool IsSigned;
+  unsigned Width;
+  const TargetInfo &TargetInfo = Result.Context->getTargetInfo();
+
+  // Look for uses of short, long, long long and their unsigned versions.
+  switch (BuiltinLoc.getTypePtr()->getKind()) {
+  case BuiltinType::Short:
+    Width = TargetInfo.getShortWidth();
+    IsSigned = true;
+    break;
+  case BuiltinType::Long:
+    Width = TargetInfo.getLongWidth();
+    IsSigned = true;
+    break;
+  case BuiltinType::LongLong:
+    Width = TargetInfo.getLongLongWidth();
+    IsSigned = true;
+    break;
+  case BuiltinType::UShort:
+    Width = TargetInfo.getShortWidth();
+    IsSigned = false;
+    break;
+  case BuiltinType::ULong:
+    Width = TargetInfo.getLongWidth();
+    IsSigned = false;
+    break;
+  case BuiltinType::ULongLong:
+    Width = TargetInfo.getLongLongWidth();
+    IsSigned = false;
+    break;
+  default:
+    return;
+  }
+
+  // We allow "unsigned short port" as that's reasonably common and required by
+  // the sockets API.
+  const StringRef Port = "unsigned short port";
+  const char *Data = Result.SourceManager->getCharacterData(Loc);
+  if (!std::strncmp(Data, Port.data(), Port.size()) &&
+      !isIdentifierBody(Data[Port.size()]))
+    return;
+
+  std::string Replacement =
+      ((IsSigned ? SignedTypePrefix : UnsignedTypePrefix) + Twine(Width) +
+       TypeSuffix)
+          .str();
+
+  // We don't add a fix-it as changing the type can easily break code,
+  // e.g. when a function requires a 'long' argument on all platforms.
+  // QualTypes are printed with implicit quotes.
+  diag(Loc, "consider replacing %0 with '%1'") << BuiltinLoc.getType()
+                                               << Replacement;
+}
+
+} // namespace runtime
+} // namespace google
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/google/IntegerTypesCheck.h b/clang-tidy/google/IntegerTypesCheck.h
new file mode 100644 (file)
index 0000000..8d8f903
--- /dev/null
@@ -0,0 +1,49 @@
+//===--- IntegerTypesCheck.h - clang-tidy -----------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_INTEGERTYPESCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_INTEGERTYPESCHECK_H
+
+#include "../ClangTidy.h"
+
+#include <memory>
+
+namespace clang {
+
+class IdentifierTable;
+
+namespace tidy {
+namespace google {
+namespace runtime {
+
+/// Finds uses of `short`, `long` and `long long` and suggest replacing them
+/// with `u?intXX(_t)?`.
+///
+/// Correspondig cpplint.py check: 'runtime/int'.
+class IntegerTypesCheck : public ClangTidyCheck {
+public:
+  IntegerTypesCheck(StringRef Name, ClangTidyContext *Context);
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+  void storeOptions(ClangTidyOptions::OptionMap &Options) override;
+
+private:
+  const std::string UnsignedTypePrefix;
+  const std::string SignedTypePrefix;
+  const std::string TypeSuffix;
+
+  std::unique_ptr<IdentifierTable> IdentTable;
+};
+
+} // namespace runtime
+} // namespace google
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_INTEGERTYPESCHECK_H
diff --git a/clang-tidy/google/NonConstReferences.cpp b/clang-tidy/google/NonConstReferences.cpp
new file mode 100644 (file)
index 0000000..856bc5b
--- /dev/null
@@ -0,0 +1,149 @@
+//===--- NonConstReferences.cpp - clang-tidy --------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "NonConstReferences.h"
+#include "../utils/OptionsUtils.h"
+#include "clang/AST/DeclBase.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace google {
+namespace runtime {
+
+NonConstReferences::NonConstReferences(StringRef Name,
+                                       ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      WhiteListTypes(
+          utils::options::parseStringList(Options.get("WhiteListTypes", ""))) {}
+
+void NonConstReferences::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "WhiteListTypes",
+                utils::options::serializeStringList(WhiteListTypes));
+}
+
+void NonConstReferences::registerMatchers(MatchFinder *Finder) {
+  if (!getLangOpts().CPlusPlus)
+    return;
+
+  Finder->addMatcher(
+      parmVarDecl(
+          unless(isInstantiated()),
+          hasType(references(
+              qualType(unless(isConstQualified())).bind("referenced_type"))),
+          unless(hasType(rValueReferenceType())))
+          .bind("param"),
+      this);
+}
+
+void NonConstReferences::check(const MatchFinder::MatchResult &Result) {
+  const auto *Parameter = Result.Nodes.getNodeAs<ParmVarDecl>("param");
+  const auto *Function =
+      dyn_cast_or_null<FunctionDecl>(Parameter->getParentFunctionOrMethod());
+
+  if (Function == nullptr || Function->isImplicit())
+    return;
+
+  if (!Function->isCanonicalDecl())
+    return;
+
+  if (const auto *Method = dyn_cast<CXXMethodDecl>(Function)) {
+    // Don't warn on implementations of an interface using references.
+    if (Method->begin_overridden_methods() != Method->end_overridden_methods())
+      return;
+    // Don't warn on lambdas, as they frequently have to conform to the
+    // interface defined elsewhere.
+    if (Method->getParent()->isLambda())
+      return;
+  }
+
+  auto ReferencedType = *Result.Nodes.getNodeAs<QualType>("referenced_type");
+
+  if (std::find_if(WhiteListTypes.begin(), WhiteListTypes.end(),
+                   [&](llvm::StringRef WhiteListType) {
+                     return ReferencedType.getCanonicalType().getAsString(
+                                Result.Context->getPrintingPolicy()) ==
+                            WhiteListType;
+                   }) != WhiteListTypes.end())
+    return;
+
+  // Don't warn on function references, they shouldn't be constant.
+  if (ReferencedType->isFunctionProtoType())
+    return;
+
+  // Don't warn on dependent types in templates.
+  if (ReferencedType->isDependentType())
+    return;
+
+  if (Function->isOverloadedOperator()) {
+    switch (Function->getOverloadedOperator()) {
+      case clang::OO_LessLess:
+      case clang::OO_PlusPlus:
+      case clang::OO_MinusMinus:
+      case clang::OO_PlusEqual:
+      case clang::OO_MinusEqual:
+      case clang::OO_StarEqual:
+      case clang::OO_SlashEqual:
+      case clang::OO_PercentEqual:
+      case clang::OO_LessLessEqual:
+      case clang::OO_GreaterGreaterEqual:
+      case clang::OO_PipeEqual:
+      case clang::OO_CaretEqual:
+      case clang::OO_AmpEqual:
+        // Don't warn on the first parameter of operator<<(Stream&, ...),
+        // operator++, operator-- and operation+assignment operators.
+        if (Function->getParamDecl(0) == Parameter)
+          return;
+        break;
+      case clang::OO_GreaterGreater: {
+        auto isNonConstRef = [](clang::QualType T) {
+          return T->isReferenceType() &&
+                 !T.getNonReferenceType().isConstQualified();
+        };
+        // Don't warn on parameters of stream extractors:
+        //   Stream& operator>>(Stream&, Value&);
+        // Both parameters should be non-const references by convention.
+        if (isNonConstRef(Function->getParamDecl(0)->getType()) &&
+            (Function->getNumParams() < 2 || // E.g. member operator>>.
+             isNonConstRef(Function->getParamDecl(1)->getType())) &&
+            isNonConstRef(Function->getReturnType()))
+          return;
+        break;
+      }
+      default:
+        break;
+    }
+  }
+
+  // Some functions use references to comply with established standards.
+  if (Function->getDeclName().isIdentifier() && Function->getName() == "swap")
+    return;
+
+  // iostream parameters are typically passed by non-const reference.
+  if (StringRef(ReferencedType.getAsString()).endswith("stream"))
+    return;
+
+  if (Parameter->getName().empty()) {
+    diag(Parameter->getLocation(), "non-const reference parameter at index %0, "
+                                   "make it const or use a pointer")
+        << Parameter->getFunctionScopeIndex();
+  } else {
+    diag(Parameter->getLocation(),
+         "non-const reference parameter %0, make it const or use a pointer")
+        << Parameter;
+  }
+}
+
+} // namespace runtime
+} // namespace google
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/google/NonConstReferences.h b/clang-tidy/google/NonConstReferences.h
new file mode 100644 (file)
index 0000000..a665813
--- /dev/null
@@ -0,0 +1,39 @@
+//===--- NonConstReferences.h - clang-tidy ----------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_NON_CONST_REFERENCES_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_NON_CONST_REFERENCES_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace google {
+namespace runtime {
+
+/// \brief Checks the usage of non-constant references in function parameters.
+///
+/// https://google.github.io/styleguide/cppguide.html#Reference_Arguments
+class NonConstReferences : public ClangTidyCheck {
+public:
+  NonConstReferences(StringRef Name, ClangTidyContext *Context);
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+
+private:
+  const std::vector<std::string> WhiteListTypes;
+};
+
+} // namespace runtime
+} // namespace google
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_NON_CONST_REFERENCES_H
diff --git a/clang-tidy/google/OverloadedUnaryAndCheck.cpp b/clang-tidy/google/OverloadedUnaryAndCheck.cpp
new file mode 100644 (file)
index 0000000..84abb5f
--- /dev/null
@@ -0,0 +1,53 @@
+//===--- OverloadedUnaryAndCheck.cpp - clang-tidy ---------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "OverloadedUnaryAndCheck.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 google {
+namespace runtime {
+
+void OverloadedUnaryAndCheck::registerMatchers(
+    ast_matchers::MatchFinder *Finder) {
+  // Only register the matchers for C++; the functionality currently does not
+  // provide any benefit to other languages, despite being benign.
+  if (!getLangOpts().CPlusPlus)
+    return;
+
+  // Match unary methods that overload operator&.
+  Finder->addMatcher(
+      cxxMethodDecl(parameterCountIs(0), hasOverloadedOperatorName("&"))
+          .bind("overload"),
+      this);
+  // Also match freestanding unary operator& overloads. Be careful not to match
+  // binary methods.
+  Finder->addMatcher(
+      functionDecl(allOf(
+          unless(cxxMethodDecl()),
+          functionDecl(parameterCountIs(1), hasOverloadedOperatorName("&"))
+              .bind("overload"))),
+      this);
+}
+
+void OverloadedUnaryAndCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *Decl = Result.Nodes.getNodeAs<FunctionDecl>("overload");
+  diag(Decl->getLocStart(),
+       "do not overload unary operator&, it is dangerous.");
+}
+
+} // namespace runtime
+} // namespace google
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/google/OverloadedUnaryAndCheck.h b/clang-tidy/google/OverloadedUnaryAndCheck.h
new file mode 100644 (file)
index 0000000..5492eba
--- /dev/null
@@ -0,0 +1,38 @@
+//===--- OverloadedUnaryAndCheck.h - clang-tidy -----------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_OVERLOADEDUNARYANDCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_OVERLOADEDUNARYANDCHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace google {
+namespace runtime {
+
+/// Finds overloads of unary `operator &`.
+///
+/// https://google.github.io/styleguide/cppguide.html#Operator_Overloading
+///
+/// Corresponding cpplint.py check name: 'runtime/operator'.
+class OverloadedUnaryAndCheck : public ClangTidyCheck {
+public:
+  OverloadedUnaryAndCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace runtime
+} // namespace google
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_OVERLOADEDUNARYANDCHECK_H
diff --git a/clang-tidy/google/StringReferenceMemberCheck.cpp b/clang-tidy/google/StringReferenceMemberCheck.cpp
new file mode 100644 (file)
index 0000000..36d979b
--- /dev/null
@@ -0,0 +1,51 @@
+//===--- StringReferenceMemberCheck.cpp - clang-tidy ------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "StringReferenceMemberCheck.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 google {
+namespace runtime {
+
+void StringReferenceMemberCheck::registerMatchers(
+    ast_matchers::MatchFinder *Finder) {
+  // Only register the matchers for C++; the functionality currently does not
+  // provide any benefit to other languages, despite being benign.
+  if (!getLangOpts().CPlusPlus)
+    return;
+
+  // Look for const references to std::string or ::string.
+  auto String = anyOf(recordDecl(hasName("::std::basic_string")),
+                      recordDecl(hasName("::string")));
+  auto ConstString = qualType(isConstQualified(), hasDeclaration(String));
+
+  // Ignore members in template instantiations.
+  Finder->addMatcher(
+      fieldDecl(hasType(references(ConstString)), unless(isInstantiated()))
+          .bind("member"),
+      this);
+}
+
+void StringReferenceMemberCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *Member = Result.Nodes.getNodeAs<FieldDecl>("member");
+  diag(Member->getLocStart(), "const string& members are dangerous; it is much "
+                              "better to use alternatives, such as pointers or "
+                              "simple constants");
+}
+
+} // namespace runtime
+} // namespace google
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/google/StringReferenceMemberCheck.h b/clang-tidy/google/StringReferenceMemberCheck.h
new file mode 100644 (file)
index 0000000..5a39fd9
--- /dev/null
@@ -0,0 +1,54 @@
+//===--- StringReferenceMemberCheck.h - clang-tidy ----------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_STRINGREFERENCEMEMBERCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_STRINGREFERENCEMEMBERCHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace google {
+namespace runtime {
+
+/// Finds members of type `const string&`.
+///
+/// const string reference members are generally considered unsafe as they can
+/// be created from a temporary quite easily.
+///
+/// \code
+///   struct S {
+///     S(const string &Str) : Str(Str) {}
+///     const string &Str;
+///   };
+///   S instance("string");
+/// \endcode
+///
+/// In the constructor call a string temporary is created from `const char *`
+/// and destroyed immediately after the call. This leaves around a dangling
+/// reference.
+///
+/// This check emit warnings for both `std::string` and `::string` const
+/// reference members.
+///
+/// Corresponding cpplint.py check name: 'runtime/member_string_reference'.
+class StringReferenceMemberCheck : public ClangTidyCheck {
+public:
+  StringReferenceMemberCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace runtime
+} // namespace google
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_STRINGREFERENCEMEMBERCHECK_H
diff --git a/clang-tidy/google/TodoCommentCheck.cpp b/clang-tidy/google/TodoCommentCheck.cpp
new file mode 100644 (file)
index 0000000..f1c79ce
--- /dev/null
@@ -0,0 +1,66 @@
+//===--- TodoCommentCheck.cpp - clang-tidy --------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "TodoCommentCheck.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Lex/Preprocessor.h"
+
+namespace clang {
+namespace tidy {
+namespace google {
+namespace readability {
+
+class TodoCommentCheck::TodoCommentHandler : public CommentHandler {
+public:
+  TodoCommentHandler(TodoCommentCheck &Check, llvm::Optional<std::string> User)
+      : Check(Check), User(User ? *User : "unknown"),
+        TodoMatch("^// *TODO *(\\(.*\\))?:?( )?(.*)$") {}
+
+  bool HandleComment(Preprocessor &PP, SourceRange Range) override {
+    StringRef Text =
+        Lexer::getSourceText(CharSourceRange::getCharRange(Range),
+                             PP.getSourceManager(), PP.getLangOpts());
+
+    SmallVector<StringRef, 4> Matches;
+    if (!TodoMatch.match(Text, &Matches))
+      return false;
+
+    StringRef Username = Matches[1];
+    StringRef Comment = Matches[3];
+
+    if (!Username.empty())
+      return false;
+
+    std::string NewText = ("// TODO(" + Twine(User) + "): " + Comment).str();
+
+    Check.diag(Range.getBegin(), "missing username/bug in TODO")
+        << FixItHint::CreateReplacement(CharSourceRange::getCharRange(Range),
+                                        NewText);
+    return false;
+  }
+
+private:
+  TodoCommentCheck &Check;
+  std::string User;
+  llvm::Regex TodoMatch;
+};
+
+TodoCommentCheck::TodoCommentCheck(StringRef Name, ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      Handler(llvm::make_unique<TodoCommentHandler>(
+          *this, Context->getOptions().User)) {}
+
+void TodoCommentCheck::registerPPCallbacks(CompilerInstance &Compiler) {
+  Compiler.getPreprocessor().addCommentHandler(Handler.get());
+}
+
+} // namespace readability
+} // namespace google
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/google/TodoCommentCheck.h b/clang-tidy/google/TodoCommentCheck.h
new file mode 100644 (file)
index 0000000..dbdc366
--- /dev/null
@@ -0,0 +1,38 @@
+//===--- TodoCommentCheck.h - clang-tidy ------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_TODOCOMMENTCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_TODOCOMMENTCHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace google {
+namespace readability {
+
+/// Finds TODO comments without a username or bug number.
+///
+/// Corresponding cpplint.py check: 'readability/todo'
+class TodoCommentCheck : public ClangTidyCheck {
+public:
+  TodoCommentCheck(StringRef Name, ClangTidyContext *Context);
+  void registerPPCallbacks(CompilerInstance &Compiler) override;
+
+private:
+  class TodoCommentHandler;
+  std::unique_ptr<TodoCommentHandler> Handler;
+};
+
+} // namespace readability
+} // namespace google
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_TODOCOMMENTCHECK_H
diff --git a/clang-tidy/google/UnnamedNamespaceInHeaderCheck.cpp b/clang-tidy/google/UnnamedNamespaceInHeaderCheck.cpp
new file mode 100644 (file)
index 0000000..3e76bdb
--- /dev/null
@@ -0,0 +1,63 @@
+//===--- UnnamedNamespaceInHeaderCheck.cpp - clang-tidy ---------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "UnnamedNamespaceInHeaderCheck.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 google {
+namespace build {
+
+UnnamedNamespaceInHeaderCheck::UnnamedNamespaceInHeaderCheck(
+    StringRef Name, ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      RawStringHeaderFileExtensions(
+          Options.getLocalOrGlobal("HeaderFileExtensions", "h,hh,hpp,hxx")) {
+  if (!utils::parseHeaderFileExtensions(RawStringHeaderFileExtensions,
+                                        HeaderFileExtensions, ',')) {
+    llvm::errs() << "Invalid header file extension: "
+                 << RawStringHeaderFileExtensions << "\n";
+  }
+}
+
+void UnnamedNamespaceInHeaderCheck::storeOptions(
+    ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "HeaderFileExtensions", RawStringHeaderFileExtensions);
+}
+
+void UnnamedNamespaceInHeaderCheck::registerMatchers(
+    ast_matchers::MatchFinder *Finder) {
+  // Only register the matchers for C++; the functionality currently does not
+  // provide any benefit to other languages, despite being benign.
+  if (getLangOpts().CPlusPlus)
+    Finder->addMatcher(namespaceDecl(isAnonymous()).bind("anonymousNamespace"),
+                       this);
+}
+
+void UnnamedNamespaceInHeaderCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  const auto *N = Result.Nodes.getNodeAs<NamespaceDecl>("anonymousNamespace");
+  SourceLocation Loc = N->getLocStart();
+  if (!Loc.isValid())
+    return;
+
+  if (utils::isPresumedLocInHeaderFile(Loc, *Result.SourceManager,
+                                       HeaderFileExtensions))
+    diag(Loc, "do not use unnamed namespaces in header files");
+}
+
+} // namespace build
+} // namespace google
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/google/UnnamedNamespaceInHeaderCheck.h b/clang-tidy/google/UnnamedNamespaceInHeaderCheck.h
new file mode 100644 (file)
index 0000000..4d310f5
--- /dev/null
@@ -0,0 +1,50 @@
+//===--- UnnamedNamespaceInHeaderCheck.h - clang-tidy -----------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_UNNAMEDNAMESPACEINHEADERCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_UNNAMEDNAMESPACEINHEADERCHECK_H
+
+#include "../ClangTidy.h"
+#include "../utils/HeaderFileExtensionsUtils.h"
+
+namespace clang {
+namespace tidy {
+namespace google {
+namespace build {
+
+/// Finds anonymous namespaces in headers.
+///
+/// The check supports these options:
+///   - `HeaderFileExtensions`: a comma-separated list of filename extensions of
+///     header files (The filename extensions should not contain "." prefix).
+///     "h,hh,hpp,hxx" by default.
+///     For extension-less header files, using an empty string or leaving an
+///     empty string between "," if there are other filename extensions.
+///
+/// https://google.github.io/styleguide/cppguide.html#Namespaces
+///
+/// Corresponding cpplint.py check name: 'build/namespaces'.
+class UnnamedNamespaceInHeaderCheck : public ClangTidyCheck {
+public:
+  UnnamedNamespaceInHeaderCheck(StringRef Name, ClangTidyContext *Context);
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+  const std::string RawStringHeaderFileExtensions;
+  utils::HeaderFileExtensionsSet HeaderFileExtensions;
+};
+
+} // namespace build
+} // namespace google
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_UNNAMEDNAMESPACEINHEADERCHECK_H
diff --git a/clang-tidy/google/UsingNamespaceDirectiveCheck.cpp b/clang-tidy/google/UsingNamespaceDirectiveCheck.cpp
new file mode 100644 (file)
index 0000000..60a46f8
--- /dev/null
@@ -0,0 +1,66 @@
+//===--- UsingNamespaceDirectiveCheck.cpp - clang-tidy ----------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "UsingNamespaceDirectiveCheck.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 google {
+namespace build {
+
+void UsingNamespaceDirectiveCheck::registerMatchers(
+    ast_matchers::MatchFinder *Finder) {
+  // Only register the matchers for C++; the functionality currently does not
+  // provide any benefit to other languages, despite being benign.
+  if (getLangOpts().CPlusPlus)
+    Finder->addMatcher(usingDirectiveDecl().bind("usingNamespace"), this);
+}
+
+void UsingNamespaceDirectiveCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  const auto *U = Result.Nodes.getNodeAs<UsingDirectiveDecl>("usingNamespace");
+  SourceLocation Loc = U->getLocStart();
+  if (U->isImplicit() || !Loc.isValid())
+    return;
+
+  // Do not warn if namespace is a std namespace with user-defined literals. The
+  // user-defined literals can only be used with a using directive.
+  if (isStdLiteralsNamespace(U->getNominatedNamespace()))
+    return;
+
+  diag(Loc, "do not use namespace using-directives; "
+            "use using-declarations instead");
+  // TODO: We could suggest a list of using directives replacing the using
+  //       namespace directive.
+}
+
+bool UsingNamespaceDirectiveCheck::isStdLiteralsNamespace(
+    const NamespaceDecl *NS) {
+  if (!NS->getName().endswith("literals"))
+    return false;
+
+  const auto *Parent = dyn_cast_or_null<NamespaceDecl>(NS->getParent());
+  if (!Parent)
+    return false;
+
+  if (Parent->isStdNamespace())
+    return true;
+
+  return Parent->getName() == "literals" && Parent->getParent() &&
+         Parent->getParent()->isStdNamespace();
+}
+} // namespace build
+} // namespace google
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/google/UsingNamespaceDirectiveCheck.h b/clang-tidy/google/UsingNamespaceDirectiveCheck.h
new file mode 100644 (file)
index 0000000..2be65c1
--- /dev/null
@@ -0,0 +1,51 @@
+//===--- UsingNamespaceDirectiveCheck.h - clang-tidy ------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_USINGNAMESPACEDIRECTIVECHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_USINGNAMESPACEDIRECTIVECHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace google {
+namespace build {
+
+/// Finds using namespace directives.
+///
+/// https://google.github.io/styleguide/cppguide.html#Namespaces
+///
+/// The check implements the following rule of the Google C++ Style Guide:
+///
+///   You may not use a using-directive to make all names from a namespace
+///   available.
+///
+///   \code
+///     // Forbidden -- This pollutes the namespace.
+///     using namespace foo;
+///   \endcode
+///
+/// Corresponding cpplint.py check name: `build/namespaces`.
+class UsingNamespaceDirectiveCheck : public ClangTidyCheck {
+public:
+  UsingNamespaceDirectiveCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+  static bool isStdLiteralsNamespace(const NamespaceDecl *NS);
+};
+
+} // namespace build
+} // namespace google
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_USINGNAMESPACEDIRECTIVECHECK_H
diff --git a/clang-tidy/hicpp/CMakeLists.txt b/clang-tidy/hicpp/CMakeLists.txt
new file mode 100644 (file)
index 0000000..d3fff95
--- /dev/null
@@ -0,0 +1,19 @@
+set(LLVM_LINK_COMPONENTS support)
+
+add_clang_library(clangTidyHICPPModule
+  NoAssemblerCheck.cpp
+  HICPPTidyModule.cpp
+
+  LINK_LIBS
+  clangAST
+  clangASTMatchers
+  clangBasic
+  clangLex
+  clangTidy
+  clangTidyCppCoreGuidelinesModule
+  clangTidyGoogleModule
+  clangTidyMiscModule
+  clangTidyModernizeModule
+  clangTidyReadabilityModule
+  clangTidyUtils
+  )
diff --git a/clang-tidy/hicpp/HICPPTidyModule.cpp b/clang-tidy/hicpp/HICPPTidyModule.cpp
new file mode 100644 (file)
index 0000000..3cde368
--- /dev/null
@@ -0,0 +1,75 @@
+//===------- HICPPTidyModule.cpp - clang-tidy -----------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "../ClangTidy.h"
+#include "../ClangTidyModule.h"
+#include "../ClangTidyModuleRegistry.h"
+#include "../cppcoreguidelines/ProTypeMemberInitCheck.h"
+#include "../cppcoreguidelines/SpecialMemberFunctionsCheck.h"
+#include "../google/DefaultArgumentsCheck.h"
+#include "../google/ExplicitConstructorCheck.h"
+#include "../misc/NewDeleteOverloadsCheck.h"
+#include "../misc/NoexceptMoveConstructorCheck.h"
+#include "../misc/UndelegatedConstructor.h"
+#include "../misc/UseAfterMoveCheck.h"
+#include "../modernize/UseEqualsDefaultCheck.h"
+#include "../modernize/UseEqualsDeleteCheck.h"
+#include "../modernize/UseOverrideCheck.h"
+#include "../readability/FunctionSizeCheck.h"
+#include "../readability/IdentifierNamingCheck.h"
+#include "NoAssemblerCheck.h"
+
+namespace clang {
+namespace tidy {
+namespace hicpp {
+
+class HICPPModule : public ClangTidyModule {
+public:
+  void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
+    CheckFactories.registerCheck<google::ExplicitConstructorCheck>(
+        "hicpp-explicit-conversions");
+    CheckFactories.registerCheck<readability::FunctionSizeCheck>(
+        "hicpp-function-size");
+    CheckFactories.registerCheck<readability::IdentifierNamingCheck>(
+        "hicpp-named-parameter");
+    CheckFactories.registerCheck<misc::UseAfterMoveCheck>(
+        "hicpp-invalid-access-moved");
+    CheckFactories.registerCheck<cppcoreguidelines::ProTypeMemberInitCheck>(
+        "hicpp-member-init");
+    CheckFactories.registerCheck<misc::NewDeleteOverloadsCheck>(
+        "hicpp-new-delete-operators");
+    CheckFactories.registerCheck<misc::NoexceptMoveConstructorCheck>(
+        "hicpp-noexcept-move");
+    CheckFactories.registerCheck<NoAssemblerCheck>("hicpp-no-assembler");
+    CheckFactories
+        .registerCheck<cppcoreguidelines::SpecialMemberFunctionsCheck>(
+            "hicpp-special-member-functions");
+    CheckFactories.registerCheck<misc::UndelegatedConstructorCheck>(
+        "hicpp-undelegated-constructor");
+    CheckFactories.registerCheck<modernize::UseEqualsDefaultCheck>(
+        "hicpp-use-equals-default");
+    CheckFactories.registerCheck<modernize::UseEqualsDeleteCheck>(
+        "hicpp-use-equals-delete");
+    CheckFactories.registerCheck<modernize::UseOverrideCheck>(
+        "hicpp-use-override");
+  }
+};
+
+// Register the HICPPModule using this statically initialized variable.
+static ClangTidyModuleRegistry::Add<HICPPModule>
+    X("hicpp-module", "Adds High-Integrity C++ checks.");
+
+} // namespace hicpp
+
+// This anchor is used to force the linker to link in the generated object file
+// and thus register the HICPPModule.
+volatile int HICPPModuleAnchorSource = 0;
+
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/hicpp/LICENSE.TXT b/clang-tidy/hicpp/LICENSE.TXT
new file mode 100644 (file)
index 0000000..fb8f513
--- /dev/null
@@ -0,0 +1,12 @@
+------------------------------------------------------------------------------
+clang-tidy High-Integrity C++ Files
+------------------------------------------------------------------------------
+All clang-tidy files are licensed under the LLVM license with the following
+additions:
+
+Any file referencing a High-Integrity C++ Coding guideline:
+
+HIC++ Coding Standard as created by PRQA.
+
+Please see http://www.codingstandard.com/section/conditions-of-use/ for more
+information.
diff --git a/clang-tidy/hicpp/NoAssemblerCheck.cpp b/clang-tidy/hicpp/NoAssemblerCheck.cpp
new file mode 100644 (file)
index 0000000..053db61
--- /dev/null
@@ -0,0 +1,51 @@
+//===--- NoAssemblerCheck.cpp - clang-tidy---------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "NoAssemblerCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace ast_matchers {
+AST_MATCHER(VarDecl, isAsm) { return Node.hasAttr<clang::AsmLabelAttr>(); }
+const internal::VariadicDynCastAllOfMatcher<Decl, FileScopeAsmDecl>
+    fileScopeAsmDecl;
+}
+}
+
+namespace clang {
+namespace tidy {
+namespace hicpp {
+
+void NoAssemblerCheck::registerMatchers(MatchFinder *Finder) {
+  Finder->addMatcher(asmStmt().bind("asm-stmt"), this);
+  Finder->addMatcher(fileScopeAsmDecl().bind("asm-file-scope"), this);
+  Finder->addMatcher(varDecl(isAsm()).bind("asm-var"), this);
+}
+
+void NoAssemblerCheck::check(const MatchFinder::MatchResult &Result) {
+  SourceLocation ASMLocation;
+  if (const auto *ASM = Result.Nodes.getNodeAs<AsmStmt>("asm-stmt"))
+    ASMLocation = ASM->getAsmLoc();
+  else if (const auto *ASM =
+               Result.Nodes.getNodeAs<FileScopeAsmDecl>("asm-file-scope"))
+    ASMLocation = ASM->getAsmLoc();
+  else if (const auto *ASM = Result.Nodes.getNodeAs<VarDecl>("asm-var"))
+    ASMLocation = ASM->getLocation();
+  else
+    llvm_unreachable("Unhandled case in matcher.");
+
+  diag(ASMLocation, "do not use inline assembler in safety-critical code");
+}
+
+} // namespace hicpp
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/hicpp/NoAssemblerCheck.h b/clang-tidy/hicpp/NoAssemblerCheck.h
new file mode 100644 (file)
index 0000000..416ccb0
--- /dev/null
@@ -0,0 +1,35 @@
+//===--- NoAssemblerCheck.h - clang-tidy-------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_HICPP_NO_ASSEMBLER_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_HICPP_NO_ASSEMBLER_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace hicpp {
+
+/// Find assembler statements. No fix is offered.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/hicpp-no-assembler.html
+class NoAssemblerCheck : public ClangTidyCheck {
+public:
+  NoAssemblerCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace hicpp
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_HICPP_NO_ASSEMBLER_H
diff --git a/clang-tidy/llvm/CMakeLists.txt b/clang-tidy/llvm/CMakeLists.txt
new file mode 100644 (file)
index 0000000..ce69c05
--- /dev/null
@@ -0,0 +1,18 @@
+set(LLVM_LINK_COMPONENTS support)
+
+add_clang_library(clangTidyLLVMModule
+  HeaderGuardCheck.cpp
+  IncludeOrderCheck.cpp
+  LLVMTidyModule.cpp
+  TwineLocalCheck.cpp
+
+  LINK_LIBS
+  clangAST
+  clangASTMatchers
+  clangBasic
+  clangLex
+  clangTidy
+  clangTidyReadabilityModule
+  clangTidyUtils
+  clangTooling
+  )
diff --git a/clang-tidy/llvm/HeaderGuardCheck.cpp b/clang-tidy/llvm/HeaderGuardCheck.cpp
new file mode 100644 (file)
index 0000000..6e0c2cb
--- /dev/null
@@ -0,0 +1,68 @@
+//===--- HeaderGuardCheck.cpp - clang-tidy --------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "HeaderGuardCheck.h"
+
+namespace clang {
+namespace tidy {
+namespace llvm {
+
+LLVMHeaderGuardCheck::LLVMHeaderGuardCheck(StringRef Name,
+                                           ClangTidyContext *Context)
+    : HeaderGuardCheck(Name, Context),
+      RawStringHeaderFileExtensions(
+          Options.getLocalOrGlobal("HeaderFileExtensions", ",h,hh,hpp,hxx")) {
+  utils::parseHeaderFileExtensions(RawStringHeaderFileExtensions,
+                                   HeaderFileExtensions, ',');
+}
+
+void LLVMHeaderGuardCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "HeaderFileExtensions", RawStringHeaderFileExtensions);
+}
+
+bool LLVMHeaderGuardCheck::shouldFixHeaderGuard(StringRef FileName) {
+  return utils::isHeaderFileExtension(FileName, HeaderFileExtensions);
+}
+
+std::string LLVMHeaderGuardCheck::getHeaderGuard(StringRef Filename,
+                                                 StringRef OldGuard) {
+  std::string Guard = tooling::getAbsolutePath(Filename);
+
+  // Sanitize the path. There are some rules for compatibility with the historic
+  // style in include/llvm and include/clang which we want to preserve.
+
+  // We don't want _INCLUDE_ in our guards.
+  size_t PosInclude = Guard.rfind("include/");
+  if (PosInclude != StringRef::npos)
+    Guard = Guard.substr(PosInclude + std::strlen("include/"));
+
+  // For clang we drop the _TOOLS_.
+  size_t PosToolsClang = Guard.rfind("tools/clang/");
+  if (PosToolsClang != StringRef::npos)
+    Guard = Guard.substr(PosToolsClang + std::strlen("tools/"));
+
+  // The remainder is LLVM_FULL_PATH_TO_HEADER_H
+  size_t PosLLVM = Guard.rfind("llvm/");
+  if (PosLLVM != StringRef::npos)
+    Guard = Guard.substr(PosLLVM);
+
+  std::replace(Guard.begin(), Guard.end(), '/', '_');
+  std::replace(Guard.begin(), Guard.end(), '.', '_');
+  std::replace(Guard.begin(), Guard.end(), '-', '_');
+
+  // The prevalent style in clang is LLVM_CLANG_FOO_BAR_H
+  if (StringRef(Guard).startswith("clang"))
+    Guard = "LLVM_" + Guard;
+
+  return StringRef(Guard).upper();
+}
+
+} // namespace llvm
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/llvm/HeaderGuardCheck.h b/clang-tidy/llvm/HeaderGuardCheck.h
new file mode 100644 (file)
index 0000000..3b8ad2b
--- /dev/null
@@ -0,0 +1,46 @@
+//===--- HeaderGuardCheck.h - clang-tidy ------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVM_HEADER_GUARD_CHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVM_HEADER_GUARD_CHECK_H
+
+#include "../utils/HeaderGuard.h"
+
+namespace clang {
+namespace tidy {
+namespace llvm {
+
+/// Finds and fixes header guards that do not adhere to LLVM style.
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/llvm-header-guard.html
+/// The check supports these options:
+///   - `HeaderFileExtensions`: a comma-separated list of filename extensions of
+///     header files (The filename extension should not contain "." prefix).
+///     ",h,hh,hpp,hxx" by default.
+///     For extension-less header files, using an empty string or leaving an
+///     empty string between "," if there are other filename extensions.
+class LLVMHeaderGuardCheck : public utils::HeaderGuardCheck {
+public:
+  LLVMHeaderGuardCheck(StringRef Name, ClangTidyContext *Context);
+
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+  bool shouldSuggestEndifComment(StringRef Filename) override { return false; }
+  bool shouldFixHeaderGuard(StringRef Filename) override;
+  std::string getHeaderGuard(StringRef Filename, StringRef OldGuard) override;
+
+private:
+  std::string RawStringHeaderFileExtensions;
+  utils::HeaderFileExtensionsSet HeaderFileExtensions;
+};
+
+} // namespace llvm
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVM_HEADER_GUARD_CHECK_H
diff --git a/clang-tidy/llvm/IncludeOrderCheck.cpp b/clang-tidy/llvm/IncludeOrderCheck.cpp
new file mode 100644 (file)
index 0000000..c277a45
--- /dev/null
@@ -0,0 +1,179 @@
+//===--- IncludeOrderCheck.cpp - clang-tidy -------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "IncludeOrderCheck.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Lex/PPCallbacks.h"
+#include "clang/Lex/Preprocessor.h"
+
+#include <map>
+
+namespace clang {
+namespace tidy {
+namespace llvm {
+
+namespace {
+class IncludeOrderPPCallbacks : public PPCallbacks {
+public:
+  explicit IncludeOrderPPCallbacks(ClangTidyCheck &Check, SourceManager &SM)
+      : LookForMainModule(true), Check(Check), SM(SM) {}
+
+  void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
+                          StringRef FileName, bool IsAngled,
+                          CharSourceRange FilenameRange, const FileEntry *File,
+                          StringRef SearchPath, StringRef RelativePath,
+                          const Module *Imported) override;
+  void EndOfMainFile() override;
+
+private:
+  struct IncludeDirective {
+    SourceLocation Loc;    ///< '#' location in the include directive
+    CharSourceRange Range; ///< SourceRange for the file name
+    std::string Filename;  ///< Filename as a string
+    bool IsAngled;         ///< true if this was an include with angle brackets
+    bool IsMainModule;     ///< true if this was the first include in a file
+  };
+
+  typedef std::vector<IncludeDirective> FileIncludes;
+  std::map<clang::FileID, FileIncludes> IncludeDirectives;
+  bool LookForMainModule;
+
+  ClangTidyCheck &Check;
+  SourceManager &SM;
+};
+} // namespace
+
+void IncludeOrderCheck::registerPPCallbacks(CompilerInstance &Compiler) {
+  Compiler.getPreprocessor().addPPCallbacks(
+      ::llvm::make_unique<IncludeOrderPPCallbacks>(
+          *this, Compiler.getSourceManager()));
+}
+
+static int getPriority(StringRef Filename, bool IsAngled, bool IsMainModule) {
+  // We leave the main module header at the top.
+  if (IsMainModule)
+    return 0;
+
+  // LLVM and clang headers are in the penultimate position.
+  if (Filename.startswith("llvm/") || Filename.startswith("llvm-c/") ||
+      Filename.startswith("clang/") || Filename.startswith("clang-c/"))
+    return 2;
+
+  // System headers are sorted to the end.
+  if (IsAngled || Filename.startswith("gtest/"))
+    return 3;
+
+  // Other headers are inserted between the main module header and LLVM headers.
+  return 1;
+}
+
+void IncludeOrderPPCallbacks::InclusionDirective(
+    SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName,
+    bool IsAngled, CharSourceRange FilenameRange, const FileEntry *File,
+    StringRef SearchPath, StringRef RelativePath, const Module *Imported) {
+  // We recognize the first include as a special main module header and want
+  // to leave it in the top position.
+  IncludeDirective ID = {HashLoc, FilenameRange, FileName, IsAngled, false};
+  if (LookForMainModule && !IsAngled) {
+    ID.IsMainModule = true;
+    LookForMainModule = false;
+  }
+
+  // Bucket the include directives by the id of the file they were declared in.
+  IncludeDirectives[SM.getFileID(HashLoc)].push_back(std::move(ID));
+}
+
+void IncludeOrderPPCallbacks::EndOfMainFile() {
+  LookForMainModule = true;
+  if (IncludeDirectives.empty())
+    return;
+
+  // TODO: find duplicated includes.
+
+  // Form blocks of includes. We don't want to sort across blocks. This also
+  // implicitly makes us never reorder over #defines or #if directives.
+  // FIXME: We should be more careful about sorting below comments as we don't
+  // know if the comment refers to the next include or the whole block that
+  // follows.
+  for (auto &Bucket : IncludeDirectives) {
+    auto &FileDirectives = Bucket.second;
+    std::vector<unsigned> Blocks(1, 0);
+    for (unsigned I = 1, E = FileDirectives.size(); I != E; ++I)
+      if (SM.getExpansionLineNumber(FileDirectives[I].Loc) !=
+          SM.getExpansionLineNumber(FileDirectives[I - 1].Loc) + 1)
+        Blocks.push_back(I);
+    Blocks.push_back(FileDirectives.size()); // Sentinel value.
+
+    // Get a vector of indices.
+    std::vector<unsigned> IncludeIndices;
+    for (unsigned I = 0, E = FileDirectives.size(); I != E; ++I)
+      IncludeIndices.push_back(I);
+
+    // Sort the includes. We first sort by priority, then lexicographically.
+    for (unsigned BI = 0, BE = Blocks.size() - 1; BI != BE; ++BI)
+      std::sort(IncludeIndices.begin() + Blocks[BI],
+                IncludeIndices.begin() + Blocks[BI + 1],
+                [&FileDirectives](unsigned LHSI, unsigned RHSI) {
+                  IncludeDirective &LHS = FileDirectives[LHSI];
+                  IncludeDirective &RHS = FileDirectives[RHSI];
+
+                  int PriorityLHS =
+                      getPriority(LHS.Filename, LHS.IsAngled, LHS.IsMainModule);
+                  int PriorityRHS =
+                      getPriority(RHS.Filename, RHS.IsAngled, RHS.IsMainModule);
+
+                  return std::tie(PriorityLHS, LHS.Filename) <
+                         std::tie(PriorityRHS, RHS.Filename);
+                });
+
+    // Emit a warning for each block and fixits for all changes within that
+    // block.
+    for (unsigned BI = 0, BE = Blocks.size() - 1; BI != BE; ++BI) {
+      // Find the first include that's not in the right position.
+      unsigned I, E;
+      for (I = Blocks[BI], E = Blocks[BI + 1]; I != E; ++I)
+        if (IncludeIndices[I] != I)
+          break;
+
+      if (I == E)
+        continue;
+
+      // Emit a warning.
+      auto D = Check.diag(FileDirectives[I].Loc,
+                          "#includes are not sorted properly");
+
+      // Emit fix-its for all following includes in this block.
+      for (; I != E; ++I) {
+        if (IncludeIndices[I] == I)
+          continue;
+        const IncludeDirective &CopyFrom = FileDirectives[IncludeIndices[I]];
+
+        SourceLocation FromLoc = CopyFrom.Range.getBegin();
+        const char *FromData = SM.getCharacterData(FromLoc);
+        unsigned FromLen = std::strcspn(FromData, "\n");
+
+        StringRef FixedName(FromData, FromLen);
+
+        SourceLocation ToLoc = FileDirectives[I].Range.getBegin();
+        const char *ToData = SM.getCharacterData(ToLoc);
+        unsigned ToLen = std::strcspn(ToData, "\n");
+        auto ToRange =
+            CharSourceRange::getCharRange(ToLoc, ToLoc.getLocWithOffset(ToLen));
+
+        D << FixItHint::CreateReplacement(ToRange, FixedName);
+      }
+    }
+  }
+
+  IncludeDirectives.clear();
+}
+
+} // namespace llvm
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/llvm/IncludeOrderCheck.h b/clang-tidy/llvm/IncludeOrderCheck.h
new file mode 100644 (file)
index 0000000..ad876b9
--- /dev/null
@@ -0,0 +1,33 @@
+//===--- IncludeOrderCheck.h - clang-tidy -----------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVM_INCLUDE_ORDER_CHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVM_INCLUDE_ORDER_CHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace llvm {
+
+/// Checks the correct order of `#includes`.
+///
+/// See http://llvm.org/docs/CodingStandards.html#include-style
+class IncludeOrderCheck : public ClangTidyCheck {
+public:
+  IncludeOrderCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerPPCallbacks(CompilerInstance &Compiler) override;
+};
+
+} // namespace llvm
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVM_INCLUDE_ORDER_CHECK_H
diff --git a/clang-tidy/llvm/LLVMTidyModule.cpp b/clang-tidy/llvm/LLVMTidyModule.cpp
new file mode 100644 (file)
index 0000000..ea46ca9
--- /dev/null
@@ -0,0 +1,44 @@
+//===--- LLVMTidyModule.cpp - clang-tidy ----------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "../ClangTidy.h"
+#include "../ClangTidyModule.h"
+#include "../ClangTidyModuleRegistry.h"
+#include "../readability/NamespaceCommentCheck.h"
+#include "HeaderGuardCheck.h"
+#include "IncludeOrderCheck.h"
+#include "TwineLocalCheck.h"
+
+namespace clang {
+namespace tidy {
+namespace llvm {
+
+class LLVMModule : public ClangTidyModule {
+public:
+  void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
+    CheckFactories.registerCheck<LLVMHeaderGuardCheck>("llvm-header-guard");
+    CheckFactories.registerCheck<IncludeOrderCheck>("llvm-include-order");
+    CheckFactories.registerCheck<readability::NamespaceCommentCheck>(
+        "llvm-namespace-comment");
+    CheckFactories.registerCheck<TwineLocalCheck>("llvm-twine-local");
+  }
+};
+
+// Register the LLVMTidyModule using this statically initialized variable.
+static ClangTidyModuleRegistry::Add<LLVMModule> X("llvm-module",
+                                                  "Adds LLVM lint checks.");
+
+} // namespace llvm
+
+// This anchor is used to force the linker to link in the generated object file
+// and thus register the LLVMModule.
+volatile int LLVMModuleAnchorSource = 0;
+
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/llvm/TwineLocalCheck.cpp b/clang-tidy/llvm/TwineLocalCheck.cpp
new file mode 100644 (file)
index 0000000..67c85a6
--- /dev/null
@@ -0,0 +1,66 @@
+//===--- TwineLocalCheck.cpp - clang-tidy ---------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "TwineLocalCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace llvm {
+
+void TwineLocalCheck::registerMatchers(MatchFinder *Finder) {
+  auto TwineType =
+      qualType(hasDeclaration(recordDecl(hasName("::llvm::Twine"))));
+  Finder->addMatcher(varDecl(hasType(TwineType)).bind("variable"), this);
+}
+
+void TwineLocalCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *VD = Result.Nodes.getNodeAs<VarDecl>("variable");
+  auto Diag = diag(VD->getLocation(),
+                   "twine variables are prone to use-after-free bugs");
+
+  // If this VarDecl has an initializer try to fix it.
+  if (VD->hasInit()) {
+    // Peel away implicit constructors and casts so we can see the actual type
+    // of the initializer.
+    const Expr *C = VD->getInit()->IgnoreImplicit();
+
+    while (isa<CXXConstructExpr>(C)) {
+      if (cast<CXXConstructExpr>(C)->getNumArgs() == 0)
+        break;
+      C = cast<CXXConstructExpr>(C)->getArg(0)->IgnoreParenImpCasts();
+    }
+
+    SourceRange TypeRange =
+        VD->getTypeSourceInfo()->getTypeLoc().getSourceRange();
+
+    // A real Twine, turn it into a std::string.
+    if (VD->getType()->getCanonicalTypeUnqualified() ==
+        C->getType()->getCanonicalTypeUnqualified()) {
+      SourceLocation EndLoc = Lexer::getLocForEndOfToken(
+          VD->getInit()->getLocEnd(), 0, *Result.SourceManager, getLangOpts());
+      Diag << FixItHint::CreateReplacement(TypeRange, "std::string")
+           << FixItHint::CreateInsertion(VD->getInit()->getLocStart(), "(")
+           << FixItHint::CreateInsertion(EndLoc, ").str()");
+    } else {
+      // Just an implicit conversion. Insert the real type.
+      Diag << FixItHint::CreateReplacement(
+          TypeRange,
+          C->getType().getAsString(Result.Context->getPrintingPolicy()));
+    }
+  }
+}
+
+} // namespace llvm
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/llvm/TwineLocalCheck.h b/clang-tidy/llvm/TwineLocalCheck.h
new file mode 100644 (file)
index 0000000..9f7979b
--- /dev/null
@@ -0,0 +1,33 @@
+//===--- TwineLocalCheck.h - clang-tidy -------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVM_TWINE_LOCAL_CHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVM_TWINE_LOCAL_CHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace llvm {
+
+/// Looks for local `Twine` variables which are prone to use after frees and
+/// should be generally avoided.
+class TwineLocalCheck : public ClangTidyCheck {
+public:
+  TwineLocalCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace llvm
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVM_TWINE_LOCAL_CHECK_H
diff --git a/clang-tidy/misc/ArgumentCommentCheck.cpp b/clang-tidy/misc/ArgumentCommentCheck.cpp
new file mode 100644 (file)
index 0000000..d7ac9f3
--- /dev/null
@@ -0,0 +1,308 @@
+//===--- ArgumentCommentCheck.cpp - clang-tidy ----------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ArgumentCommentCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Lex/Token.h"
+#include "../utils/LexerUtils.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+ArgumentCommentCheck::ArgumentCommentCheck(StringRef Name,
+                                           ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      StrictMode(Options.getLocalOrGlobal("StrictMode", 0) != 0),
+      IdentRE("^(/\\* *)([_A-Za-z][_A-Za-z0-9]*)( *= *\\*/)$") {}
+
+void ArgumentCommentCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "StrictMode", StrictMode);
+}
+
+void ArgumentCommentCheck::registerMatchers(MatchFinder *Finder) {
+  Finder->addMatcher(
+      callExpr(unless(cxxOperatorCallExpr()),
+               // NewCallback's arguments relate to the pointed function, don't
+               // check them against NewCallback's parameter names.
+               // FIXME: Make this configurable.
+               unless(hasDeclaration(functionDecl(
+                   hasAnyName("NewCallback", "NewPermanentCallback")))))
+          .bind("expr"),
+      this);
+  Finder->addMatcher(cxxConstructExpr().bind("expr"), this);
+}
+
+static std::vector<std::pair<SourceLocation, StringRef>>
+getCommentsInRange(ASTContext *Ctx, CharSourceRange Range) {
+  std::vector<std::pair<SourceLocation, StringRef>> Comments;
+  auto &SM = Ctx->getSourceManager();
+  std::pair<FileID, unsigned> BeginLoc = SM.getDecomposedLoc(Range.getBegin()),
+                              EndLoc = SM.getDecomposedLoc(Range.getEnd());
+
+  if (BeginLoc.first != EndLoc.first)
+    return Comments;
+
+  bool Invalid = false;
+  StringRef Buffer = SM.getBufferData(BeginLoc.first, &Invalid);
+  if (Invalid)
+    return Comments;
+
+  const char *StrData = Buffer.data() + BeginLoc.second;
+
+  Lexer TheLexer(SM.getLocForStartOfFile(BeginLoc.first), Ctx->getLangOpts(),
+                 Buffer.begin(), StrData, Buffer.end());
+  TheLexer.SetCommentRetentionState(true);
+
+  while (true) {
+    Token Tok;
+    if (TheLexer.LexFromRawLexer(Tok))
+      break;
+    if (Tok.getLocation() == Range.getEnd() || Tok.is(tok::eof))
+      break;
+
+    if (Tok.is(tok::comment)) {
+      std::pair<FileID, unsigned> CommentLoc =
+          SM.getDecomposedLoc(Tok.getLocation());
+      assert(CommentLoc.first == BeginLoc.first);
+      Comments.emplace_back(
+          Tok.getLocation(),
+          StringRef(Buffer.begin() + CommentLoc.second, Tok.getLength()));
+    } else {
+      // Clear comments found before the different token, e.g. comma.
+      Comments.clear();
+    }
+  }
+
+  return Comments;
+}
+
+static std::vector<std::pair<SourceLocation, StringRef>>
+getCommentsBeforeLoc(ASTContext *Ctx, SourceLocation Loc) {
+  std::vector<std::pair<SourceLocation, StringRef>> Comments;
+  while (Loc.isValid()) {
+    clang::Token Tok =
+        utils::lexer::getPreviousToken(*Ctx, Loc, /*SkipComments=*/false);
+    if (Tok.isNot(tok::comment))
+      break;
+    Loc = Tok.getLocation();
+    Comments.emplace_back(
+        Loc,
+        Lexer::getSourceText(CharSourceRange::getCharRange(
+                                 Loc, Loc.getLocWithOffset(Tok.getLength())),
+                             Ctx->getSourceManager(), Ctx->getLangOpts()));
+  }
+  return Comments;
+}
+
+static bool isLikelyTypo(llvm::ArrayRef<ParmVarDecl *> Params,
+                         StringRef ArgName, unsigned ArgIndex) {
+  std::string ArgNameLowerStr = ArgName.lower();
+  StringRef ArgNameLower = ArgNameLowerStr;
+  // The threshold is arbitrary.
+  unsigned UpperBound = (ArgName.size() + 2) / 3 + 1;
+  unsigned ThisED = ArgNameLower.edit_distance(
+      Params[ArgIndex]->getIdentifier()->getName().lower(),
+      /*AllowReplacements=*/true, UpperBound);
+  if (ThisED >= UpperBound)
+    return false;
+
+  for (unsigned I = 0, E = Params.size(); I != E; ++I) {
+    if (I == ArgIndex)
+      continue;
+    IdentifierInfo *II = Params[I]->getIdentifier();
+    if (!II)
+      continue;
+
+    const unsigned Threshold = 2;
+    // Other parameters must be an edit distance at least Threshold more away
+    // from this parameter. This gives us greater confidence that this is a typo
+    // of this parameter and not one with a similar name.
+    unsigned OtherED = ArgNameLower.edit_distance(II->getName().lower(),
+                                                  /*AllowReplacements=*/true,
+                                                  ThisED + Threshold);
+    if (OtherED < ThisED + Threshold)
+      return false;
+  }
+
+  return true;
+}
+
+static bool sameName(StringRef InComment, StringRef InDecl, bool StrictMode) {
+  if (StrictMode)
+    return InComment == InDecl;
+  InComment = InComment.trim('_');
+  InDecl = InDecl.trim('_');
+  // FIXME: compare_lower only works for ASCII.
+  return InComment.compare_lower(InDecl) == 0;
+}
+
+static bool looksLikeExpectMethod(const CXXMethodDecl *Expect) {
+  return Expect != nullptr && Expect->getLocation().isMacroID() &&
+         Expect->getNameInfo().getName().isIdentifier() &&
+         Expect->getName().startswith("gmock_");
+}
+static bool areMockAndExpectMethods(const CXXMethodDecl *Mock,
+                                    const CXXMethodDecl *Expect) {
+  assert(looksLikeExpectMethod(Expect));
+  return Mock != nullptr && Mock->getNextDeclInContext() == Expect &&
+         Mock->getNumParams() == Expect->getNumParams() &&
+         Mock->getLocation().isMacroID() &&
+         Mock->getNameInfo().getName().isIdentifier() &&
+         Mock->getName() == Expect->getName().substr(strlen("gmock_"));
+}
+
+// This uses implementation details of MOCK_METHODx_ macros: for each mocked
+// method M it defines M() with appropriate signature and a method used to set
+// up expectations - gmock_M() - with each argument's type changed the
+// corresponding matcher. This function returns M when given either M or
+// gmock_M.
+static const CXXMethodDecl *findMockedMethod(const CXXMethodDecl *Method) {
+  if (looksLikeExpectMethod(Method)) {
+    const DeclContext *Ctx = Method->getDeclContext();
+    if (Ctx == nullptr || !Ctx->isRecord())
+      return nullptr;
+    for (const auto *D : Ctx->decls()) {
+      if (D->getNextDeclInContext() == Method) {
+        const auto *Previous = dyn_cast<CXXMethodDecl>(D);
+        return areMockAndExpectMethods(Previous, Method) ? Previous : nullptr;
+      }
+    }
+    return nullptr;
+  }
+  if (const auto *Next = dyn_cast_or_null<CXXMethodDecl>(
+                 Method->getNextDeclInContext())) {
+    if (looksLikeExpectMethod(Next) && areMockAndExpectMethods(Method, Next))
+      return Method;
+  }
+  return nullptr;
+}
+
+// For gmock expectation builder method (the target of the call generated by
+// `EXPECT_CALL(obj, Method(...))`) tries to find the real method being mocked
+// (returns nullptr, if the mock method doesn't override anything). For other
+// functions returns the function itself.
+static const FunctionDecl *resolveMocks(const FunctionDecl *Func) {
+  if (const auto *Method = dyn_cast<CXXMethodDecl>(Func)) {
+    if (const auto *MockedMethod = findMockedMethod(Method)) {
+      // If mocked method overrides the real one, we can use its parameter
+      // names, otherwise we're out of luck.
+      if (MockedMethod->size_overridden_methods() > 0) {
+        return *MockedMethod->begin_overridden_methods();
+      }
+      return nullptr;
+    }
+  }
+  return Func;
+}
+
+void ArgumentCommentCheck::checkCallArgs(ASTContext *Ctx,
+                                         const FunctionDecl *OriginalCallee,
+                                         SourceLocation ArgBeginLoc,
+                                         llvm::ArrayRef<const Expr *> Args) {
+  const FunctionDecl *Callee = resolveMocks(OriginalCallee);
+  if (!Callee)
+    return;
+
+  Callee = Callee->getFirstDecl();
+  unsigned NumArgs = std::min<unsigned>(Args.size(), Callee->getNumParams());
+  if (NumArgs == 0)
+    return;
+
+  auto makeFileCharRange = [Ctx](SourceLocation Begin, SourceLocation End) {
+    return Lexer::makeFileCharRange(CharSourceRange::getCharRange(Begin, End),
+                                    Ctx->getSourceManager(),
+                                    Ctx->getLangOpts());
+  };
+
+  for (unsigned I = 0; I < NumArgs; ++I) {
+    const ParmVarDecl *PVD = Callee->getParamDecl(I);
+    IdentifierInfo *II = PVD->getIdentifier();
+    if (!II)
+      continue;
+    if (auto Template = Callee->getTemplateInstantiationPattern()) {
+      // Don't warn on arguments for parameters instantiated from template
+      // parameter packs. If we find more arguments than the template
+      // definition has, it also means that they correspond to a parameter
+      // pack.
+      if (Template->getNumParams() <= I ||
+          Template->getParamDecl(I)->isParameterPack()) {
+        continue;
+      }
+    }
+
+    CharSourceRange BeforeArgument =
+        makeFileCharRange(ArgBeginLoc, Args[I]->getLocStart());
+    ArgBeginLoc = Args[I]->getLocEnd();
+
+    std::vector<std::pair<SourceLocation, StringRef>> Comments;
+    if (BeforeArgument.isValid()) {
+      Comments = getCommentsInRange(Ctx, BeforeArgument);
+    } else {
+      // Fall back to parsing back from the start of the argument.
+      CharSourceRange ArgsRange = makeFileCharRange(
+          Args[I]->getLocStart(), Args[NumArgs - 1]->getLocEnd());
+      Comments = getCommentsBeforeLoc(Ctx, ArgsRange.getBegin());
+    }
+
+    for (auto Comment : Comments) {
+      llvm::SmallVector<StringRef, 2> Matches;
+      if (IdentRE.match(Comment.second, &Matches) &&
+          !sameName(Matches[2], II->getName(), StrictMode)) {
+        {
+          DiagnosticBuilder Diag =
+              diag(Comment.first, "argument name '%0' in comment does not "
+                                  "match parameter name %1")
+              << Matches[2] << II;
+          if (isLikelyTypo(Callee->parameters(), Matches[2], I)) {
+            Diag << FixItHint::CreateReplacement(
+                Comment.first, (Matches[1] + II->getName() + Matches[3]).str());
+          }
+        }
+        diag(PVD->getLocation(), "%0 declared here", DiagnosticIDs::Note) << II;
+        if (OriginalCallee != Callee) {
+          diag(OriginalCallee->getLocation(),
+               "actual callee (%0) is declared here", DiagnosticIDs::Note)
+              << OriginalCallee;
+        }
+      }
+    }
+  }
+}
+
+void ArgumentCommentCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *E = Result.Nodes.getNodeAs<Expr>("expr");
+  if (const auto *Call = dyn_cast<CallExpr>(E)) {
+    const FunctionDecl *Callee = Call->getDirectCallee();
+    if (!Callee)
+      return;
+
+    checkCallArgs(Result.Context, Callee, Call->getCallee()->getLocEnd(),
+                  llvm::makeArrayRef(Call->getArgs(), Call->getNumArgs()));
+  } else {
+    const auto *Construct = cast<CXXConstructExpr>(E);
+    if (Construct->getNumArgs() == 1 &&
+        Construct->getArg(0)->getSourceRange() == Construct->getSourceRange()) {
+      // Ignore implicit construction.
+      return;
+    }
+    checkCallArgs(
+        Result.Context, Construct->getConstructor(),
+        Construct->getParenOrBraceRange().getBegin(),
+        llvm::makeArrayRef(Construct->getArgs(), Construct->getNumArgs()));
+  }
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/misc/ArgumentCommentCheck.h b/clang-tidy/misc/ArgumentCommentCheck.h
new file mode 100644 (file)
index 0000000..50dda2b
--- /dev/null
@@ -0,0 +1,55 @@
+//===--- ArgumentCommentCheck.h - clang-tidy --------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_ARGUMENTCOMMENTCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_ARGUMENTCOMMENTCHECK_H
+
+#include "../ClangTidy.h"
+#include "llvm/Support/Regex.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Checks that argument comments match parameter names.
+///
+/// The check understands argument comments in the form `/*parameter_name=*/`
+/// that are placed right before the argument.
+///
+/// \code
+///   void f(bool foo);
+///
+///   ...
+///   f(/*bar=*/true);
+///   // warning: argument name 'bar' in comment does not match parameter name 'foo'
+/// \endcode
+///
+/// The check tries to detect typos and suggest automated fixes for them.
+class ArgumentCommentCheck : public ClangTidyCheck {
+public:
+  ArgumentCommentCheck(StringRef Name, ClangTidyContext *Context);
+
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+
+private:
+  const bool StrictMode;
+  llvm::Regex IdentRE;
+
+  void checkCallArgs(ASTContext *Ctx, const FunctionDecl *Callee,
+                     SourceLocation ArgBeginLoc,
+                     llvm::ArrayRef<const Expr *> Args);
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_ARGUMENTCOMMENTCHECK_H
diff --git a/clang-tidy/misc/AssertSideEffectCheck.cpp b/clang-tidy/misc/AssertSideEffectCheck.cpp
new file mode 100644 (file)
index 0000000..0b63c0d
--- /dev/null
@@ -0,0 +1,127 @@
+//===--- AssertSideEffectCheck.cpp - clang-tidy ---------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "AssertSideEffectCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Lex/Lexer.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Casting.h"
+#include <algorithm>
+#include <string>
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+namespace {
+
+AST_MATCHER_P(Expr, hasSideEffect, bool, CheckFunctionCalls) {
+  const Expr *E = &Node;
+
+  if (const auto *Op = dyn_cast<UnaryOperator>(E)) {
+    UnaryOperator::Opcode OC = Op->getOpcode();
+    return OC == UO_PostInc || OC == UO_PostDec || OC == UO_PreInc ||
+           OC == UO_PreDec;
+  }
+
+  if (const auto *Op = dyn_cast<BinaryOperator>(E)) {
+    return Op->isAssignmentOp();
+  }
+
+  if (const auto *OpCallExpr = dyn_cast<CXXOperatorCallExpr>(E)) {
+    OverloadedOperatorKind OpKind = OpCallExpr->getOperator();
+    return OpKind == OO_Equal || OpKind == OO_PlusEqual ||
+           OpKind == OO_MinusEqual || OpKind == OO_StarEqual ||
+           OpKind == OO_SlashEqual || OpKind == OO_AmpEqual ||
+           OpKind == OO_PipeEqual || OpKind == OO_CaretEqual ||
+           OpKind == OO_LessLessEqual || OpKind == OO_GreaterGreaterEqual ||
+           OpKind == OO_PlusPlus || OpKind == OO_MinusMinus ||
+           OpKind == OO_PercentEqual || OpKind == OO_New ||
+           OpKind == OO_Delete || OpKind == OO_Array_New ||
+           OpKind == OO_Array_Delete;
+  }
+
+  if (const auto *CExpr = dyn_cast<CallExpr>(E)) {
+    bool Result = CheckFunctionCalls;
+    if (const auto *FuncDecl = CExpr->getDirectCallee()) {
+      if (FuncDecl->getDeclName().isIdentifier() &&
+          FuncDecl->getName() == "__builtin_expect") // exceptions come here
+        Result = false;
+      else if (const auto *MethodDecl = dyn_cast<CXXMethodDecl>(FuncDecl))
+        Result &= !MethodDecl->isConst();
+    }
+    return Result;
+  }
+
+  return isa<CXXNewExpr>(E) || isa<CXXDeleteExpr>(E) || isa<CXXThrowExpr>(E);
+}
+
+} // namespace
+
+AssertSideEffectCheck::AssertSideEffectCheck(StringRef Name,
+                                             ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      CheckFunctionCalls(Options.get("CheckFunctionCalls", false)),
+      RawAssertList(Options.get("AssertMacros", "assert")) {
+  StringRef(RawAssertList).split(AssertMacros, ",", -1, false);
+}
+
+// The options are explained in AssertSideEffectCheck.h.
+void AssertSideEffectCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "CheckFunctionCalls", CheckFunctionCalls);
+  Options.store(Opts, "AssertMacros", RawAssertList);
+}
+
+void AssertSideEffectCheck::registerMatchers(MatchFinder *Finder) {
+  auto DescendantWithSideEffect =
+      hasDescendant(expr(hasSideEffect(CheckFunctionCalls)));
+  auto ConditionWithSideEffect = hasCondition(DescendantWithSideEffect);
+  Finder->addMatcher(
+      stmt(
+          anyOf(conditionalOperator(ConditionWithSideEffect),
+                ifStmt(ConditionWithSideEffect),
+                unaryOperator(hasOperatorName("!"),
+                              hasUnaryOperand(unaryOperator(
+                                  hasOperatorName("!"),
+                                  hasUnaryOperand(DescendantWithSideEffect))))))
+          .bind("condStmt"),
+      this);
+}
+
+void AssertSideEffectCheck::check(const MatchFinder::MatchResult &Result) {
+  const SourceManager &SM = *Result.SourceManager;
+  const LangOptions LangOpts = getLangOpts();
+  SourceLocation Loc = Result.Nodes.getNodeAs<Stmt>("condStmt")->getLocStart();
+
+  StringRef AssertMacroName;
+  while (Loc.isValid() && Loc.isMacroID()) {
+    StringRef MacroName = Lexer::getImmediateMacroName(Loc, SM, LangOpts);
+
+    // Check if this macro is an assert.
+    if (std::find(AssertMacros.begin(), AssertMacros.end(), MacroName) !=
+        AssertMacros.end()) {
+      AssertMacroName = MacroName;
+      break;
+    }
+    Loc = SM.getImmediateMacroCallerLoc(Loc);
+  }
+  if (AssertMacroName.empty())
+    return;
+
+  diag(Loc, "found %0() with side effect") << AssertMacroName;
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/misc/AssertSideEffectCheck.h b/clang-tidy/misc/AssertSideEffectCheck.h
new file mode 100644 (file)
index 0000000..2bb25e2
--- /dev/null
@@ -0,0 +1,52 @@
+//===--- AssertSideEffectCheck.h - clang-tidy -------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_ASSERTSIDEEFFECTCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_ASSERTSIDEEFFECTCHECK_H
+
+#include "../ClangTidy.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+#include <string>
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Finds `assert()` with side effect.
+///
+/// The condition of `assert()` is evaluated only in debug builds so a
+/// condition with side effect can cause different behavior in debug / release
+/// builds.
+///
+/// There are two options:
+///
+///   - `AssertMacros`: A comma-separated list of the names of assert macros to
+///     be checked.
+///   - `CheckFunctionCalls`: Whether to treat non-const member and non-member
+///     functions as they produce side effects. Disabled by default because it
+///     can increase the number of false positive warnings.
+class AssertSideEffectCheck : public ClangTidyCheck {
+public:
+  AssertSideEffectCheck(StringRef Name, ClangTidyContext *Context);
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+  const bool CheckFunctionCalls;
+  const std::string RawAssertList;
+  SmallVector<StringRef, 5> AssertMacros;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_ASSERTSIDEEFFECTCHECK_H
diff --git a/clang-tidy/misc/BoolPointerImplicitConversionCheck.cpp b/clang-tidy/misc/BoolPointerImplicitConversionCheck.cpp
new file mode 100644 (file)
index 0000000..f831125
--- /dev/null
@@ -0,0 +1,73 @@
+//===--- BoolPointerImplicitConversionCheck.cpp - clang-tidy --------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "BoolPointerImplicitConversionCheck.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+void BoolPointerImplicitConversionCheck::registerMatchers(MatchFinder *Finder) {
+  // Look for ifs that have an implicit bool* to bool conversion in the
+  // condition. Filter negations.
+  Finder->addMatcher(
+      ifStmt(hasCondition(findAll(implicitCastExpr(
+                 allOf(unless(hasParent(unaryOperator(hasOperatorName("!")))),
+                       hasSourceExpression(expr(
+                           hasType(pointerType(pointee(booleanType()))),
+                           ignoringParenImpCasts(declRefExpr().bind("expr")))),
+                       hasCastKind(CK_PointerToBoolean))))),
+             unless(isInTemplateInstantiation()))
+          .bind("if"),
+      this);
+}
+
+void BoolPointerImplicitConversionCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  auto *If = Result.Nodes.getNodeAs<IfStmt>("if");
+  auto *Var = Result.Nodes.getNodeAs<DeclRefExpr>("expr");
+
+  // Ignore macros.
+  if (Var->getLocStart().isMacroID())
+    return;
+
+  // Only allow variable accesses for now, no function calls or member exprs.
+  // Check that we don't dereference the variable anywhere within the if. This
+  // avoids false positives for checks of the pointer for nullptr before it is
+  // dereferenced. If there is a dereferencing operator on this variable don't
+  // emit a diagnostic. Also ignore array subscripts.
+  const Decl *D = Var->getDecl();
+  auto DeclRef = ignoringParenImpCasts(declRefExpr(to(equalsNode(D))));
+  if (!match(findAll(
+                 unaryOperator(hasOperatorName("*"), hasUnaryOperand(DeclRef))),
+             *If, *Result.Context)
+           .empty() ||
+      !match(findAll(arraySubscriptExpr(hasBase(DeclRef))), *If,
+             *Result.Context)
+           .empty() ||
+      // FIXME: We should still warn if the paremater is implicitly converted to
+      // bool.
+      !match(findAll(callExpr(hasAnyArgument(ignoringParenImpCasts(DeclRef)))),
+             *If, *Result.Context)
+           .empty() ||
+      !match(findAll(cxxDeleteExpr(has(ignoringParenImpCasts(expr(DeclRef))))),
+             *If, *Result.Context)
+           .empty())
+    return;
+
+  diag(Var->getLocStart(), "dubious check of 'bool *' against 'nullptr', did "
+                           "you mean to dereference it?")
+      << FixItHint::CreateInsertion(Var->getLocStart(), "*");
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/misc/BoolPointerImplicitConversionCheck.h b/clang-tidy/misc/BoolPointerImplicitConversionCheck.h
new file mode 100644 (file)
index 0000000..d8a90f3
--- /dev/null
@@ -0,0 +1,42 @@
+//===--- BoolPointerImplicitConversionCheck.h - clang-tidy ------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_BOOLPOINTERIMPLICITCONVERSIONCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_BOOLPOINTERIMPLICITCONVERSIONCHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Checks for conditions based on implicit conversion from a bool pointer to
+/// bool.
+///
+/// Example:
+///
+/// \code
+///   bool *p;
+///   if (p) {
+///     // Never used in a pointer-specific way.
+///   }
+/// \endcode
+class BoolPointerImplicitConversionCheck : public ClangTidyCheck {
+public:
+  BoolPointerImplicitConversionCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_BOOLPOINTERIMPLICITCONVERSIONCHECK_H
diff --git a/clang-tidy/misc/CMakeLists.txt b/clang-tidy/misc/CMakeLists.txt
new file mode 100644 (file)
index 0000000..9e74387
--- /dev/null
@@ -0,0 +1,61 @@
+set(LLVM_LINK_COMPONENTS support)
+
+add_clang_library(clangTidyMiscModule
+  ArgumentCommentCheck.cpp
+  AssertSideEffectCheck.cpp
+  ForwardingReferenceOverloadCheck.cpp
+  LambdaFunctionNameCheck.cpp
+  MisplacedConstCheck.cpp
+  UnconventionalAssignOperatorCheck.cpp
+  BoolPointerImplicitConversionCheck.cpp
+  DanglingHandleCheck.cpp
+  DefinitionsInHeadersCheck.cpp
+  FoldInitTypeCheck.cpp
+  ForwardDeclarationNamespaceCheck.cpp
+  InaccurateEraseCheck.cpp
+  IncorrectRoundings.cpp
+  InefficientAlgorithmCheck.cpp
+  MacroParenthesesCheck.cpp
+  MacroRepeatedSideEffectsCheck.cpp
+  MiscTidyModule.cpp
+  MisplacedWideningCastCheck.cpp
+  MoveConstantArgumentCheck.cpp
+  MoveConstructorInitCheck.cpp
+  MoveForwardingReferenceCheck.cpp
+  MultipleStatementMacroCheck.cpp
+  NewDeleteOverloadsCheck.cpp
+  NoexceptMoveConstructorCheck.cpp
+  NonCopyableObjects.cpp
+  RedundantExpressionCheck.cpp
+  SizeofContainerCheck.cpp
+  SizeofExpressionCheck.cpp
+  StaticAssertCheck.cpp
+  StringCompareCheck.cpp
+  StringConstructorCheck.cpp
+  StringIntegerAssignmentCheck.cpp
+  StringLiteralWithEmbeddedNulCheck.cpp
+  SuspiciousEnumUsageCheck.cpp
+  SuspiciousMissingCommaCheck.cpp
+  SuspiciousSemicolonCheck.cpp
+  SuspiciousStringCompareCheck.cpp
+  SwappedArgumentsCheck.cpp
+  ThrowByValueCatchByReferenceCheck.cpp
+  UndelegatedConstructor.cpp
+  UniqueptrResetReleaseCheck.cpp
+  UnusedAliasDeclsCheck.cpp
+  UnusedParametersCheck.cpp
+  UnusedRAIICheck.cpp
+  UnusedUsingDeclsCheck.cpp
+  UseAfterMoveCheck.cpp
+  VirtualNearMissCheck.cpp
+
+  LINK_LIBS
+  clangAnalysis
+  clangAST
+  clangASTMatchers
+  clangBasic
+  clangLex
+  clangTidy
+  clangTidyUtils
+  clangTooling
+  )
diff --git a/clang-tidy/misc/DanglingHandleCheck.cpp b/clang-tidy/misc/DanglingHandleCheck.cpp
new file mode 100644 (file)
index 0000000..67f83d2
--- /dev/null
@@ -0,0 +1,177 @@
+//===--- DanglingHandleCheck.cpp - clang-tidy------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "DanglingHandleCheck.h"
+#include "../utils/Matchers.h"
+#include "../utils/OptionsUtils.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+using namespace clang::tidy::matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+namespace {
+
+ast_matchers::internal::BindableMatcher<Stmt>
+handleFrom(const ast_matchers::internal::Matcher<RecordDecl> &IsAHandle,
+           const ast_matchers::internal::Matcher<Expr> &Arg) {
+  return cxxConstructExpr(hasDeclaration(cxxMethodDecl(ofClass(IsAHandle))),
+                          hasArgument(0, Arg));
+}
+
+ast_matchers::internal::Matcher<Stmt> handleFromTemporaryValue(
+    const ast_matchers::internal::Matcher<RecordDecl> &IsAHandle) {
+  // If a ternary operator returns a temporary value, then both branches hold a
+  // temporary value. If one of them is not a temporary then it must be copied
+  // into one to satisfy the type of the operator.
+  const auto TemporaryTernary =
+      conditionalOperator(hasTrueExpression(cxxBindTemporaryExpr()),
+                          hasFalseExpression(cxxBindTemporaryExpr()));
+
+  return handleFrom(IsAHandle, anyOf(cxxBindTemporaryExpr(), TemporaryTernary));
+}
+
+ast_matchers::internal::Matcher<RecordDecl> isASequence() {
+  return hasAnyName("::std::deque", "::std::forward_list", "::std::list",
+                    "::std::vector");
+}
+
+ast_matchers::internal::Matcher<RecordDecl> isASet() {
+  return hasAnyName("::std::set", "::std::multiset", "::std::unordered_set",
+                    "::std::unordered_multiset");
+}
+
+ast_matchers::internal::Matcher<RecordDecl> isAMap() {
+  return hasAnyName("::std::map", "::std::multimap", "::std::unordered_map",
+                    "::std::unordered_multimap");
+}
+
+ast_matchers::internal::BindableMatcher<Stmt> makeContainerMatcher(
+    const ast_matchers::internal::Matcher<RecordDecl> &IsAHandle) {
+  // This matcher could be expanded to detect:
+  //  - Constructors: eg. vector<string_view>(3, string("A"));
+  //  - emplace*(): This requires a different logic to determine that
+  //                the conversion will happen inside the container.
+  //  - map's insert: This requires detecting that the pair conversion triggers
+  //                  the bug. A little more complicated than what we have now.
+  return callExpr(
+      hasAnyArgument(
+          ignoringParenImpCasts(handleFromTemporaryValue(IsAHandle))),
+      anyOf(
+          // For sequences: assign, push_back, resize.
+          cxxMemberCallExpr(
+              callee(functionDecl(hasAnyName("assign", "push_back", "resize"))),
+              on(expr(hasType(recordDecl(isASequence()))))),
+          // For sequences and sets: insert.
+          cxxMemberCallExpr(
+              callee(functionDecl(hasName("insert"))),
+              on(expr(hasType(recordDecl(anyOf(isASequence(), isASet())))))),
+          // For maps: operator[].
+          cxxOperatorCallExpr(callee(cxxMethodDecl(ofClass(isAMap()))),
+                              hasOverloadedOperatorName("[]"))));
+}
+
+} // anonymous namespace
+
+DanglingHandleCheck::DanglingHandleCheck(StringRef Name,
+                                         ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      HandleClasses(utils::options::parseStringList(Options.get(
+          "HandleClasses",
+          "std::basic_string_view;std::experimental::basic_string_view"))),
+      IsAHandle(cxxRecordDecl(hasAnyName(std::vector<StringRef>(
+                                  HandleClasses.begin(), HandleClasses.end())))
+                    .bind("handle")) {}
+
+void DanglingHandleCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "HandleClasses",
+                utils::options::serializeStringList(HandleClasses));
+}
+
+void DanglingHandleCheck::registerMatchersForVariables(MatchFinder *Finder) {
+  const auto ConvertedHandle = handleFromTemporaryValue(IsAHandle);
+
+  // Find 'Handle foo(ReturnsAValue());'
+  Finder->addMatcher(
+      varDecl(hasType(cxxRecordDecl(IsAHandle)),
+              hasInitializer(
+                  exprWithCleanups(has(ignoringParenImpCasts(ConvertedHandle)))
+                      .bind("bad_stmt"))),
+      this);
+
+  // Find 'Handle foo = ReturnsAValue();'
+  Finder->addMatcher(
+      varDecl(
+          hasType(cxxRecordDecl(IsAHandle)), unless(parmVarDecl()),
+          hasInitializer(exprWithCleanups(has(ignoringParenImpCasts(handleFrom(
+                                              IsAHandle, ConvertedHandle))))
+                             .bind("bad_stmt"))),
+      this);
+  // Find 'foo = ReturnsAValue();  // foo is Handle'
+  Finder->addMatcher(
+      cxxOperatorCallExpr(callee(cxxMethodDecl(ofClass(IsAHandle))),
+                          hasOverloadedOperatorName("="),
+                          hasArgument(1, ConvertedHandle))
+          .bind("bad_stmt"),
+      this);
+
+  // Container insertions that will dangle.
+  Finder->addMatcher(makeContainerMatcher(IsAHandle).bind("bad_stmt"), this);
+}
+
+void DanglingHandleCheck::registerMatchersForReturn(MatchFinder *Finder) {
+  // Return a local.
+  Finder->addMatcher(
+      returnStmt(
+          // The AST contains two constructor calls:
+          //   1. Value to Handle conversion.
+          //   2. Handle copy construction.
+          // We have to match both.
+          has(ignoringImplicit(handleFrom(
+              IsAHandle,
+              handleFrom(IsAHandle, declRefExpr(to(varDecl(
+                                        // Is function scope ...
+                                        hasAutomaticStorageDuration(),
+                                        // ... and it is a local array or Value.
+                                        anyOf(hasType(arrayType()),
+                                              hasType(recordDecl(
+                                                  unless(IsAHandle))))))))))),
+          // Temporary fix for false positives inside lambdas.
+          unless(hasAncestor(lambdaExpr())))
+          .bind("bad_stmt"),
+      this);
+
+  // Return a temporary.
+  Finder->addMatcher(
+      returnStmt(
+          has(ignoringParenImpCasts(exprWithCleanups(has(ignoringParenImpCasts(
+              handleFrom(IsAHandle, handleFromTemporaryValue(IsAHandle))))))))
+          .bind("bad_stmt"),
+      this);
+}
+
+void DanglingHandleCheck::registerMatchers(MatchFinder *Finder) {
+  registerMatchersForVariables(Finder);
+  registerMatchersForReturn(Finder);
+}
+
+void DanglingHandleCheck::check(const MatchFinder::MatchResult &Result) {
+  auto *Handle = Result.Nodes.getNodeAs<CXXRecordDecl>("handle");
+  diag(Result.Nodes.getNodeAs<Stmt>("bad_stmt")->getLocStart(),
+       "%0 outlives its value")
+      << Handle->getQualifiedNameAsString();
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/misc/DanglingHandleCheck.h b/clang-tidy/misc/DanglingHandleCheck.h
new file mode 100644 (file)
index 0000000..48797a4
--- /dev/null
@@ -0,0 +1,43 @@
+//===--- DanglingHandleCheck.h - clang-tidy----------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_DANGLING_HANDLE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_DANGLING_HANDLE_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Detect dangling references in value handlers like
+/// std::experimental::string_view.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/misc-dangling-handle.html
+class DanglingHandleCheck : public ClangTidyCheck {
+public:
+  DanglingHandleCheck(StringRef Name, ClangTidyContext *Context);
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+
+private:
+  void registerMatchersForVariables(ast_matchers::MatchFinder *Finder);
+  void registerMatchersForReturn(ast_matchers::MatchFinder *Finder);
+
+  const std::vector<std::string> HandleClasses;
+  const ast_matchers::internal::Matcher<RecordDecl> IsAHandle;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_DANGLING_HANDLE_H
diff --git a/clang-tidy/misc/DefinitionsInHeadersCheck.cpp b/clang-tidy/misc/DefinitionsInHeadersCheck.cpp
new file mode 100644 (file)
index 0000000..162ebff
--- /dev/null
@@ -0,0 +1,155 @@
+//===--- DefinitionsInHeadersCheck.cpp - clang-tidy------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "DefinitionsInHeadersCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+namespace {
+
+AST_MATCHER_P(NamedDecl, usesHeaderFileExtension,
+              utils::HeaderFileExtensionsSet, HeaderFileExtensions) {
+  return utils::isExpansionLocInHeaderFile(
+      Node.getLocStart(), Finder->getASTContext().getSourceManager(),
+      HeaderFileExtensions);
+}
+
+} // namespace
+
+DefinitionsInHeadersCheck::DefinitionsInHeadersCheck(StringRef Name,
+                                                     ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      UseHeaderFileExtension(Options.get("UseHeaderFileExtension", true)),
+      RawStringHeaderFileExtensions(
+          Options.getLocalOrGlobal("HeaderFileExtensions", ",h,hh,hpp,hxx")) {
+  if (!utils::parseHeaderFileExtensions(RawStringHeaderFileExtensions,
+                                        HeaderFileExtensions, ',')) {
+    // FIXME: Find a more suitable way to handle invalid configuration
+    // options.
+    llvm::errs() << "Invalid header file extension: "
+                 << RawStringHeaderFileExtensions << "\n";
+  }
+}
+
+void DefinitionsInHeadersCheck::storeOptions(
+    ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "UseHeaderFileExtension", UseHeaderFileExtension);
+  Options.store(Opts, "HeaderFileExtensions", RawStringHeaderFileExtensions);
+}
+
+void DefinitionsInHeadersCheck::registerMatchers(MatchFinder *Finder) {
+  if (!getLangOpts().CPlusPlus)
+    return;
+  auto DefinitionMatcher =
+      anyOf(functionDecl(isDefinition(), unless(isDeleted())),
+            varDecl(isDefinition()));
+  if (UseHeaderFileExtension) {
+    Finder->addMatcher(namedDecl(DefinitionMatcher,
+                                 usesHeaderFileExtension(HeaderFileExtensions))
+                           .bind("name-decl"),
+                       this);
+  } else {
+    Finder->addMatcher(
+        namedDecl(DefinitionMatcher,
+                  anyOf(usesHeaderFileExtension(HeaderFileExtensions),
+                        unless(isExpansionInMainFile())))
+            .bind("name-decl"),
+        this);
+  }
+}
+
+void DefinitionsInHeadersCheck::check(const MatchFinder::MatchResult &Result) {
+  // Don't run the check in failing TUs.
+  if (Result.Context->getDiagnostics().hasUncompilableErrorOccurred())
+    return;
+
+  // C++ [basic.def.odr] p6:
+  // There can be more than one definition of a class type, enumeration type,
+  // inline function with external linkage, class template, non-static function
+  // template, static data member of a class template, member function of a
+  // class template, or template specialization for which some template
+  // parameters are not specifiedin a program provided that each definition
+  // appears in a different translation unit, and provided the definitions
+  // satisfy the following requirements.
+  const auto *ND = Result.Nodes.getNodeAs<NamedDecl>("name-decl");
+  assert(ND);
+  if (ND->isInvalidDecl())
+    return;
+
+  // Internal linkage variable definitions are ignored for now:
+  //   const int a = 1;
+  //   static int b = 1;
+  //
+  // Although these might also cause ODR violations, we can be less certain and
+  // should try to keep the false-positive rate down.
+  if (ND->getLinkageInternal() == InternalLinkage)
+    return;
+
+  if (const auto *FD = dyn_cast<FunctionDecl>(ND)) {
+    // Inline functions are allowed.
+    if (FD->isInlined())
+      return;
+    // Function templates are allowed.
+    if (FD->getTemplatedKind() == FunctionDecl::TK_FunctionTemplate)
+      return;
+    // Ignore instantiated functions.
+    if (FD->isTemplateInstantiation())
+      return;
+    // Member function of a class template and member function of a nested class
+    // in a class template are allowed.
+    if (const auto *MD = dyn_cast<CXXMethodDecl>(FD)) {
+      const auto *DC = MD->getDeclContext();
+      while (DC->isRecord()) {
+        if (const auto *RD = dyn_cast<CXXRecordDecl>(DC)) {
+          if (isa<ClassTemplatePartialSpecializationDecl>(RD))
+            return;
+          if (RD->getDescribedClassTemplate())
+            return;
+        }
+        DC = DC->getParent();
+      }
+    }
+
+    bool is_full_spec = FD->getTemplateSpecializationKind() != TSK_Undeclared;
+    diag(FD->getLocation(),
+         "%select{function|full function template specialization}0 %1 defined "
+         "in a header file; function definitions in header files can lead to "
+         "ODR violations")
+        << is_full_spec << FD << FixItHint::CreateInsertion(
+                     FD->getReturnTypeSourceRange().getBegin(), "inline ");
+  } else if (const auto *VD = dyn_cast<VarDecl>(ND)) {
+    // Static data members of a class template are allowed.
+    if (VD->getDeclContext()->isDependentContext() && VD->isStaticDataMember())
+      return;
+    // Ignore instantiated static data members of classes.
+    if (isTemplateInstantiation(VD->getTemplateSpecializationKind()))
+      return;
+    // Ignore variable definition within function scope.
+    if (VD->hasLocalStorage() || VD->isStaticLocal())
+      return;
+    // Ignore inline variables.
+    if (VD->isInline())
+      return;
+
+    diag(VD->getLocation(),
+         "variable %0 defined in a header file; "
+         "variable definitions in header files can lead to ODR violations")
+        << VD;
+  }
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/misc/DefinitionsInHeadersCheck.h b/clang-tidy/misc/DefinitionsInHeadersCheck.h
new file mode 100644 (file)
index 0000000..428b05c
--- /dev/null
@@ -0,0 +1,51 @@
+//===--- DefinitionsInHeadersCheck.h - clang-tidy----------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_DEFINITIONS_IN_HEADERS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_DEFINITIONS_IN_HEADERS_H
+
+#include "../ClangTidy.h"
+#include "../utils/HeaderFileExtensionsUtils.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Finds non-extern non-inline function and variable definitions in header
+/// files, which can lead to potential ODR violations.
+///
+/// The check supports these options:
+///   - `UseHeaderFileExtension`: Whether to use file extension to distinguish
+///     header files. True by default.
+///   - `HeaderFileExtensions`: a comma-separated list of filename extensions of
+///     header files (The filename extension should not contain "." prefix).
+///     ",h,hh,hpp,hxx" by default.
+///     For extension-less header files, using an empty string or leaving an
+///     empty string between "," if there are other filename extensions.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/misc-definitions-in-headers.html
+class DefinitionsInHeadersCheck : public ClangTidyCheck {
+public:
+  DefinitionsInHeadersCheck(StringRef Name, ClangTidyContext *Context);
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+  const bool UseHeaderFileExtension;
+  const std::string RawStringHeaderFileExtensions;
+  utils::HeaderFileExtensionsSet HeaderFileExtensions;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_DEFINITIONS_IN_HEADERS_H
diff --git a/clang-tidy/misc/FoldInitTypeCheck.cpp b/clang-tidy/misc/FoldInitTypeCheck.cpp
new file mode 100644 (file)
index 0000000..c5d2720
--- /dev/null
@@ -0,0 +1,140 @@
+//===--- FoldInitTypeCheck.cpp - clang-tidy--------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "FoldInitTypeCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+void FoldInitTypeCheck::registerMatchers(MatchFinder *Finder) {
+  // We match functions of interest and bind the iterator and init value types.
+  // Note: Right now we check only builtin types.
+  const auto BuiltinTypeWithId = [](const char *ID) {
+    return hasCanonicalType(builtinType().bind(ID));
+  };
+  const auto IteratorWithValueType = [&BuiltinTypeWithId](const char *ID) {
+    return anyOf(
+        // Pointer types.
+        pointsTo(BuiltinTypeWithId(ID)),
+        // Iterator types.
+        recordType(hasDeclaration(has(typedefNameDecl(
+            hasName("value_type"), hasType(BuiltinTypeWithId(ID)))))));
+  };
+
+  const auto IteratorParam = parmVarDecl(
+      hasType(hasCanonicalType(IteratorWithValueType("IterValueType"))));
+  const auto Iterator2Param = parmVarDecl(
+      hasType(hasCanonicalType(IteratorWithValueType("Iter2ValueType"))));
+  const auto InitParam = parmVarDecl(hasType(BuiltinTypeWithId("InitType")));
+
+  // std::accumulate, std::reduce.
+  Finder->addMatcher(
+      callExpr(callee(functionDecl(
+                   hasAnyName("::std::accumulate", "::std::reduce"),
+                   hasParameter(0, IteratorParam), hasParameter(2, InitParam))),
+               argumentCountIs(3))
+          .bind("Call"),
+      this);
+  // std::inner_product.
+  Finder->addMatcher(
+      callExpr(callee(functionDecl(hasName("::std::inner_product"),
+                                   hasParameter(0, IteratorParam),
+                                   hasParameter(2, Iterator2Param),
+                                   hasParameter(3, InitParam))),
+               argumentCountIs(4))
+          .bind("Call"),
+      this);
+  // std::reduce with a policy.
+  Finder->addMatcher(
+      callExpr(callee(functionDecl(hasName("::std::reduce"),
+                                   hasParameter(1, IteratorParam),
+                                   hasParameter(3, InitParam))),
+               argumentCountIs(4))
+          .bind("Call"),
+      this);
+  // std::inner_product with a policy.
+  Finder->addMatcher(
+      callExpr(callee(functionDecl(hasName("::std::inner_product"),
+                                   hasParameter(1, IteratorParam),
+                                   hasParameter(3, Iterator2Param),
+                                   hasParameter(4, InitParam))),
+               argumentCountIs(5))
+          .bind("Call"),
+      this);
+}
+
+/// Returns true if ValueType is allowed to fold into InitType, i.e. if:
+///   static_cast<InitType>(ValueType{some_value})
+/// does not result in trucation.
+static bool isValidBuiltinFold(const BuiltinType &ValueType,
+                               const BuiltinType &InitType,
+                               const ASTContext &Context) {
+  const auto ValueTypeSize = Context.getTypeSize(&ValueType);
+  const auto InitTypeSize = Context.getTypeSize(&InitType);
+  // It's OK to fold a float into a float of bigger or equal size, but not OK to
+  // fold into an int.
+  if (ValueType.isFloatingPoint())
+    return InitType.isFloatingPoint() && InitTypeSize >= ValueTypeSize;
+  // It's OK to fold an int into:
+  //  - an int of the same size and signedness.
+  //  - a bigger int, regardless of signedness.
+  //  - FIXME: should it be a warning to fold into floating point?
+  if (ValueType.isInteger()) {
+    if (InitType.isInteger()) {
+      if (InitType.isSignedInteger() == ValueType.isSignedInteger())
+        return InitTypeSize >= ValueTypeSize;
+      return InitTypeSize > ValueTypeSize;
+    }
+    if (InitType.isFloatingPoint())
+      return InitTypeSize >= ValueTypeSize;
+  }
+  return false;
+}
+
+/// Prints a diagnostic if IterValueType doe snot fold into IterValueType (see
+// isValidBuiltinFold for details).
+void FoldInitTypeCheck::doCheck(const BuiltinType &IterValueType,
+                                const BuiltinType &InitType,
+                                const ASTContext &Context,
+                                const CallExpr &CallNode) {
+  if (!isValidBuiltinFold(IterValueType, InitType, Context)) {
+    diag(CallNode.getExprLoc(), "folding type %0 into type %1 might result in "
+                                "loss of precision")
+        << IterValueType.desugar() << InitType.desugar();
+  }
+}
+
+void FoldInitTypeCheck::check(const MatchFinder::MatchResult &Result) {
+  // Given the iterator and init value type retreived by the matchers,
+  // we check that the ::value_type of the iterator is compatible with
+  // the init value type.
+  const auto *InitType = Result.Nodes.getNodeAs<BuiltinType>("InitType");
+  const auto *IterValueType =
+      Result.Nodes.getNodeAs<BuiltinType>("IterValueType");
+  assert(InitType != nullptr);
+  assert(IterValueType != nullptr);
+
+  const auto *CallNode = Result.Nodes.getNodeAs<CallExpr>("Call");
+  assert(CallNode != nullptr);
+
+  doCheck(*IterValueType, *InitType, *Result.Context, *CallNode);
+
+  if (const auto *Iter2ValueType =
+          Result.Nodes.getNodeAs<BuiltinType>("Iter2ValueType"))
+    doCheck(*Iter2ValueType, *InitType, *Result.Context, *CallNode);
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/misc/FoldInitTypeCheck.h b/clang-tidy/misc/FoldInitTypeCheck.h
new file mode 100644 (file)
index 0000000..df4ec88
--- /dev/null
@@ -0,0 +1,44 @@
+//===--- FoldInitTypeCheck.h - clang-tidy------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_FOLD_INIT_TYPE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_FOLD_INIT_TYPE_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Find and flag invalid initializer values in folds, e.g. std::accumulate.
+/// Example:
+/// \code
+///   auto v = {65536L * 65536 * 65536};
+///   std::accumulate(begin(v), end(v), 0 /* int type is too small */);
+/// \endcode
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/misc-fold-init-type.html
+class FoldInitTypeCheck : public ClangTidyCheck {
+public:
+  FoldInitTypeCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+  void doCheck(const BuiltinType &IterValueType, const BuiltinType &InitType,
+               const ASTContext &Context, const CallExpr &CallNode);
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_FOLD_INIT_TYPE_H
diff --git a/clang-tidy/misc/ForwardDeclarationNamespaceCheck.cpp b/clang-tidy/misc/ForwardDeclarationNamespaceCheck.cpp
new file mode 100644 (file)
index 0000000..1487e8f
--- /dev/null
@@ -0,0 +1,174 @@
+//===--- ForwardDeclarationNamespaceCheck.cpp - clang-tidy ------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ForwardDeclarationNamespaceCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include <stack>
+#include <string>
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+void ForwardDeclarationNamespaceCheck::registerMatchers(MatchFinder *Finder) {
+  // Match all class declarations/definitions *EXCEPT*
+  // 1. implicit classes, e.g. `class A {};` has implicit `class A` inside `A`.
+  // 2. nested classes declared/defined inside another class.
+  // 3. template class declaration, template instantiation or
+  //    specialization (NOTE: extern specialization is filtered out by
+  //    `unless(hasAncestor(cxxRecordDecl()))`).
+  auto IsInSpecialization = hasAncestor(
+      decl(anyOf(cxxRecordDecl(isExplicitTemplateSpecialization()),
+                 functionDecl(isExplicitTemplateSpecialization()))));
+  Finder->addMatcher(
+      cxxRecordDecl(
+          hasParent(decl(anyOf(namespaceDecl(), translationUnitDecl()))),
+          unless(isImplicit()), unless(hasAncestor(cxxRecordDecl())),
+          unless(isInstantiated()), unless(IsInSpecialization),
+          unless(classTemplateSpecializationDecl()))
+          .bind("record_decl"),
+      this);
+
+  // Match all friend declarations. Classes used in friend declarations are not
+  // marked as referenced in AST. We need to record all record classes used in
+  // friend declarations.
+  Finder->addMatcher(friendDecl().bind("friend_decl"), this);
+}
+
+void ForwardDeclarationNamespaceCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  if (const auto *RecordDecl =
+          Result.Nodes.getNodeAs<CXXRecordDecl>("record_decl")) {
+    StringRef DeclName = RecordDecl->getName();
+    if (RecordDecl->isThisDeclarationADefinition()) {
+      DeclNameToDefinitions[DeclName].push_back(RecordDecl);
+    } else {
+      // If a declaration has no definition, the definition could be in another
+      // namespace (a wrong namespace).
+      // NOTE: even a declaration does have definition, we still need it to
+      // compare with other declarations.
+      DeclNameToDeclarations[DeclName].push_back(RecordDecl);
+    }
+  } else {
+    const auto *Decl = Result.Nodes.getNodeAs<FriendDecl>("friend_decl");
+    assert(Decl && "Decl is neither record_decl nor friend decl!");
+
+    // Classes used in friend delarations are not marked referenced in AST,
+    // so we need to check classes used in friend declarations manually to
+    // reduce the rate of false positive.
+    // For example, in
+    //    \code
+    //      struct A;
+    //      struct B { friend A; };
+    //    \endcode
+    // `A` will not be marked as "referenced" in the AST.
+    if (const TypeSourceInfo *Tsi = Decl->getFriendType()) {
+      QualType Desugared = Tsi->getType().getDesugaredType(*Result.Context);
+      FriendTypes.insert(Desugared.getTypePtr());
+    }
+  }
+}
+
+static bool haveSameNamespaceOrTranslationUnit(const CXXRecordDecl *Decl1,
+                                               const CXXRecordDecl *Decl2) {
+  const DeclContext *ParentDecl1 = Decl1->getLexicalParent();
+  const DeclContext *ParentDecl2 = Decl2->getLexicalParent();
+
+  // Since we only matched declarations whose parent is Namespace or
+  // TranslationUnit declaration, the parent should be either a translation unit
+  // or namespace.
+  if (ParentDecl1->getDeclKind() == Decl::TranslationUnit ||
+      ParentDecl2->getDeclKind() == Decl::TranslationUnit) {
+    return ParentDecl1 == ParentDecl2;
+  }
+  assert(ParentDecl1->getDeclKind() == Decl::Namespace &&
+         "ParentDecl1 declaration must be a namespace");
+  assert(ParentDecl2->getDeclKind() == Decl::Namespace &&
+         "ParentDecl2 declaration must be a namespace");
+  auto *Ns1 = NamespaceDecl::castFromDeclContext(ParentDecl1);
+  auto *Ns2 = NamespaceDecl::castFromDeclContext(ParentDecl2);
+  return Ns1->getOriginalNamespace() == Ns2->getOriginalNamespace();
+}
+
+static std::string getNameOfNamespace(const CXXRecordDecl *Decl) {
+  const auto *ParentDecl = Decl->getLexicalParent();
+  if (ParentDecl->getDeclKind() == Decl::TranslationUnit) {
+    return "(global)";
+  }
+  const auto *NsDecl = cast<NamespaceDecl>(ParentDecl);
+  std::string Ns;
+  llvm::raw_string_ostream OStream(Ns);
+  NsDecl->printQualifiedName(OStream);
+  OStream.flush();
+  return Ns.empty() ? "(global)" : Ns;
+}
+
+void ForwardDeclarationNamespaceCheck::onEndOfTranslationUnit() {
+  // Iterate each group of declarations by name.
+  for (const auto &KeyValuePair : DeclNameToDeclarations) {
+    const auto &Declarations = KeyValuePair.second;
+    // If more than 1 declaration exists, we check if all are in the same
+    // namespace.
+    for (const auto *CurDecl : Declarations) {
+      if (CurDecl->hasDefinition() || CurDecl->isReferenced()) {
+        continue; // Skip forward declarations that are used/referenced.
+      }
+      if (FriendTypes.count(CurDecl->getTypeForDecl()) != 0) {
+        continue; // Skip forward declarations referenced as friend.
+      }
+      if (CurDecl->getLocation().isMacroID() ||
+          CurDecl->getLocation().isInvalid()) {
+        continue;
+      }
+      // Compare with all other declarations with the same name.
+      for (const auto *Decl : Declarations) {
+        if (Decl == CurDecl) {
+          continue; // Don't compare with self.
+        }
+        if (!CurDecl->hasDefinition() &&
+            !haveSameNamespaceOrTranslationUnit(CurDecl, Decl)) {
+          diag(CurDecl->getLocation(),
+               "declaration %0 is never referenced, but a declaration with "
+               "the same name found in another namespace '%1'")
+              << CurDecl << getNameOfNamespace(Decl);
+          diag(Decl->getLocation(), "a declaration of %0 is found here",
+               DiagnosticIDs::Note)
+              << Decl;
+          break; // FIXME: We only generate one warning for each declaration.
+        }
+      }
+      // Check if a definition in another namespace exists.
+      const auto DeclName = CurDecl->getName();
+      if (DeclNameToDefinitions.find(DeclName) == DeclNameToDefinitions.end()) {
+        continue; // No definition in this translation unit, we can skip it.
+      }
+      // Make a warning for each definition with the same name (in other
+      // namespaces).
+      const auto &Definitions = DeclNameToDefinitions[DeclName];
+      for (const auto *Def : Definitions) {
+        diag(CurDecl->getLocation(),
+             "no definition found for %0, but a definition with "
+             "the same name %1 found in another namespace '%2'")
+            << CurDecl << Def << getNameOfNamespace(Def);
+        diag(Def->getLocation(), "a definition of %0 is found here",
+             DiagnosticIDs::Note)
+            << Def;
+      }
+    }
+  }
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/misc/ForwardDeclarationNamespaceCheck.h b/clang-tidy/misc/ForwardDeclarationNamespaceCheck.h
new file mode 100644 (file)
index 0000000..dd7042d
--- /dev/null
@@ -0,0 +1,59 @@
+//===--- ForwardDeclarationNamespaceCheck.h - clang-tidy --------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_FORWARDDECLARATIONNAMESPACECHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_FORWARDDECLARATIONNAMESPACECHECK_H
+
+#include "../ClangTidy.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include <set>
+#include <vector>
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Checks if an unused forward declaration is in a wrong namespace.
+///
+/// The check inspects all unused forward declarations and checks if there is
+/// any declaration/definition with the same name, which could indicate
+/// that the forward declaration is potentially in a wrong namespace.
+///
+/// \code
+///   namespace na { struct A; }
+///   namespace nb { struct A {} };
+///   nb::A a;
+///   // warning : no definition found for 'A', but a definition with the same
+///   name 'A' found in another namespace 'nb::'
+/// \endcode
+///
+/// This check can only generate warnings, but it can't suggest fixes at this
+/// point.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/misc-forward-declaration-namespace.html
+class ForwardDeclarationNamespaceCheck : public ClangTidyCheck {
+public:
+  ForwardDeclarationNamespaceCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+  void onEndOfTranslationUnit() override;
+
+private:
+  llvm::StringMap<std::vector<const CXXRecordDecl *>> DeclNameToDefinitions;
+  llvm::StringMap<std::vector<const CXXRecordDecl *>> DeclNameToDeclarations;
+  llvm::SmallPtrSet<const Type *, 16> FriendTypes;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_FORWARDDECLARATIONNAMESPACECHECK_H
diff --git a/clang-tidy/misc/ForwardingReferenceOverloadCheck.cpp b/clang-tidy/misc/ForwardingReferenceOverloadCheck.cpp
new file mode 100644 (file)
index 0000000..3da5a09
--- /dev/null
@@ -0,0 +1,148 @@
+//===--- ForwardingReferenceOverloadCheck.cpp - clang-tidy-----------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ForwardingReferenceOverloadCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include <algorithm>
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+namespace {
+// Check if the given type is related to std::enable_if.
+AST_MATCHER(QualType, isEnableIf) {
+  auto CheckTemplate = [](const TemplateSpecializationType *Spec) {
+    if (!Spec || !Spec->getTemplateName().getAsTemplateDecl()) {
+      return false;
+    }
+    const NamedDecl *TypeDecl =
+        Spec->getTemplateName().getAsTemplateDecl()->getTemplatedDecl();
+    return TypeDecl->isInStdNamespace() &&
+           (TypeDecl->getName().equals("enable_if") ||
+            TypeDecl->getName().equals("enable_if_t"));
+  };
+  const Type *BaseType = Node.getTypePtr();
+  // Case: pointer or reference to enable_if.
+  while (BaseType->isPointerType() || BaseType->isReferenceType()) {
+    BaseType = BaseType->getPointeeType().getTypePtr();
+  }
+  // Case: type parameter dependent (enable_if<is_integral<T>>).
+  if (const auto *Dependent = BaseType->getAs<DependentNameType>()) {
+    BaseType = Dependent->getQualifier()->getAsType();
+  }
+  if (!BaseType)
+    return false;
+  if (CheckTemplate(BaseType->getAs<TemplateSpecializationType>())) {
+    return true; // Case: enable_if_t< >.
+  } else if (const auto *Elaborated = BaseType->getAs<ElaboratedType>()) {
+    if (const auto *Qualifier = Elaborated->getQualifier()->getAsType()) {
+      if (CheckTemplate(Qualifier->getAs<TemplateSpecializationType>())) {
+        return true; // Case: enable_if< >::type.
+      }
+    }
+  }
+  return false;
+}
+AST_MATCHER_P(TemplateTypeParmDecl, hasDefaultArgument,
+              clang::ast_matchers::internal::Matcher<QualType>, TypeMatcher) {
+  return Node.hasDefaultArgument() &&
+         TypeMatcher.matches(Node.getDefaultArgument(), Finder, Builder);
+}
+} // namespace
+
+void ForwardingReferenceOverloadCheck::registerMatchers(MatchFinder *Finder) {
+  // Forwarding references require C++11 or later.
+  if (!getLangOpts().CPlusPlus11)
+    return;
+
+  auto ForwardingRefParm =
+      parmVarDecl(
+          hasType(qualType(rValueReferenceType(),
+                           references(templateTypeParmType(hasDeclaration(
+                               templateTypeParmDecl().bind("type-parm-decl")))),
+                           unless(references(isConstQualified())))))
+          .bind("parm-var");
+
+  DeclarationMatcher findOverload =
+      cxxConstructorDecl(
+          hasParameter(0, ForwardingRefParm),
+          unless(hasAnyParameter(
+              // No warning: enable_if as constructor parameter.
+              parmVarDecl(hasType(isEnableIf())))),
+          unless(hasParent(functionTemplateDecl(has(templateTypeParmDecl(
+              // No warning: enable_if as type parameter.
+              hasDefaultArgument(isEnableIf())))))))
+          .bind("ctor");
+  Finder->addMatcher(findOverload, this);
+}
+
+void ForwardingReferenceOverloadCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  const auto *ParmVar = Result.Nodes.getNodeAs<ParmVarDecl>("parm-var");
+  const auto *TypeParmDecl =
+      Result.Nodes.getNodeAs<TemplateTypeParmDecl>("type-parm-decl");
+
+  // Get the FunctionDecl and FunctionTemplateDecl containing the function
+  // parameter.
+  const auto *FuncForParam = dyn_cast<FunctionDecl>(ParmVar->getDeclContext());
+  if (!FuncForParam)
+    return;
+  const FunctionTemplateDecl *FuncTemplate =
+      FuncForParam->getDescribedFunctionTemplate();
+  if (!FuncTemplate)
+    return;
+
+  // Check that the template type parameter belongs to the same function
+  // template as the function parameter of that type. (This implies that type
+  // deduction will happen on the type.)
+  const TemplateParameterList *Params = FuncTemplate->getTemplateParameters();
+  if (std::find(Params->begin(), Params->end(), TypeParmDecl) == Params->end())
+    return;
+
+  // Every parameter after the first must have a default value.
+  const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructorDecl>("ctor");
+  for (auto Iter = Ctor->param_begin() + 1; Iter != Ctor->param_end(); ++Iter) {
+    if (!(*Iter)->hasDefaultArg())
+      return;
+  }
+  bool EnabledCopy = false, DisabledCopy = false, EnabledMove = false,
+       DisabledMove = false;
+  for (const auto *OtherCtor : Ctor->getParent()->ctors()) {
+    if (OtherCtor->isCopyOrMoveConstructor()) {
+      if (OtherCtor->isDeleted() || OtherCtor->getAccess() == AS_private)
+        (OtherCtor->isCopyConstructor() ? DisabledCopy : DisabledMove) = true;
+      else
+        (OtherCtor->isCopyConstructor() ? EnabledCopy : EnabledMove) = true;
+    }
+  }
+  bool Copy = (!EnabledMove && !DisabledMove && !DisabledCopy) || EnabledCopy;
+  bool Move = !DisabledMove || EnabledMove;
+  if (!Copy && !Move)
+    return;
+  diag(Ctor->getLocation(),
+       "constructor accepting a forwarding reference can "
+       "hide the %select{copy|move|copy and move}0 constructor%s1")
+      << (Copy && Move ? 2 : (Copy ? 0 : 1)) << Copy + Move;
+  for (const auto *OtherCtor : Ctor->getParent()->ctors()) {
+    if (OtherCtor->isCopyOrMoveConstructor() && !OtherCtor->isDeleted() &&
+        OtherCtor->getAccess() != AS_private) {
+      diag(OtherCtor->getLocation(),
+           "%select{copy|move}0 constructor declared here", DiagnosticIDs::Note)
+          << OtherCtor->isMoveConstructor();
+    }
+  }
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/misc/ForwardingReferenceOverloadCheck.h b/clang-tidy/misc/ForwardingReferenceOverloadCheck.h
new file mode 100644 (file)
index 0000000..ffdf0e8
--- /dev/null
@@ -0,0 +1,42 @@
+//===--- ForwardingReferenceOverloadCheck.h - clang-tidy---------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_FORWARDING_REFERENCE_OVERLOAD_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_FORWARDING_REFERENCE_OVERLOAD_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// The checker looks for constructors that can act as copy or move constructors
+/// through their forwarding reference parameters. If a non const lvalue
+/// reference is passed to the constructor, the forwarding reference parameter
+/// can be a perfect match while the const reference parameter of the copy
+/// constructor can't. The forwarding reference constructor will be called,
+/// which can lead to confusion.
+/// For detailed description of this problem see: Scott Meyers, Effective Modern
+/// C++ Design, item 26.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/misc-forwarding-reference-overload.html
+class ForwardingReferenceOverloadCheck : public ClangTidyCheck {
+public:
+  ForwardingReferenceOverloadCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_FORWARDING_REFERENCE_OVERLOAD_H
diff --git a/clang-tidy/misc/InaccurateEraseCheck.cpp b/clang-tidy/misc/InaccurateEraseCheck.cpp
new file mode 100644 (file)
index 0000000..f75d6fe
--- /dev/null
@@ -0,0 +1,80 @@
+//===--- InaccurateEraseCheck.cpp - clang-tidy-----------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "InaccurateEraseCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+namespace {
+AST_MATCHER(Decl, isInStdNamespace) { return Node.isInStdNamespace(); }
+}
+
+void InaccurateEraseCheck::registerMatchers(MatchFinder *Finder) {
+  // Only register the matchers for C++; the functionality currently does not
+  // provide any benefit to other languages, despite being benign.
+  if (!getLangOpts().CPlusPlus)
+    return;
+
+  const auto EndCall =
+      callExpr(
+          callee(functionDecl(hasAnyName("remove", "remove_if", "unique"))),
+          hasArgument(
+              1,
+              anyOf(cxxConstructExpr(has(ignoringImplicit(
+                        cxxMemberCallExpr(callee(cxxMethodDecl(hasName("end"))))
+                            .bind("end")))),
+                    anything())))
+          .bind("alg");
+
+  const auto DeclInStd = decl(isInStdNamespace());
+  Finder->addMatcher(
+      cxxMemberCallExpr(
+          on(anyOf(hasType(DeclInStd), hasType(pointsTo(DeclInStd)))),
+          callee(cxxMethodDecl(hasName("erase"))), argumentCountIs(1),
+          hasArgument(0, has(ignoringImplicit(
+                             anyOf(EndCall, has(ignoringImplicit(EndCall)))))),
+          unless(isInTemplateInstantiation()))
+          .bind("erase"),
+      this);
+}
+
+void InaccurateEraseCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *MemberCall =
+      Result.Nodes.getNodeAs<CXXMemberCallExpr>("erase");
+  const auto *EndExpr =
+      Result.Nodes.getNodeAs<CXXMemberCallExpr>("end");
+  const SourceLocation Loc = MemberCall->getLocStart();
+
+  FixItHint Hint;
+
+  if (!Loc.isMacroID() && EndExpr) {
+    const auto *AlgCall = Result.Nodes.getNodeAs<CallExpr>("alg");
+    std::string ReplacementText = Lexer::getSourceText(
+        CharSourceRange::getTokenRange(EndExpr->getSourceRange()),
+        *Result.SourceManager, getLangOpts());
+    const SourceLocation EndLoc = Lexer::getLocForEndOfToken(
+        AlgCall->getLocEnd(), 0, *Result.SourceManager, getLangOpts());
+    Hint = FixItHint::CreateInsertion(EndLoc, ", " + ReplacementText);
+  }
+
+  diag(Loc, "this call will remove at most one item even when multiple items "
+            "should be removed")
+      << Hint;
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/misc/InaccurateEraseCheck.h b/clang-tidy/misc/InaccurateEraseCheck.h
new file mode 100644 (file)
index 0000000..623e1c2
--- /dev/null
@@ -0,0 +1,38 @@
+//===--- InaccurateEraseCheck.h - clang-tidy---------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_INACCURATEERASECHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_INACCURATEERASECHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Checks for inaccurate use of the `erase()` method.
+///
+/// Algorithms like `remove()` do not actually remove any element from the
+/// container but return an iterator to the first redundant element at the end
+/// of the container. These redundant elements must be removed using the
+/// `erase()` method. This check warns when not all of the elements will be
+/// removed due to using an inappropriate overload.
+class InaccurateEraseCheck : public ClangTidyCheck {
+public:
+  InaccurateEraseCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_INACCURATEERASECHECK_H
diff --git a/clang-tidy/misc/IncorrectRoundings.cpp b/clang-tidy/misc/IncorrectRoundings.cpp
new file mode 100644 (file)
index 0000000..7f9b90b
--- /dev/null
@@ -0,0 +1,71 @@
+//===--- IncorrectRoundings.cpp - clang-tidy ------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "IncorrectRoundings.h"
+#include "clang/AST/DeclBase.h"
+#include "clang/AST/Type.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+namespace {
+AST_MATCHER(FloatingLiteral, floatHalf) {
+  const auto &literal = Node.getValue();
+  if ((&Node.getSemantics()) == &llvm::APFloat::IEEEsingle())
+    return literal.convertToFloat() == 0.5f;
+  if ((&Node.getSemantics()) == &llvm::APFloat::IEEEdouble())
+    return literal.convertToDouble() == 0.5;
+  return false;
+}
+} // namespace
+
+void IncorrectRoundings::registerMatchers(MatchFinder *MatchFinder) {
+  // Match a floating literal with value 0.5.
+  auto FloatHalf = floatLiteral(floatHalf());
+
+  // Match a floating point expression.
+  auto FloatType = expr(hasType(realFloatingPointType()));
+
+  // Match a floating literal of 0.5 or a floating literal of 0.5 implicitly.
+  // cast to floating type.
+  auto FloatOrCastHalf =
+      anyOf(FloatHalf,
+            implicitCastExpr(FloatType, has(ignoringParenImpCasts(FloatHalf))));
+
+  // Match if either the LHS or RHS is a floating literal of 0.5 or a floating
+  // literal of 0.5 and the other is of type double or vice versa.
+  auto OneSideHalf = anyOf(allOf(hasLHS(FloatOrCastHalf), hasRHS(FloatType)),
+                           allOf(hasRHS(FloatOrCastHalf), hasLHS(FloatType)));
+
+  // Find expressions of cast to int of the sum of a floating point expression
+  // and 0.5.
+  MatchFinder->addMatcher(
+      implicitCastExpr(
+          hasImplicitDestinationType(isInteger()),
+          ignoringParenCasts(binaryOperator(hasOperatorName("+"), OneSideHalf)))
+          .bind("CastExpr"),
+      this);
+}
+
+void IncorrectRoundings::check(const MatchFinder::MatchResult &Result) {
+  const auto *CastExpr = Result.Nodes.getNodeAs<ImplicitCastExpr>("CastExpr");
+  diag(CastExpr->getLocStart(),
+       "casting (double + 0.5) to integer leads to incorrect rounding; "
+       "consider using lround (#include <cmath>) instead");
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/misc/IncorrectRoundings.h b/clang-tidy/misc/IncorrectRoundings.h
new file mode 100644 (file)
index 0000000..7b5f6a0
--- /dev/null
@@ -0,0 +1,39 @@
+//===--- IncorrectRoundings.h - clang-tidy ----------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_INCORRECTROUNDINGS_H_
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_INCORRECTROUNDINGS_H_
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// \brief Checks the usage of patterns known to produce incorrect rounding.
+/// Programmers often use
+///   (int)(double_expression + 0.5)
+/// to round the double expression to an integer. The problem with this
+///  1. It is unnecessarily slow.
+///  2. It is incorrect. The number 0.499999975 (smallest representable float
+///     number below 0.5) rounds to 1.0. Even worse behavior for negative
+///     numbers where both -0.5f and -1.4f both round to 0.0.
+class IncorrectRoundings : public ClangTidyCheck {
+public:
+  IncorrectRoundings(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_INCORRECTROUNDINGS_H_
diff --git a/clang-tidy/misc/InefficientAlgorithmCheck.cpp b/clang-tidy/misc/InefficientAlgorithmCheck.cpp
new file mode 100644 (file)
index 0000000..a5a5b2b
--- /dev/null
@@ -0,0 +1,163 @@
+//===--- InefficientAlgorithmCheck.cpp - clang-tidy------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "InefficientAlgorithmCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+static bool areTypesCompatible(QualType Left, QualType Right) {
+  if (const auto *LeftRefType = Left->getAs<ReferenceType>())
+    Left = LeftRefType->getPointeeType();
+  if (const auto *RightRefType = Right->getAs<ReferenceType>())
+    Right = RightRefType->getPointeeType();
+  return Left->getCanonicalTypeUnqualified() ==
+         Right->getCanonicalTypeUnqualified();
+}
+
+void InefficientAlgorithmCheck::registerMatchers(MatchFinder *Finder) {
+  // Only register the matchers for C++; the functionality currently does not
+  // provide any benefit to other languages, despite being benign.
+  if (!getLangOpts().CPlusPlus)
+    return;
+
+  const auto Algorithms =
+      hasAnyName("::std::find", "::std::count", "::std::equal_range",
+                 "::std::lower_bound", "::std::upper_bound");
+  const auto ContainerMatcher = classTemplateSpecializationDecl(hasAnyName(
+      "::std::set", "::std::map", "::std::multiset", "::std::multimap",
+      "::std::unordered_set", "::std::unordered_map",
+      "::std::unordered_multiset", "::std::unordered_multimap"));
+
+  const auto Matcher =
+      callExpr(
+          callee(functionDecl(Algorithms)),
+          hasArgument(
+              0, cxxConstructExpr(has(ignoringParenImpCasts(cxxMemberCallExpr(
+                     callee(cxxMethodDecl(hasName("begin"))),
+                     on(declRefExpr(
+                            hasDeclaration(decl().bind("IneffContObj")),
+                            anyOf(hasType(ContainerMatcher.bind("IneffCont")),
+                                  hasType(pointsTo(
+                                      ContainerMatcher.bind("IneffContPtr")))))
+                            .bind("IneffContExpr"))))))),
+          hasArgument(
+              1, cxxConstructExpr(has(ignoringParenImpCasts(cxxMemberCallExpr(
+                     callee(cxxMethodDecl(hasName("end"))),
+                     on(declRefExpr(
+                         hasDeclaration(equalsBoundNode("IneffContObj"))))))))),
+          hasArgument(2, expr().bind("AlgParam")),
+          unless(isInTemplateInstantiation()))
+          .bind("IneffAlg");
+
+  Finder->addMatcher(Matcher, this);
+}
+
+void InefficientAlgorithmCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *AlgCall = Result.Nodes.getNodeAs<CallExpr>("IneffAlg");
+  const auto *IneffCont =
+      Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>("IneffCont");
+  bool PtrToContainer = false;
+  if (!IneffCont) {
+    IneffCont =
+        Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>("IneffContPtr");
+    PtrToContainer = true;
+  }
+  const llvm::StringRef IneffContName = IneffCont->getName();
+  const bool Unordered =
+      IneffContName.find("unordered") != llvm::StringRef::npos;
+  const bool Maplike = IneffContName.find("map") != llvm::StringRef::npos;
+
+  // Store if the key type of the container is compatible with the value
+  // that is searched for.
+  QualType ValueType = AlgCall->getArg(2)->getType();
+  QualType KeyType =
+      IneffCont->getTemplateArgs()[0].getAsType().getCanonicalType();
+  const bool CompatibleTypes = areTypesCompatible(KeyType, ValueType);
+
+  // Check if the comparison type for the algorithm and the container matches.
+  if (AlgCall->getNumArgs() == 4 && !Unordered) {
+    const Expr *Arg = AlgCall->getArg(3);
+    const QualType AlgCmp =
+        Arg->getType().getUnqualifiedType().getCanonicalType();
+    const unsigned CmpPosition =
+        (IneffContName.find("map") == llvm::StringRef::npos) ? 1 : 2;
+    const QualType ContainerCmp = IneffCont->getTemplateArgs()[CmpPosition]
+                                      .getAsType()
+                                      .getUnqualifiedType()
+                                      .getCanonicalType();
+    if (AlgCmp != ContainerCmp) {
+      diag(Arg->getLocStart(),
+           "different comparers used in the algorithm and the container");
+      return;
+    }
+  }
+
+  const auto *AlgDecl = AlgCall->getDirectCallee();
+  if (!AlgDecl)
+    return;
+
+  if (Unordered && AlgDecl->getName().find("bound") != llvm::StringRef::npos)
+    return;
+
+  const auto *AlgParam = Result.Nodes.getNodeAs<Expr>("AlgParam");
+  const auto *IneffContExpr = Result.Nodes.getNodeAs<Expr>("IneffContExpr");
+  FixItHint Hint;
+
+  SourceManager &SM = *Result.SourceManager;
+  LangOptions LangOpts = getLangOpts();
+
+  CharSourceRange CallRange =
+      CharSourceRange::getTokenRange(AlgCall->getSourceRange());
+
+  // FIXME: Create a common utility to extract a file range that the given token
+  // sequence is exactly spelled at (without macro argument expansions etc.).
+  // We can't use Lexer::makeFileCharRange here, because for
+  //
+  //   #define F(x) x
+  //   x(a b c);
+  //
+  // it will return "x(a b c)", when given the range "a"-"c". It makes sense for
+  // removals, but not for replacements.
+  //
+  // This code is over-simplified, but works for many real cases.
+  if (SM.isMacroArgExpansion(CallRange.getBegin()) &&
+      SM.isMacroArgExpansion(CallRange.getEnd())) {
+    CallRange.setBegin(SM.getSpellingLoc(CallRange.getBegin()));
+    CallRange.setEnd(SM.getSpellingLoc(CallRange.getEnd()));
+  }
+
+  if (!CallRange.getBegin().isMacroID() && !Maplike && CompatibleTypes) {
+    StringRef ContainerText = Lexer::getSourceText(
+        CharSourceRange::getTokenRange(IneffContExpr->getSourceRange()), SM,
+        LangOpts);
+    StringRef ParamText = Lexer::getSourceText(
+        CharSourceRange::getTokenRange(AlgParam->getSourceRange()), SM,
+        LangOpts);
+    std::string ReplacementText =
+        (llvm::Twine(ContainerText) + (PtrToContainer ? "->" : ".") +
+         AlgDecl->getName() + "(" + ParamText + ")")
+            .str();
+    Hint = FixItHint::CreateReplacement(CallRange, ReplacementText);
+  }
+
+  diag(AlgCall->getLocStart(),
+       "this STL algorithm call should be replaced with a container method")
+      << Hint;
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/misc/InefficientAlgorithmCheck.h b/clang-tidy/misc/InefficientAlgorithmCheck.h
new file mode 100644 (file)
index 0000000..6935b45
--- /dev/null
@@ -0,0 +1,36 @@
+//===--- InefficientAlgorithmCheck.h - clang-tidy----------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_INEFFICIENTALGORITHMCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_INEFFICIENTALGORITHMCHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Warns on inefficient use of STL algorithms on associative containers.
+///
+/// Associative containers implements some of the algorithms as methods which
+/// should be preferred to the algorithms in the algorithm header. The methods
+/// can take advanatage of the order of the elements.
+class InefficientAlgorithmCheck : public ClangTidyCheck {
+public:
+  InefficientAlgorithmCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_INEFFICIENTALGORITHMCHECK_H
diff --git a/clang-tidy/misc/LambdaFunctionNameCheck.cpp b/clang-tidy/misc/LambdaFunctionNameCheck.cpp
new file mode 100644 (file)
index 0000000..85dad5c
--- /dev/null
@@ -0,0 +1,99 @@
+//===--- LambdaFunctionNameCheck.cpp - clang-tidy--------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "LambdaFunctionNameCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Lex/MacroInfo.h"
+#include "clang/Lex/Preprocessor.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+namespace {
+
+// Keep track of macro expansions that contain both __FILE__ and __LINE__. If
+// such a macro also uses __func__ or __FUNCTION__, we don't want to issue a
+// warning because __FILE__ and __LINE__ may be useful even if __func__ or
+// __FUNCTION__ is not, especially if the macro could be used in the context of
+// either a function body or a lambda body.
+class MacroExpansionsWithFileAndLine : public PPCallbacks {
+public:
+  explicit MacroExpansionsWithFileAndLine(
+      LambdaFunctionNameCheck::SourceRangeSet *SME)
+      : SuppressMacroExpansions(SME) {}
+
+  void MacroExpands(const Token &MacroNameTok,
+                    const MacroDefinition &MD, SourceRange Range,
+                    const MacroArgs *Args) override {
+    bool has_file = false;
+    bool has_line = false;
+    for (const auto& T : MD.getMacroInfo()->tokens()) {
+      if (T.is(tok::identifier)) {
+        StringRef IdentName = T.getIdentifierInfo()->getName();
+        if (IdentName == "__FILE__") {
+          has_file = true;
+        } else if (IdentName == "__LINE__") {
+          has_line = true;
+        }
+      }
+    }
+    if (has_file && has_line) {
+      SuppressMacroExpansions->insert(Range);
+    }
+  }
+
+private:
+  LambdaFunctionNameCheck::SourceRangeSet* SuppressMacroExpansions;
+};
+
+} // namespace
+
+void LambdaFunctionNameCheck::registerMatchers(MatchFinder *Finder) {
+  // Match on PredefinedExprs inside a lambda.
+  Finder->addMatcher(predefinedExpr(hasAncestor(lambdaExpr())).bind("E"),
+                     this);
+}
+
+void LambdaFunctionNameCheck::registerPPCallbacks(CompilerInstance &Compiler) {
+  Compiler.getPreprocessor().addPPCallbacks(
+      llvm::make_unique<MacroExpansionsWithFileAndLine>(
+          &SuppressMacroExpansions));
+}
+
+void LambdaFunctionNameCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *E = Result.Nodes.getNodeAs<PredefinedExpr>("E");
+  if (E->getIdentType() != PredefinedExpr::Func &&
+      E->getIdentType() != PredefinedExpr::Function) {
+    // We don't care about other PredefinedExprs.
+    return;
+  }
+  if (E->getLocation().isMacroID()) {
+    auto ER =
+        Result.SourceManager->getImmediateExpansionRange(E->getLocation());
+    if (SuppressMacroExpansions.find(SourceRange(ER.first, ER.second)) !=
+        SuppressMacroExpansions.end()) {
+      // This is a macro expansion for which we should not warn.
+      return;
+    }
+  }
+  diag(E->getLocation(),
+       "inside a lambda, '%0' expands to the name of the function call "
+       "operator; consider capturing the name of the enclosing function "
+       "explicitly")
+      << PredefinedExpr::getIdentTypeName(E->getIdentType());
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/misc/LambdaFunctionNameCheck.h b/clang-tidy/misc/LambdaFunctionNameCheck.h
new file mode 100644 (file)
index 0000000..bd23b00
--- /dev/null
@@ -0,0 +1,51 @@
+//===--- LambdaFunctionNameCheck.h - clang-tidy------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_LAMBDA_FUNCTION_NAME_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_LAMBDA_FUNCTION_NAME_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Detect when __func__ or __FUNCTION__ is being used from within a lambda. In
+/// that context, those expressions expand to the name of the call operator
+/// (i.e., `operator()`).
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/misc-lambda-function-name.html
+class LambdaFunctionNameCheck : public ClangTidyCheck {
+public:
+  struct SourceRangeLessThan {
+    bool operator()(const SourceRange &L, const SourceRange &R) const {
+      if (L.getBegin() == R.getBegin()) {
+        return L.getEnd() < R.getEnd();
+      }
+      return L.getBegin() < R.getBegin();
+    }
+  };
+  using SourceRangeSet = std::set<SourceRange, SourceRangeLessThan>;
+
+  LambdaFunctionNameCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void registerPPCallbacks(CompilerInstance &Compiler) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+  SourceRangeSet SuppressMacroExpansions;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_LAMBDA_FUNCTION_NAME_H
diff --git a/clang-tidy/misc/MacroParenthesesCheck.cpp b/clang-tidy/misc/MacroParenthesesCheck.cpp
new file mode 100644 (file)
index 0000000..59fe772
--- /dev/null
@@ -0,0 +1,260 @@
+//===--- MacroParenthesesCheck.cpp - clang-tidy----------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "MacroParenthesesCheck.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Lex/PPCallbacks.h"
+#include "clang/Lex/Preprocessor.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+namespace {
+class MacroParenthesesPPCallbacks : public PPCallbacks {
+public:
+  MacroParenthesesPPCallbacks(Preprocessor *PP, MacroParenthesesCheck *Check)
+      : PP(PP), Check(Check) {}
+
+  void MacroDefined(const Token &MacroNameTok,
+                    const MacroDirective *MD) override {
+    replacementList(MacroNameTok, MD->getMacroInfo());
+    argument(MacroNameTok, MD->getMacroInfo());
+  }
+
+private:
+  /// Replacement list with calculations should be enclosed in parentheses.
+  void replacementList(const Token &MacroNameTok, const MacroInfo *MI);
+
+  /// Arguments should be enclosed in parentheses.
+  void argument(const Token &MacroNameTok, const MacroInfo *MI);
+
+  Preprocessor *PP;
+  MacroParenthesesCheck *Check;
+};
+} // namespace
+
+/// Is argument surrounded properly with parentheses/braces/squares/commas?
+static bool isSurroundedLeft(const Token &T) {
+  return T.isOneOf(tok::l_paren, tok::l_brace, tok::l_square, tok::comma,
+                   tok::semi);
+}
+
+/// Is argument surrounded properly with parentheses/braces/squares/commas?
+static bool isSurroundedRight(const Token &T) {
+  return T.isOneOf(tok::r_paren, tok::r_brace, tok::r_square, tok::comma,
+                   tok::semi);
+}
+
+/// Is given TokenKind a keyword?
+static bool isKeyword(const Token &T) {
+  // FIXME: better matching of keywords to avoid false positives.
+  return T.isOneOf(tok::kw_case, tok::kw_const, tok::kw_struct);
+}
+
+/// Warning is written when one of these operators are not within parentheses.
+static bool isWarnOp(const Token &T) {
+  // FIXME: This is an initial list of operators. It can be tweaked later to
+  // get more positives or perhaps avoid some false positive.
+  return T.isOneOf(tok::plus, tok::minus, tok::star, tok::slash, tok::percent,
+                   tok::amp, tok::pipe, tok::caret);
+}
+
+/// Is given Token a keyword that is used in variable declarations?
+static bool isVarDeclKeyword(const Token &T) {
+  return T.isOneOf(tok::kw_bool, tok::kw_char, tok::kw_short, tok::kw_int,
+                   tok::kw_long, tok::kw_float, tok::kw_double, tok::kw_const,
+                   tok::kw_enum, tok::kw_inline, tok::kw_static, tok::kw_struct,
+                   tok::kw_signed, tok::kw_unsigned);
+}
+
+/// Is there a possible variable declaration at Tok?
+static bool possibleVarDecl(const MacroInfo *MI, const Token *Tok) {
+  if (Tok == MI->tokens_end())
+    return false;
+
+  // If we see int/short/struct/etc., just assume this is a variable
+  // declaration.
+  if (isVarDeclKeyword(*Tok))
+    return true;
+
+  // Variable declarations start with identifier or coloncolon.
+  if (!Tok->isOneOf(tok::identifier, tok::raw_identifier, tok::coloncolon))
+    return false;
+
+  // Skip possible types, etc
+  while (Tok != MI->tokens_end() &&
+         Tok->isOneOf(tok::identifier, tok::raw_identifier, tok::coloncolon,
+                      tok::star, tok::amp, tok::ampamp, tok::less,
+                      tok::greater))
+    Tok++;
+
+  // Return true for possible variable declarations.
+  return Tok == MI->tokens_end() ||
+         Tok->isOneOf(tok::equal, tok::semi, tok::l_square, tok::l_paren) ||
+         isVarDeclKeyword(*Tok);
+}
+
+void MacroParenthesesPPCallbacks::replacementList(const Token &MacroNameTok,
+                                                  const MacroInfo *MI) {
+  // Make sure macro replacement isn't a variable declaration.
+  if (possibleVarDecl(MI, MI->tokens_begin()))
+    return;
+
+  // Count how deep we are in parentheses/braces/squares.
+  int Count = 0;
+
+  // SourceLocation for error
+  SourceLocation Loc;
+
+  for (auto TI = MI->tokens_begin(), TE = MI->tokens_end(); TI != TE; ++TI) {
+    const Token &Tok = *TI;
+    // Replacement list contains keywords, don't warn about it.
+    if (isKeyword(Tok))
+      return;
+    // When replacement list contains comma/semi don't warn about it.
+    if (Count == 0 && Tok.isOneOf(tok::comma, tok::semi))
+      return;
+    if (Tok.isOneOf(tok::l_paren, tok::l_brace, tok::l_square)) {
+      ++Count;
+    } else if (Tok.isOneOf(tok::r_paren, tok::r_brace, tok::r_square)) {
+      --Count;
+      // If there are unbalanced parentheses don't write any warning
+      if (Count < 0)
+        return;
+    } else if (Count == 0 && isWarnOp(Tok)) {
+      // Heuristic for macros that are clearly not intended to be enclosed in
+      // parentheses, macro starts with operator. For example:
+      // #define X     *10
+      if (TI == MI->tokens_begin() && (TI + 1) != TE &&
+          !Tok.isOneOf(tok::plus, tok::minus))
+        return;
+      // Don't warn about this macro if the last token is a star. For example:
+      // #define X    void *
+      if ((TE - 1)->is(tok::star))
+        return;
+
+      Loc = Tok.getLocation();
+    }
+  }
+  if (Loc.isValid()) {
+    const Token &Last = *(MI->tokens_end() - 1);
+    Check->diag(Loc, "macro replacement list should be enclosed in parentheses")
+        << FixItHint::CreateInsertion(MI->tokens_begin()->getLocation(), "(")
+        << FixItHint::CreateInsertion(Last.getLocation().getLocWithOffset(
+                                          PP->getSpelling(Last).length()),
+                                      ")");
+  }
+}
+
+void MacroParenthesesPPCallbacks::argument(const Token &MacroNameTok,
+                                           const MacroInfo *MI) {
+
+  // Skip variable declaration.
+  bool VarDecl = possibleVarDecl(MI, MI->tokens_begin());
+
+  for (auto TI = MI->tokens_begin(), TE = MI->tokens_end(); TI != TE; ++TI) {
+    // First token.
+    if (TI == MI->tokens_begin())
+      continue;
+
+    // Last token.
+    if ((TI + 1) == MI->tokens_end())
+      continue;
+
+    const Token &Prev = *(TI - 1);
+    const Token &Next = *(TI + 1);
+
+    const Token &Tok = *TI;
+
+    // There should not be extra parentheses in possible variable declaration.
+    if (VarDecl) {
+      if (Tok.isOneOf(tok::equal, tok::semi, tok::l_square, tok::l_paren))
+        VarDecl = false;
+      continue;
+    }
+
+    // Only interested in identifiers.
+    if (!Tok.isOneOf(tok::identifier, tok::raw_identifier))
+      continue;
+
+    // Only interested in macro arguments.
+    if (MI->getParameterNum(Tok.getIdentifierInfo()) < 0)
+      continue;
+
+    // Argument is surrounded with parentheses/squares/braces/commas.
+    if (isSurroundedLeft(Prev) && isSurroundedRight(Next))
+      continue;
+
+    // Don't warn after hash/hashhash or before hashhash.
+    if (Prev.isOneOf(tok::hash, tok::hashhash) || Next.is(tok::hashhash))
+      continue;
+
+    // Argument is a struct member.
+    if (Prev.isOneOf(tok::period, tok::arrow, tok::coloncolon, tok::arrowstar,
+                     tok::periodstar))
+      continue;
+
+    // Argument is a namespace or class.
+    if (Next.is(tok::coloncolon))
+      continue;
+
+    // String concatenation.
+    if (isStringLiteral(Prev.getKind()) || isStringLiteral(Next.getKind()))
+      continue;
+
+    // Type/Var.
+    if (isAnyIdentifier(Prev.getKind()) || isKeyword(Prev) ||
+        isAnyIdentifier(Next.getKind()) || isKeyword(Next))
+      continue;
+
+    // Initialization.
+    if (Next.is(tok::l_paren))
+      continue;
+
+    // Cast.
+    if (Prev.is(tok::l_paren) && Next.is(tok::star) &&
+        TI + 2 != MI->tokens_end() && (TI + 2)->is(tok::r_paren))
+      continue;
+
+    // Assignment/return, i.e. '=x;' or 'return x;'.
+    if (Prev.isOneOf(tok::equal, tok::kw_return) && Next.is(tok::semi))
+      continue;
+
+    // C++ template parameters.
+    if (PP->getLangOpts().CPlusPlus && Prev.isOneOf(tok::comma, tok::less) &&
+        Next.isOneOf(tok::comma, tok::greater))
+      continue;
+
+    // Namespaces.
+    if (Prev.is(tok::kw_namespace))
+      continue;
+
+    // Variadic templates
+    if (MI->isVariadic())
+      continue;
+
+    Check->diag(Tok.getLocation(), "macro argument should be enclosed in "
+                                   "parentheses")
+        << FixItHint::CreateInsertion(Tok.getLocation(), "(")
+        << FixItHint::CreateInsertion(Tok.getLocation().getLocWithOffset(
+                                          PP->getSpelling(Tok).length()),
+                                      ")");
+  }
+}
+
+void MacroParenthesesCheck::registerPPCallbacks(CompilerInstance &Compiler) {
+  Compiler.getPreprocessor().addPPCallbacks(
+      llvm::make_unique<MacroParenthesesPPCallbacks>(
+          &Compiler.getPreprocessor(), this));
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/misc/MacroParenthesesCheck.h b/clang-tidy/misc/MacroParenthesesCheck.h
new file mode 100644 (file)
index 0000000..e398fc6
--- /dev/null
@@ -0,0 +1,43 @@
+//===--- MacroParenthesesCheck.h - clang-tidy--------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MACRO_PARENTHESES_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MACRO_PARENTHESES_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Finds macros that can have unexpected behaviour due to missing parentheses.
+///
+/// Macros are expanded by the preprocessor as-is. As a result, there can be
+/// unexpected behaviour; operators may be evaluated in unexpected order and
+/// unary operators may become binary operators, etc.
+///
+/// When the replacement list has an expression, it is recommended to surround
+/// it with parentheses. This ensures that the macro result is evaluated
+/// completely before it is used.
+///
+/// It is also recommended to surround macro arguments in the replacement list
+/// with parentheses. This ensures that the argument value is calculated
+/// properly.
+class MacroParenthesesCheck : public ClangTidyCheck {
+public:
+  MacroParenthesesCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerPPCallbacks(CompilerInstance &Compiler) override;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MACRO_PARENTHESES_H
diff --git a/clang-tidy/misc/MacroRepeatedSideEffectsCheck.cpp b/clang-tidy/misc/MacroRepeatedSideEffectsCheck.cpp
new file mode 100644 (file)
index 0000000..1bbed46
--- /dev/null
@@ -0,0 +1,184 @@
+//===--- MacroRepeatedSideEffectsCheck.cpp - clang-tidy--------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "MacroRepeatedSideEffectsCheck.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Lex/MacroArgs.h"
+#include "clang/Lex/PPCallbacks.h"
+#include "clang/Lex/Preprocessor.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+namespace {
+class MacroRepeatedPPCallbacks : public PPCallbacks {
+public:
+  MacroRepeatedPPCallbacks(ClangTidyCheck &Check, Preprocessor &PP)
+      : Check(Check), PP(PP) {}
+
+  void MacroExpands(const Token &MacroNameTok, const MacroDefinition &MD,
+                    SourceRange Range, const MacroArgs *Args) override;
+
+private:
+  ClangTidyCheck &Check;
+  Preprocessor &PP;
+
+  unsigned countArgumentExpansions(const MacroInfo *MI,
+                                   const IdentifierInfo *Arg) const;
+
+  bool hasSideEffects(const Token *ResultArgToks) const;
+};
+} // End of anonymous namespace.
+
+void MacroRepeatedPPCallbacks::MacroExpands(const Token &MacroNameTok,
+                                            const MacroDefinition &MD,
+                                            SourceRange Range,
+                                            const MacroArgs *Args) {
+  // Ignore macro argument expansions.
+  if (!Range.getBegin().isFileID())
+    return;
+
+  const MacroInfo *MI = MD.getMacroInfo();
+
+  // Bail out if the contents of the macro are containing keywords that are
+  // making the macro too complex.
+  if (std::find_if(
+          MI->tokens().begin(), MI->tokens().end(), [](const Token &T) {
+            return T.isOneOf(tok::kw_if, tok::kw_else, tok::kw_switch,
+                             tok::kw_case, tok::kw_break, tok::kw_while,
+                             tok::kw_do, tok::kw_for, tok::kw_continue,
+                             tok::kw_goto, tok::kw_return);
+          }) != MI->tokens().end())
+    return;
+
+  for (unsigned ArgNo = 0U; ArgNo < MI->getNumParams(); ++ArgNo) {
+    const IdentifierInfo *Arg = *(MI->param_begin() + ArgNo);
+    const Token *ResultArgToks = Args->getUnexpArgument(ArgNo);
+
+    if (hasSideEffects(ResultArgToks) &&
+        countArgumentExpansions(MI, Arg) >= 2) {
+      Check.diag(ResultArgToks->getLocation(),
+                 "side effects in the %ordinal0 macro argument %1 are "
+                 "repeated in macro expansion")
+          << (ArgNo + 1) << Arg;
+      Check.diag(MI->getDefinitionLoc(), "macro %0 defined here",
+                 DiagnosticIDs::Note)
+          << MacroNameTok.getIdentifierInfo();
+    }
+  }
+}
+
+unsigned MacroRepeatedPPCallbacks::countArgumentExpansions(
+    const MacroInfo *MI, const IdentifierInfo *Arg) const {
+  // Current argument count. When moving forward to a different control-flow
+  // path this can decrease.
+  unsigned Current = 0;
+  // Max argument count.
+  unsigned Max = 0;
+  bool SkipParen = false;
+  int SkipParenCount = 0;
+  // Has a __builtin_constant_p been found?
+  bool FoundBuiltin = false;
+  bool PrevTokenIsHash = false;
+  // Count when "?" is reached. The "Current" will get this value when the ":"
+  // is reached.
+  std::stack<unsigned, SmallVector<unsigned, 8>> CountAtQuestion;
+  for (const auto &T : MI->tokens()) {
+    // The result of __builtin_constant_p(x) is 0 if x is a macro argument
+    // with side effects. If we see a __builtin_constant_p(x) followed by a
+    // "?" "&&" or "||", then we need to reason about control flow to report
+    // warnings correctly. Until such reasoning is added, bail out when this
+    // happens.
+    if (FoundBuiltin && T.isOneOf(tok::question, tok::ampamp, tok::pipepipe))
+      return Max;
+
+    // Skip stringified tokens.
+    if (T.is(tok::hash)) {
+      PrevTokenIsHash = true;
+      continue;
+    }
+    if (PrevTokenIsHash) {
+      PrevTokenIsHash = false;
+      continue;
+    }
+
+    // Handling of ? and :.
+    if (T.is(tok::question)) {
+      CountAtQuestion.push(Current);
+    } else if (T.is(tok::colon)) {
+      if (CountAtQuestion.empty())
+        return 0;
+      Current = CountAtQuestion.top();
+      CountAtQuestion.pop();
+    }
+
+    // If current token is a parenthesis, skip it.
+    if (SkipParen) {
+      if (T.is(tok::l_paren))
+        SkipParenCount++;
+      else if (T.is(tok::r_paren))
+        SkipParenCount--;
+      SkipParen = (SkipParenCount != 0);
+      if (SkipParen)
+        continue;
+    }
+
+    IdentifierInfo *TII = T.getIdentifierInfo();
+    // If not existent, skip it.
+    if (TII == nullptr)
+      continue;
+
+    // If a __builtin_constant_p is found within the macro definition, don't
+    // count arguments inside the parentheses and remember that it has been
+    // seen in case there are "?", "&&" or "||" operators later.
+    if (TII->getBuiltinID() == Builtin::BI__builtin_constant_p) {
+      FoundBuiltin = true;
+      SkipParen = true;
+      continue;
+    }
+
+    // If another macro is found within the macro definition, skip the macro
+    // and the eventual arguments.
+    if (TII->hasMacroDefinition()) {
+      const MacroInfo *M = PP.getMacroDefinition(TII).getMacroInfo();
+      if (M != nullptr && M->isFunctionLike())
+        SkipParen = true;
+      continue;
+    }
+
+    // Count argument.
+    if (TII == Arg) {
+      Current++;
+      if (Current > Max)
+        Max = Current;
+    }
+  }
+  return Max;
+}
+
+bool MacroRepeatedPPCallbacks::hasSideEffects(
+    const Token *ResultArgToks) const {
+  for (; ResultArgToks->isNot(tok::eof); ++ResultArgToks) {
+    if (ResultArgToks->isOneOf(tok::plusplus, tok::minusminus))
+      return true;
+  }
+  return false;
+}
+
+void MacroRepeatedSideEffectsCheck::registerPPCallbacks(
+    CompilerInstance &Compiler) {
+  Compiler.getPreprocessor().addPPCallbacks(
+      ::llvm::make_unique<MacroRepeatedPPCallbacks>(
+          *this, Compiler.getPreprocessor()));
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/misc/MacroRepeatedSideEffectsCheck.h b/clang-tidy/misc/MacroRepeatedSideEffectsCheck.h
new file mode 100644 (file)
index 0000000..10ff842
--- /dev/null
@@ -0,0 +1,31 @@
+//===--- MacroRepeatedSideEffectsCheck.h - clang-tidy -----------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MACRO_REPEATED_SIDE_EFFECTS_CHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MACRO_REPEATED_SIDE_EFFECTS_CHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Checks for repeated argument with side effects in macros.
+class MacroRepeatedSideEffectsCheck : public ClangTidyCheck {
+public:
+  MacroRepeatedSideEffectsCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerPPCallbacks(CompilerInstance &Compiler) override;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MACRO_REPEATED_SIDE_EFFECTS_CHECK_H
diff --git a/clang-tidy/misc/MiscTidyModule.cpp b/clang-tidy/misc/MiscTidyModule.cpp
new file mode 100644 (file)
index 0000000..5448c04
--- /dev/null
@@ -0,0 +1,163 @@
+//===--- MiscTidyModule.cpp - clang-tidy ----------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "../ClangTidy.h"
+#include "../ClangTidyModule.h"
+#include "../ClangTidyModuleRegistry.h"
+#include "ArgumentCommentCheck.h"
+#include "AssertSideEffectCheck.h"
+#include "BoolPointerImplicitConversionCheck.h"
+#include "DanglingHandleCheck.h"
+#include "DefinitionsInHeadersCheck.h"
+#include "FoldInitTypeCheck.h"
+#include "ForwardDeclarationNamespaceCheck.h"
+#include "ForwardingReferenceOverloadCheck.h"
+#include "InaccurateEraseCheck.h"
+#include "IncorrectRoundings.h"
+#include "InefficientAlgorithmCheck.h"
+#include "LambdaFunctionNameCheck.h"
+#include "MacroParenthesesCheck.h"
+#include "MacroRepeatedSideEffectsCheck.h"
+#include "MisplacedConstCheck.h"
+#include "MisplacedWideningCastCheck.h"
+#include "MoveConstantArgumentCheck.h"
+#include "MoveConstructorInitCheck.h"
+#include "MoveForwardingReferenceCheck.h"
+#include "MultipleStatementMacroCheck.h"
+#include "NewDeleteOverloadsCheck.h"
+#include "NoexceptMoveConstructorCheck.h"
+#include "NonCopyableObjects.h"
+#include "RedundantExpressionCheck.h"
+#include "SizeofContainerCheck.h"
+#include "SizeofExpressionCheck.h"
+#include "StaticAssertCheck.h"
+#include "StringCompareCheck.h"
+#include "StringConstructorCheck.h"
+#include "StringIntegerAssignmentCheck.h"
+#include "StringLiteralWithEmbeddedNulCheck.h"
+#include "SuspiciousEnumUsageCheck.h"
+#include "SuspiciousMissingCommaCheck.h"
+#include "SuspiciousSemicolonCheck.h"
+#include "SuspiciousStringCompareCheck.h"
+#include "SwappedArgumentsCheck.h"
+#include "ThrowByValueCatchByReferenceCheck.h"
+#include "UnconventionalAssignOperatorCheck.h"
+#include "UndelegatedConstructor.h"
+#include "UniqueptrResetReleaseCheck.h"
+#include "UnusedAliasDeclsCheck.h"
+#include "UnusedParametersCheck.h"
+#include "UnusedRAIICheck.h"
+#include "UnusedUsingDeclsCheck.h"
+#include "UseAfterMoveCheck.h"
+#include "VirtualNearMissCheck.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+class MiscModule : public ClangTidyModule {
+public:
+  void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
+    CheckFactories.registerCheck<ArgumentCommentCheck>("misc-argument-comment");
+    CheckFactories.registerCheck<AssertSideEffectCheck>(
+        "misc-assert-side-effect");
+    CheckFactories.registerCheck<ForwardingReferenceOverloadCheck>(
+        "misc-forwarding-reference-overload");
+    CheckFactories.registerCheck<LambdaFunctionNameCheck>(
+        "misc-lambda-function-name");
+    CheckFactories.registerCheck<MisplacedConstCheck>("misc-misplaced-const");
+    CheckFactories.registerCheck<UnconventionalAssignOperatorCheck>(
+        "misc-unconventional-assign-operator");
+    CheckFactories.registerCheck<BoolPointerImplicitConversionCheck>(
+        "misc-bool-pointer-implicit-conversion");
+    CheckFactories.registerCheck<DanglingHandleCheck>("misc-dangling-handle");
+    CheckFactories.registerCheck<DefinitionsInHeadersCheck>(
+        "misc-definitions-in-headers");
+    CheckFactories.registerCheck<FoldInitTypeCheck>("misc-fold-init-type");
+    CheckFactories.registerCheck<ForwardDeclarationNamespaceCheck>(
+        "misc-forward-declaration-namespace");
+    CheckFactories.registerCheck<InaccurateEraseCheck>("misc-inaccurate-erase");
+    CheckFactories.registerCheck<IncorrectRoundings>(
+        "misc-incorrect-roundings");
+    CheckFactories.registerCheck<InefficientAlgorithmCheck>(
+        "misc-inefficient-algorithm");
+    CheckFactories.registerCheck<MacroParenthesesCheck>(
+        "misc-macro-parentheses");
+    CheckFactories.registerCheck<MacroRepeatedSideEffectsCheck>(
+        "misc-macro-repeated-side-effects");
+    CheckFactories.registerCheck<MisplacedWideningCastCheck>(
+        "misc-misplaced-widening-cast");
+    CheckFactories.registerCheck<MoveConstantArgumentCheck>(
+        "misc-move-const-arg");
+    CheckFactories.registerCheck<MoveConstructorInitCheck>(
+        "misc-move-constructor-init");
+    CheckFactories.registerCheck<MoveForwardingReferenceCheck>(
+        "misc-move-forwarding-reference");
+    CheckFactories.registerCheck<MultipleStatementMacroCheck>(
+        "misc-multiple-statement-macro");
+    CheckFactories.registerCheck<NewDeleteOverloadsCheck>(
+        "misc-new-delete-overloads");
+    CheckFactories.registerCheck<NoexceptMoveConstructorCheck>(
+        "misc-noexcept-move-constructor");
+    CheckFactories.registerCheck<NonCopyableObjectsCheck>(
+        "misc-non-copyable-objects");
+    CheckFactories.registerCheck<RedundantExpressionCheck>(
+        "misc-redundant-expression");
+    CheckFactories.registerCheck<SizeofContainerCheck>("misc-sizeof-container");
+    CheckFactories.registerCheck<SizeofExpressionCheck>(
+        "misc-sizeof-expression");
+    CheckFactories.registerCheck<StaticAssertCheck>("misc-static-assert");
+    CheckFactories.registerCheck<StringCompareCheck>("misc-string-compare");
+    CheckFactories.registerCheck<StringConstructorCheck>(
+        "misc-string-constructor");
+    CheckFactories.registerCheck<StringIntegerAssignmentCheck>(
+        "misc-string-integer-assignment");
+    CheckFactories.registerCheck<StringLiteralWithEmbeddedNulCheck>(
+        "misc-string-literal-with-embedded-nul");
+    CheckFactories.registerCheck<SuspiciousEnumUsageCheck>(
+        "misc-suspicious-enum-usage");
+    CheckFactories.registerCheck<SuspiciousMissingCommaCheck>(
+        "misc-suspicious-missing-comma");
+    CheckFactories.registerCheck<SuspiciousSemicolonCheck>(
+        "misc-suspicious-semicolon");
+    CheckFactories.registerCheck<SuspiciousStringCompareCheck>(
+        "misc-suspicious-string-compare");
+    CheckFactories.registerCheck<SwappedArgumentsCheck>(
+        "misc-swapped-arguments");
+    CheckFactories.registerCheck<ThrowByValueCatchByReferenceCheck>(
+        "misc-throw-by-value-catch-by-reference");
+    CheckFactories.registerCheck<UndelegatedConstructorCheck>(
+        "misc-undelegated-constructor");
+    CheckFactories.registerCheck<UniqueptrResetReleaseCheck>(
+        "misc-uniqueptr-reset-release");
+    CheckFactories.registerCheck<UnusedAliasDeclsCheck>(
+        "misc-unused-alias-decls");
+    CheckFactories.registerCheck<UnusedParametersCheck>(
+        "misc-unused-parameters");
+    CheckFactories.registerCheck<UnusedRAIICheck>("misc-unused-raii");
+    CheckFactories.registerCheck<UnusedUsingDeclsCheck>(
+        "misc-unused-using-decls");
+    CheckFactories.registerCheck<UseAfterMoveCheck>("misc-use-after-move");
+    CheckFactories.registerCheck<VirtualNearMissCheck>(
+        "misc-virtual-near-miss");
+  }
+};
+
+} // namespace misc
+
+// Register the MiscTidyModule using this statically initialized variable.
+static ClangTidyModuleRegistry::Add<misc::MiscModule>
+    X("misc-module", "Adds miscellaneous lint checks.");
+
+// This anchor is used to force the linker to link in the generated object file
+// and thus register the MiscModule.
+volatile int MiscModuleAnchorSource = 0;
+
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/misc/MisplacedConstCheck.cpp b/clang-tidy/misc/MisplacedConstCheck.cpp
new file mode 100644 (file)
index 0000000..515b22c
--- /dev/null
@@ -0,0 +1,63 @@
+//===--- MisplacedConstCheck.cpp - clang-tidy------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "MisplacedConstCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+void MisplacedConstCheck::registerMatchers(MatchFinder *Finder) {
+  Finder->addMatcher(
+      valueDecl(hasType(isConstQualified()),
+                hasType(typedefType(hasDeclaration(
+                    typedefDecl(hasType(pointerType(unless(pointee(
+                                    anyOf(isConstQualified(),
+                                          ignoringParens(functionType())))))))
+                        .bind("typedef")))))
+          .bind("decl"),
+      this);
+}
+
+static QualType guessAlternateQualification(ASTContext &Context, QualType QT) {
+  // We're given a QualType from a typedef where the qualifiers apply to the
+  // pointer instead of the pointee. Strip the const qualifier from the pointer
+  // type and add it to the pointee instead.
+  if (!QT->isPointerType())
+    return QT;
+
+  Qualifiers Quals = QT.getLocalQualifiers();
+  Quals.removeConst();
+
+  QualType NewQT = Context.getPointerType(
+      QualType(QT->getPointeeType().getTypePtr(), Qualifiers::Const));
+  return NewQT.withCVRQualifiers(Quals.getCVRQualifiers());
+}
+
+void MisplacedConstCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *Var = Result.Nodes.getNodeAs<ValueDecl>("decl");
+  const auto *Typedef = Result.Nodes.getNodeAs<TypedefDecl>("typedef");
+  ASTContext &Ctx = *Result.Context;
+  QualType CanQT = Var->getType().getCanonicalType();
+
+  diag(Var->getLocation(), "%0 declared with a const-qualified typedef type; "
+                           "results in the type being '%1' instead of '%2'")
+      << Var << CanQT.getAsString(Ctx.getPrintingPolicy())
+      << guessAlternateQualification(Ctx, CanQT)
+             .getAsString(Ctx.getPrintingPolicy());
+  diag(Typedef->getLocation(), "typedef declared here", DiagnosticIDs::Note);
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/misc/MisplacedConstCheck.h b/clang-tidy/misc/MisplacedConstCheck.h
new file mode 100644 (file)
index 0000000..410edf7
--- /dev/null
@@ -0,0 +1,36 @@
+//===--- MisplacedConstCheck.h - clang-tidy----------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MISPLACED_CONST_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MISPLACED_CONST_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// This check diagnoses when a const qualifier is applied to a typedef to a
+/// pointer type rather than to the pointee.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/misc-misplaced-const.html
+class MisplacedConstCheck : public ClangTidyCheck {
+public:
+  MisplacedConstCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MISPLACED_CONST_H
diff --git a/clang-tidy/misc/MisplacedWideningCastCheck.cpp b/clang-tidy/misc/MisplacedWideningCastCheck.cpp
new file mode 100644 (file)
index 0000000..269004f
--- /dev/null
@@ -0,0 +1,229 @@
+//===--- MisplacedWideningCastCheck.cpp - clang-tidy-----------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "MisplacedWideningCastCheck.h"
+#include "../utils/Matchers.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+MisplacedWideningCastCheck::MisplacedWideningCastCheck(
+    StringRef Name, ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      CheckImplicitCasts(Options.get("CheckImplicitCasts", false)) {}
+
+void MisplacedWideningCastCheck::storeOptions(
+    ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "CheckImplicitCasts", CheckImplicitCasts);
+}
+
+void MisplacedWideningCastCheck::registerMatchers(MatchFinder *Finder) {
+  const auto Calc =
+      expr(anyOf(binaryOperator(
+                     anyOf(hasOperatorName("+"), hasOperatorName("-"),
+                           hasOperatorName("*"), hasOperatorName("<<"))),
+                 unaryOperator(hasOperatorName("~"))),
+           hasType(isInteger()))
+          .bind("Calc");
+
+  const auto ExplicitCast = explicitCastExpr(hasDestinationType(isInteger()),
+                                             has(ignoringParenImpCasts(Calc)));
+  const auto ImplicitCast =
+      implicitCastExpr(hasImplicitDestinationType(isInteger()),
+                       has(ignoringParenImpCasts(Calc)));
+  const auto Cast = expr(anyOf(ExplicitCast, ImplicitCast)).bind("Cast");
+
+  Finder->addMatcher(varDecl(hasInitializer(Cast)), this);
+  Finder->addMatcher(returnStmt(hasReturnValue(Cast)), this);
+  Finder->addMatcher(callExpr(hasAnyArgument(Cast)), this);
+  Finder->addMatcher(binaryOperator(hasOperatorName("="), hasRHS(Cast)), this);
+  Finder->addMatcher(
+      binaryOperator(matchers::isComparisonOperator(), hasEitherOperand(Cast)),
+      this);
+}
+
+static unsigned getMaxCalculationWidth(const ASTContext &Context,
+                                       const Expr *E) {
+  E = E->IgnoreParenImpCasts();
+
+  if (const auto *Bop = dyn_cast<BinaryOperator>(E)) {
+    unsigned LHSWidth = getMaxCalculationWidth(Context, Bop->getLHS());
+    unsigned RHSWidth = getMaxCalculationWidth(Context, Bop->getRHS());
+    if (Bop->getOpcode() == BO_Mul)
+      return LHSWidth + RHSWidth;
+    if (Bop->getOpcode() == BO_Add)
+      return std::max(LHSWidth, RHSWidth) + 1;
+    if (Bop->getOpcode() == BO_Rem) {
+      llvm::APSInt Val;
+      if (Bop->getRHS()->EvaluateAsInt(Val, Context))
+        return Val.getActiveBits();
+    } else if (Bop->getOpcode() == BO_Shl) {
+      llvm::APSInt Bits;
+      if (Bop->getRHS()->EvaluateAsInt(Bits, Context)) {
+        // We don't handle negative values and large values well. It is assumed
+        // that compiler warnings are written for such values so the user will
+        // fix that.
+        return LHSWidth + Bits.getExtValue();
+      }
+
+      // Unknown bitcount, assume there is truncation.
+      return 1024U;
+    }
+  } else if (const auto *Uop = dyn_cast<UnaryOperator>(E)) {
+    // There is truncation when ~ is used.
+    if (Uop->getOpcode() == UO_Not)
+      return 1024U;
+
+    QualType T = Uop->getType();
+    return T->isIntegerType() ? Context.getIntWidth(T) : 1024U;
+  } else if (const auto *I = dyn_cast<IntegerLiteral>(E)) {
+    return I->getValue().getActiveBits();
+  }
+
+  return Context.getIntWidth(E->getType());
+}
+
+static int relativeIntSizes(BuiltinType::Kind Kind) {
+  switch (Kind) {
+  case BuiltinType::UChar:
+    return 1;
+  case BuiltinType::SChar:
+    return 1;
+  case BuiltinType::Char_U:
+    return 1;
+  case BuiltinType::Char_S:
+    return 1;
+  case BuiltinType::UShort:
+    return 2;
+  case BuiltinType::Short:
+    return 2;
+  case BuiltinType::UInt:
+    return 3;
+  case BuiltinType::Int:
+    return 3;
+  case BuiltinType::ULong:
+    return 4;
+  case BuiltinType::Long:
+    return 4;
+  case BuiltinType::ULongLong:
+    return 5;
+  case BuiltinType::LongLong:
+    return 5;
+  case BuiltinType::UInt128:
+    return 6;
+  case BuiltinType::Int128:
+    return 6;
+  default:
+    return 0;
+  }
+}
+
+static int relativeCharSizes(BuiltinType::Kind Kind) {
+  switch (Kind) {
+  case BuiltinType::UChar:
+    return 1;
+  case BuiltinType::SChar:
+    return 1;
+  case BuiltinType::Char_U:
+    return 1;
+  case BuiltinType::Char_S:
+    return 1;
+  case BuiltinType::Char16:
+    return 2;
+  case BuiltinType::Char32:
+    return 3;
+  default:
+    return 0;
+  }
+}
+
+static int relativeCharSizesW(BuiltinType::Kind Kind) {
+  switch (Kind) {
+  case BuiltinType::UChar:
+    return 1;
+  case BuiltinType::SChar:
+    return 1;
+  case BuiltinType::Char_U:
+    return 1;
+  case BuiltinType::Char_S:
+    return 1;
+  case BuiltinType::WChar_U:
+    return 2;
+  case BuiltinType::WChar_S:
+    return 2;
+  default:
+    return 0;
+  }
+}
+
+static bool isFirstWider(BuiltinType::Kind First, BuiltinType::Kind Second) {
+  int FirstSize, SecondSize;
+  if ((FirstSize = relativeIntSizes(First)) != 0 &&
+      (SecondSize = relativeIntSizes(Second)) != 0)
+    return FirstSize > SecondSize;
+  if ((FirstSize = relativeCharSizes(First)) != 0 &&
+      (SecondSize = relativeCharSizes(Second)) != 0)
+    return FirstSize > SecondSize;
+  if ((FirstSize = relativeCharSizesW(First)) != 0 &&
+      (SecondSize = relativeCharSizesW(Second)) != 0)
+    return FirstSize > SecondSize;
+  return false;
+}
+
+void MisplacedWideningCastCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *Cast = Result.Nodes.getNodeAs<CastExpr>("Cast");
+  if (!CheckImplicitCasts && isa<ImplicitCastExpr>(Cast))
+    return;
+  if (Cast->getLocStart().isMacroID())
+    return;
+
+  const auto *Calc = Result.Nodes.getNodeAs<Expr>("Calc");
+  if (Calc->getLocStart().isMacroID())
+    return;
+
+  ASTContext &Context = *Result.Context;
+
+  QualType CastType = Cast->getType();
+  QualType CalcType = Calc->getType();
+
+  // Explicit truncation using cast.
+  if (Context.getIntWidth(CastType) < Context.getIntWidth(CalcType))
+    return;
+
+  // If CalcType and CastType have same size then there is no real danger, but
+  // there can be a portability problem.
+
+  if (Context.getIntWidth(CastType) == Context.getIntWidth(CalcType)) {
+    const auto *CastBuiltinType =
+        dyn_cast<BuiltinType>(CastType->getUnqualifiedDesugaredType());
+    const auto *CalcBuiltinType =
+        dyn_cast<BuiltinType>(CalcType->getUnqualifiedDesugaredType());
+    if (CastBuiltinType && CalcBuiltinType &&
+        !isFirstWider(CastBuiltinType->getKind(), CalcBuiltinType->getKind()))
+      return;
+  }
+
+  // Don't write a warning if we can easily see that the result is not
+  // truncated.
+  if (Context.getIntWidth(CalcType) >= getMaxCalculationWidth(Context, Calc))
+    return;
+
+  diag(Cast->getLocStart(), "either cast from %0 to %1 is ineffective, or "
+                            "there is loss of precision before the conversion")
+      << CalcType << CastType;
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/misc/MisplacedWideningCastCheck.h b/clang-tidy/misc/MisplacedWideningCastCheck.h
new file mode 100644 (file)
index 0000000..1c3bc4a
--- /dev/null
@@ -0,0 +1,46 @@
+//===--- MisplacedWideningCastCheck.h - clang-tidy---------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MISPLACED_WIDENING_CAST_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MISPLACED_WIDENING_CAST_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Find casts of calculation results to bigger type. Typically from int to
+/// long. If the intention of the cast is to avoid loss of precision then
+/// the cast is misplaced, and there can be loss of precision. Otherwise
+/// such cast is ineffective.
+///
+/// There is one option:
+///
+///   - `CheckImplicitCasts`: Whether to check implicit casts as well which may
+//      be the most common case. Enabled by default.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/misc-misplaced-widening-cast.html
+class MisplacedWideningCastCheck : public ClangTidyCheck {
+public:
+  MisplacedWideningCastCheck(StringRef Name, ClangTidyContext *Context);
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+  const bool CheckImplicitCasts;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif
diff --git a/clang-tidy/misc/MoveConstantArgumentCheck.cpp b/clang-tidy/misc/MoveConstantArgumentCheck.cpp
new file mode 100644 (file)
index 0000000..cc2d353
--- /dev/null
@@ -0,0 +1,113 @@
+//===--- MoveConstantArgumentCheck.cpp - clang-tidy -----------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "MoveConstantArgumentCheck.h"
+
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+static void ReplaceCallWithArg(const CallExpr *Call, DiagnosticBuilder &Diag,
+                               const SourceManager &SM,
+                               const LangOptions &LangOpts) {
+  const Expr *Arg = Call->getArg(0);
+
+  CharSourceRange BeforeArgumentsRange = Lexer::makeFileCharRange(
+      CharSourceRange::getCharRange(Call->getLocStart(), Arg->getLocStart()),
+      SM, LangOpts);
+  CharSourceRange AfterArgumentsRange = Lexer::makeFileCharRange(
+      CharSourceRange::getCharRange(Call->getLocEnd(),
+                                    Call->getLocEnd().getLocWithOffset(1)),
+      SM, LangOpts);
+
+  if (BeforeArgumentsRange.isValid() && AfterArgumentsRange.isValid()) {
+    Diag << FixItHint::CreateRemoval(BeforeArgumentsRange)
+         << FixItHint::CreateRemoval(AfterArgumentsRange);
+  }
+}
+
+void MoveConstantArgumentCheck::registerMatchers(MatchFinder *Finder) {
+  if (!getLangOpts().CPlusPlus)
+    return;
+
+  auto MoveCallMatcher =
+      callExpr(callee(functionDecl(hasName("::std::move"))), argumentCountIs(1),
+               unless(isInTemplateInstantiation()))
+          .bind("call-move");
+
+  Finder->addMatcher(MoveCallMatcher, this);
+
+  auto ConstParamMatcher = forEachArgumentWithParam(
+      MoveCallMatcher, parmVarDecl(hasType(references(isConstQualified()))));
+
+  Finder->addMatcher(callExpr(ConstParamMatcher).bind("receiving-expr"), this);
+  Finder->addMatcher(cxxConstructExpr(ConstParamMatcher).bind("receiving-expr"),
+                     this);
+}
+
+void MoveConstantArgumentCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *CallMove = Result.Nodes.getNodeAs<CallExpr>("call-move");
+  const auto *ReceivingExpr = Result.Nodes.getNodeAs<Expr>("receiving-expr");
+  const Expr *Arg = CallMove->getArg(0);
+  SourceManager &SM = Result.Context->getSourceManager();
+
+  CharSourceRange MoveRange =
+      CharSourceRange::getCharRange(CallMove->getSourceRange());
+  CharSourceRange FileMoveRange =
+      Lexer::makeFileCharRange(MoveRange, SM, getLangOpts());
+  if (!FileMoveRange.isValid())
+    return;
+
+  bool IsConstArg = Arg->getType().isConstQualified();
+  bool IsTriviallyCopyable =
+      Arg->getType().isTriviallyCopyableType(*Result.Context);
+
+  if (IsConstArg || IsTriviallyCopyable) {
+    if (const CXXRecordDecl *R = Arg->getType()->getAsCXXRecordDecl()) {
+      // According to [expr.prim.lambda]p3, "whether the closure type is
+      // trivially copyable" property can be changed by the implementation of
+      // the language, so we shouldn't rely on it when issuing diagnostics.
+      if (R->isLambda())
+        return;
+      // Don't warn when the type is not copyable.
+      for (const auto *Ctor : R->ctors()) {
+        if (Ctor->isCopyConstructor() && Ctor->isDeleted())
+          return;
+      }
+    }
+    bool IsVariable = isa<DeclRefExpr>(Arg);
+    const auto *Var =
+        IsVariable ? dyn_cast<DeclRefExpr>(Arg)->getDecl() : nullptr;
+    auto Diag = diag(FileMoveRange.getBegin(),
+                     "std::move of the %select{|const }0"
+                     "%select{expression|variable %4}1 "
+                     "%select{|of the trivially-copyable type %5 }2"
+                     "has no effect; remove std::move()"
+                     "%select{| or make the variable non-const}3")
+                << IsConstArg << IsVariable << IsTriviallyCopyable
+                << (IsConstArg && IsVariable && !IsTriviallyCopyable) << Var
+                << Arg->getType();
+
+    ReplaceCallWithArg(CallMove, Diag, SM, getLangOpts());
+  } else if (ReceivingExpr) {
+    auto Diag = diag(FileMoveRange.getBegin(),
+                     "passing result of std::move() as a const reference "
+                     "argument; no move will actually happen");
+
+    ReplaceCallWithArg(CallMove, Diag, SM, getLangOpts());
+  }
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/misc/MoveConstantArgumentCheck.h b/clang-tidy/misc/MoveConstantArgumentCheck.h
new file mode 100644 (file)
index 0000000..27aa06f
--- /dev/null
@@ -0,0 +1,31 @@
+//===--- MoveConstantArgumentCheck.h - clang-tidy -------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MOVECONSTANTARGUMENTCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MOVECONSTANTARGUMENTCHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+class MoveConstantArgumentCheck : public ClangTidyCheck {
+public:
+  MoveConstantArgumentCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MOVECONSTANTARGUMENTCHECK_H
diff --git a/clang-tidy/misc/MoveConstructorInitCheck.cpp b/clang-tidy/misc/MoveConstructorInitCheck.cpp
new file mode 100644 (file)
index 0000000..081c439
--- /dev/null
@@ -0,0 +1,110 @@
+//===--- MoveConstructorInitCheck.cpp - clang-tidy-------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "MoveConstructorInitCheck.h"
+#include "../utils/Matchers.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Lex/Preprocessor.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+MoveConstructorInitCheck::MoveConstructorInitCheck(StringRef Name,
+                                                   ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      IncludeStyle(utils::IncludeSorter::parseIncludeStyle(
+          Options.get("IncludeStyle", "llvm"))) {}
+
+void MoveConstructorInitCheck::registerMatchers(MatchFinder *Finder) {
+  // Only register the matchers for C++11; the functionality currently does not
+  // provide any benefit to other languages, despite being benign.
+  if (!getLangOpts().CPlusPlus11)
+    return;
+
+  Finder->addMatcher(
+      cxxConstructorDecl(
+          unless(isImplicit()),
+          allOf(isMoveConstructor(),
+                hasAnyConstructorInitializer(
+                    cxxCtorInitializer(
+                        withInitializer(cxxConstructExpr(hasDeclaration(
+                            cxxConstructorDecl(isCopyConstructor())
+                                .bind("ctor")))))
+                        .bind("move-init")))),
+      this);
+}
+
+void MoveConstructorInitCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *CopyCtor = Result.Nodes.getNodeAs<CXXConstructorDecl>("ctor");
+  const auto *Initializer =
+      Result.Nodes.getNodeAs<CXXCtorInitializer>("move-init");
+
+  // Do not diagnose if the expression used to perform the initialization is a
+  // trivially-copyable type.
+  QualType QT = Initializer->getInit()->getType();
+  if (QT.isTriviallyCopyableType(*Result.Context))
+    return;
+
+  if (QT.isConstQualified())
+    return;
+
+  const auto *RD = QT->getAsCXXRecordDecl();
+  if (RD && RD->isTriviallyCopyable())
+    return;
+
+  // Diagnose when the class type has a move constructor available, but the
+  // ctor-initializer uses the copy constructor instead.
+  const CXXConstructorDecl *Candidate = nullptr;
+  for (const auto *Ctor : CopyCtor->getParent()->ctors()) {
+    if (Ctor->isMoveConstructor() && Ctor->getAccess() <= AS_protected &&
+        !Ctor->isDeleted()) {
+      // The type has a move constructor that is at least accessible to the
+      // initializer.
+      //
+      // FIXME: Determine whether the move constructor is a viable candidate
+      // for the ctor-initializer, perhaps provide a fixit that suggests
+      // using std::move().
+      Candidate = Ctor;
+      break;
+    }
+  }
+
+  if (Candidate) {
+    // There's a move constructor candidate that the caller probably intended
+    // to call instead.
+    diag(Initializer->getSourceLocation(),
+         "move constructor initializes %0 by calling a copy constructor")
+        << (Initializer->isBaseInitializer() ? "base class" : "class member");
+    diag(CopyCtor->getLocation(), "copy constructor being called",
+         DiagnosticIDs::Note);
+    diag(Candidate->getLocation(), "candidate move constructor here",
+         DiagnosticIDs::Note);
+  }
+}
+
+void MoveConstructorInitCheck::registerPPCallbacks(CompilerInstance &Compiler) {
+  Inserter.reset(new utils::IncludeInserter(
+      Compiler.getSourceManager(), Compiler.getLangOpts(), IncludeStyle));
+  Compiler.getPreprocessor().addPPCallbacks(Inserter->CreatePPCallbacks());
+}
+
+void MoveConstructorInitCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "IncludeStyle",
+                utils::IncludeSorter::toString(IncludeStyle));
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/misc/MoveConstructorInitCheck.h b/clang-tidy/misc/MoveConstructorInitCheck.h
new file mode 100644 (file)
index 0000000..adfba02
--- /dev/null
@@ -0,0 +1,44 @@
+//===--- MoveConstructorInitCheck.h - clang-tidy-----------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MOVECONSTRUCTORINITCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MOVECONSTRUCTORINITCHECK_H
+
+#include "../ClangTidy.h"
+#include "../utils/IncludeInserter.h"
+
+#include <memory>
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// The check flags user-defined move constructors that have a ctor-initializer
+/// initializing a member or base class through a copy constructor instead of a
+/// move constructor.
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/misc-move-constructor-init.html
+class MoveConstructorInitCheck : public ClangTidyCheck {
+public:
+  MoveConstructorInitCheck(StringRef Name, ClangTidyContext *Context);
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+  void registerPPCallbacks(clang::CompilerInstance &Compiler) override;
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+
+private:
+  std::unique_ptr<utils::IncludeInserter> Inserter;
+  const utils::IncludeSorter::IncludeStyle IncludeStyle;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MOVECONSTRUCTORINITCHECK_H
diff --git a/clang-tidy/misc/MoveForwardingReferenceCheck.cpp b/clang-tidy/misc/MoveForwardingReferenceCheck.cpp
new file mode 100644 (file)
index 0000000..12c19dd
--- /dev/null
@@ -0,0 +1,133 @@
+//===--- MoveForwardingReferenceCheck.cpp - clang-tidy --------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "MoveForwardingReferenceCheck.h"
+#include "clang/Lex/Lexer.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <algorithm>
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+static void replaceMoveWithForward(const UnresolvedLookupExpr *Callee,
+                                   const ParmVarDecl *ParmVar,
+                                   const TemplateTypeParmDecl *TypeParmDecl,
+                                   DiagnosticBuilder &Diag,
+                                   const ASTContext &Context) {
+  const SourceManager &SM = Context.getSourceManager();
+  const LangOptions &LangOpts = Context.getLangOpts();
+
+  CharSourceRange CallRange =
+      Lexer::makeFileCharRange(CharSourceRange::getTokenRange(
+                                   Callee->getLocStart(), Callee->getLocEnd()),
+                               SM, LangOpts);
+
+  if (CallRange.isValid()) {
+    const std::string TypeName =
+        TypeParmDecl->getIdentifier()
+            ? TypeParmDecl->getName().str()
+            : (llvm::Twine("decltype(") + ParmVar->getName() + ")").str();
+
+    const std::string ForwardName =
+        (llvm::Twine("forward<") + TypeName + ">").str();
+
+    // Create a replacement only if we see a "standard" way of calling
+    // std::move(). This will hopefully prevent erroneous replacements if the
+    // code does unusual things (e.g. create an alias for std::move() in
+    // another namespace).
+    NestedNameSpecifier *NNS = Callee->getQualifier();
+    if (!NNS) {
+      // Called as "move" (i.e. presumably the code had a "using std::move;").
+      // We still conservatively put a "std::" in front of the forward because
+      // we don't know whether the code also had a "using std::forward;".
+      Diag << FixItHint::CreateReplacement(CallRange, "std::" + ForwardName);
+    } else if (const NamespaceDecl *Namespace = NNS->getAsNamespace()) {
+      if (Namespace->getName() == "std") {
+        if (!NNS->getPrefix()) {
+          // Called as "std::move".
+          Diag << FixItHint::CreateReplacement(CallRange,
+                                               "std::" + ForwardName);
+        } else if (NNS->getPrefix()->getKind() == NestedNameSpecifier::Global) {
+          // Called as "::std::move".
+          Diag << FixItHint::CreateReplacement(CallRange,
+                                               "::std::" + ForwardName);
+        }
+      }
+    }
+  }
+}
+
+void MoveForwardingReferenceCheck::registerMatchers(MatchFinder *Finder) {
+  if (!getLangOpts().CPlusPlus11)
+    return;
+
+  // Matches a ParmVarDecl for a forwarding reference, i.e. a non-const rvalue
+  // reference of a function template parameter type.
+  auto ForwardingReferenceParmMatcher =
+      parmVarDecl(
+          hasType(qualType(rValueReferenceType(),
+                           references(templateTypeParmType(hasDeclaration(
+                               templateTypeParmDecl().bind("type-parm-decl")))),
+                           unless(references(qualType(isConstQualified()))))))
+          .bind("parm-var");
+
+  Finder->addMatcher(
+      callExpr(callee(unresolvedLookupExpr(
+                          hasAnyDeclaration(namedDecl(
+                              hasUnderlyingDecl(hasName("::std::move")))))
+                          .bind("lookup")),
+               argumentCountIs(1),
+               hasArgument(0, ignoringParenImpCasts(declRefExpr(
+                                  to(ForwardingReferenceParmMatcher)))))
+          .bind("call-move"),
+      this);
+}
+
+void MoveForwardingReferenceCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  const auto *CallMove = Result.Nodes.getNodeAs<CallExpr>("call-move");
+  const auto *UnresolvedLookup =
+      Result.Nodes.getNodeAs<UnresolvedLookupExpr>("lookup");
+  const auto *ParmVar = Result.Nodes.getNodeAs<ParmVarDecl>("parm-var");
+  const auto *TypeParmDecl =
+      Result.Nodes.getNodeAs<TemplateTypeParmDecl>("type-parm-decl");
+
+  // Get the FunctionDecl and FunctionTemplateDecl containing the function
+  // parameter.
+  const auto *FuncForParam = dyn_cast<FunctionDecl>(ParmVar->getDeclContext());
+  if (!FuncForParam)
+    return;
+  const FunctionTemplateDecl *FuncTemplate =
+      FuncForParam->getDescribedFunctionTemplate();
+  if (!FuncTemplate)
+    return;
+
+  // Check that the template type parameter belongs to the same function
+  // template as the function parameter of that type. (This implies that type
+  // deduction will happen on the type.)
+  const TemplateParameterList *Params = FuncTemplate->getTemplateParameters();
+  if (!std::count(Params->begin(), Params->end(), TypeParmDecl))
+    return;
+
+  auto Diag = diag(CallMove->getExprLoc(),
+                   "forwarding reference passed to std::move(), which may "
+                   "unexpectedly cause lvalues to be moved; use "
+                   "std::forward() instead");
+
+  replaceMoveWithForward(UnresolvedLookup, ParmVar, TypeParmDecl, Diag,
+                         *Result.Context);
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/misc/MoveForwardingReferenceCheck.h b/clang-tidy/misc/MoveForwardingReferenceCheck.h
new file mode 100644 (file)
index 0000000..2e6ec36
--- /dev/null
@@ -0,0 +1,49 @@
+//===--- MoveForwardingReferenceCheck.h - clang-tidy ----------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MOVEFORWARDINGREFERENCECHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MOVEFORWARDINGREFERENCECHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// The check warns if std::move is applied to a forwarding reference (i.e. an
+/// rvalue reference of a function template argument type).
+///
+/// If a developer is unaware of the special rules for template argument
+/// deduction on forwarding references, it will seem reasonable to apply
+/// std::move to the forwarding reference, in the same way that this would be
+/// done for a "normal" rvalue reference.
+///
+/// This has a consequence that is usually unwanted and possibly surprising: if
+/// the function that takes the forwarding reference as its parameter is called
+/// with an lvalue, that lvalue will be moved from (and hence placed into an
+/// indeterminate state) even though no std::move was applied to the lvalue at
+/// the call site.
+//
+/// The check suggests replacing the std::move with a std::forward.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/misc-move-forwarding-reference.html
+class MoveForwardingReferenceCheck : public ClangTidyCheck {
+public:
+  MoveForwardingReferenceCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MOVEFORWARDINGREFERENCECHECK_H
diff --git a/clang-tidy/misc/MultipleStatementMacroCheck.cpp b/clang-tidy/misc/MultipleStatementMacroCheck.cpp
new file mode 100644 (file)
index 0000000..9d485fd
--- /dev/null
@@ -0,0 +1,106 @@
+//===--- MultipleStatementMacroCheck.cpp - clang-tidy----------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "MultipleStatementMacroCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+namespace {
+
+AST_MATCHER(Expr, isInMacro) { return Node.getLocStart().isMacroID(); }
+
+/// \brief Find the next statement after `S`.
+const Stmt *nextStmt(const MatchFinder::MatchResult &Result, const Stmt *S) {
+  auto Parents = Result.Context->getParents(*S);
+  if (Parents.empty())
+    return nullptr;
+  const auto *Parent = Parents[0].get<Stmt>();
+  if (!Parent)
+    return nullptr;
+  const Stmt *Prev = nullptr;
+  for (const Stmt *Child : Parent->children()) {
+    if (Prev == S)
+      return Child;
+    Prev = Child;
+  }
+  return nextStmt(Result, Parent);
+}
+
+using ExpansionRanges = std::vector<std::pair<SourceLocation, SourceLocation>>;
+
+/// \bried Get all the macro expansion ranges related to `Loc`.
+///
+/// The result is ordered from most inner to most outer.
+ExpansionRanges getExpansionRanges(SourceLocation Loc,
+                                   const MatchFinder::MatchResult &Result) {
+  ExpansionRanges Locs;
+  while (Loc.isMacroID()) {
+    Locs.push_back(Result.SourceManager->getImmediateExpansionRange(Loc));
+    Loc = Locs.back().first;
+  }
+  return Locs;
+}
+
+} // namespace
+
+void MultipleStatementMacroCheck::registerMatchers(MatchFinder *Finder) {
+  const auto Inner = expr(isInMacro(), unless(compoundStmt())).bind("inner");
+  Finder->addMatcher(
+      stmt(anyOf(ifStmt(hasThen(Inner)), ifStmt(hasElse(Inner)).bind("else"),
+                 whileStmt(hasBody(Inner)), forStmt(hasBody(Inner))))
+          .bind("outer"),
+      this);
+}
+
+void MultipleStatementMacroCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  const auto *Inner = Result.Nodes.getNodeAs<Expr>("inner");
+  const auto *Outer = Result.Nodes.getNodeAs<Stmt>("outer");
+  const auto *Next = nextStmt(Result, Outer);
+  if (!Next)
+    return;
+
+  SourceLocation OuterLoc = Outer->getLocStart();
+  if (Result.Nodes.getNodeAs<Stmt>("else"))
+    OuterLoc = cast<IfStmt>(Outer)->getElseLoc();
+
+  auto InnerRanges = getExpansionRanges(Inner->getLocStart(), Result);
+  auto OuterRanges = getExpansionRanges(OuterLoc, Result);
+  auto NextRanges = getExpansionRanges(Next->getLocStart(), Result);
+
+  // Remove all the common ranges, starting from the top (the last ones in the
+  // list).
+  while (!InnerRanges.empty() && !OuterRanges.empty() && !NextRanges.empty() &&
+         InnerRanges.back() == OuterRanges.back() &&
+         InnerRanges.back() == NextRanges.back()) {
+    InnerRanges.pop_back();
+    OuterRanges.pop_back();
+    NextRanges.pop_back();
+  }
+
+  // Inner and Next must have at least one more macro that Outer doesn't have,
+  // and that range must be common to both.
+  if (InnerRanges.empty() || NextRanges.empty() ||
+      InnerRanges.back() != NextRanges.back())
+    return;
+
+  diag(InnerRanges.back().first, "multiple statement macro used without "
+                                 "braces; some statements will be "
+                                 "unconditionally executed");
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/misc/MultipleStatementMacroCheck.h b/clang-tidy/misc/MultipleStatementMacroCheck.h
new file mode 100644 (file)
index 0000000..77a6b27
--- /dev/null
@@ -0,0 +1,37 @@
+//===--- MultipleStatementMacroCheck.h - clang-tidy--------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MULTIPLE_STATEMENT_MACRO_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MULTIPLE_STATEMENT_MACRO_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Detect multiple statement macros that are used in unbraced conditionals.
+/// Only the first statement of the macro will be inside the conditional and the
+/// other ones will be executed unconditionally.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/misc-multiple-statement-macro.html
+class MultipleStatementMacroCheck : public ClangTidyCheck {
+public:
+  MultipleStatementMacroCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MULTIPLE_STATEMENT_MACRO_H
diff --git a/clang-tidy/misc/NewDeleteOverloadsCheck.cpp b/clang-tidy/misc/NewDeleteOverloadsCheck.cpp
new file mode 100644 (file)
index 0000000..5e29119
--- /dev/null
@@ -0,0 +1,213 @@
+//===--- NewDeleteOverloadsCheck.cpp - clang-tidy--------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "NewDeleteOverloadsCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+namespace {
+
+AST_MATCHER(FunctionDecl, isPlacementOverload) {
+  bool New;
+  switch (Node.getOverloadedOperator()) {
+  default:
+    return false;
+  case OO_New:
+  case OO_Array_New:
+    New = true;
+    break;
+  case OO_Delete:
+  case OO_Array_Delete:
+    New = false;
+    break;
+  }
+
+  // Variadic functions are always placement functions.
+  if (Node.isVariadic())
+    return true;
+
+  // Placement new is easy: it always has more than one parameter (the first
+  // parameter is always the size). If it's an overload of delete or delete[]
+  // that has only one parameter, it's never a placement delete.
+  if (New)
+    return Node.getNumParams() > 1;
+  if (Node.getNumParams() == 1)
+    return false;
+
+  // Placement delete is a little more challenging. They always have more than
+  // one parameter with the first parameter being a pointer. However, the
+  // second parameter can be a size_t for sized deallocation, and that is never
+  // a placement delete operator.
+  if (Node.getNumParams() <= 1 || Node.getNumParams() > 2)
+    return true;
+
+  const auto *FPT = Node.getType()->castAs<FunctionProtoType>();
+  ASTContext &Ctx = Node.getASTContext();
+  if (Ctx.getLangOpts().SizedDeallocation &&
+      Ctx.hasSameType(FPT->getParamType(1), Ctx.getSizeType()))
+    return false;
+
+  return true;
+}
+
+OverloadedOperatorKind getCorrespondingOverload(const FunctionDecl *FD) {
+  switch (FD->getOverloadedOperator()) {
+  default:
+    break;
+  case OO_New:
+    return OO_Delete;
+  case OO_Delete:
+    return OO_New;
+  case OO_Array_New:
+    return OO_Array_Delete;
+  case OO_Array_Delete:
+    return OO_Array_New;
+  }
+  llvm_unreachable("Not an overloaded allocation operator");
+}
+
+const char *getOperatorName(OverloadedOperatorKind K) {
+  switch (K) {
+  default:
+    break;
+  case OO_New:
+    return "operator new";
+  case OO_Delete:
+    return "operator delete";
+  case OO_Array_New:
+    return "operator new[]";
+  case OO_Array_Delete:
+    return "operator delete[]";
+  }
+  llvm_unreachable("Not an overloaded allocation operator");
+}
+
+bool areCorrespondingOverloads(const FunctionDecl *LHS,
+                               const FunctionDecl *RHS) {
+  return RHS->getOverloadedOperator() == getCorrespondingOverload(LHS);
+}
+
+bool hasCorrespondingOverloadInBaseClass(const CXXMethodDecl *MD,
+                                         const CXXRecordDecl *RD = nullptr) {
+  if (RD) {
+    // Check the methods in the given class and accessible to derived classes.
+    for (const auto *BMD : RD->methods())
+      if (BMD->isOverloadedOperator() && BMD->getAccess() != AS_private &&
+          areCorrespondingOverloads(MD, BMD))
+        return true;
+  } else {
+    // Get the parent class of the method; we do not need to care about checking
+    // the methods in this class as the caller has already done that by looking
+    // at the declaration contexts.
+    RD = MD->getParent();
+  }
+
+  for (const auto &BS : RD->bases()) {
+    // We can't say much about a dependent base class, but to avoid false
+    // positives assume it can have a corresponding overload.
+    if (BS.getType()->isDependentType())
+      return true;
+    if (const auto *BaseRD = BS.getType()->getAsCXXRecordDecl())
+      if (hasCorrespondingOverloadInBaseClass(MD, BaseRD))
+        return true;
+  }
+
+  return false;
+}
+
+} // anonymous namespace
+
+void NewDeleteOverloadsCheck::registerMatchers(MatchFinder *Finder) {
+  if (!getLangOpts().CPlusPlus)
+    return;
+
+  // Match all operator new and operator delete overloads (including the array
+  // forms). Do not match implicit operators, placement operators, or
+  // deleted/private operators.
+  //
+  // Technically, trivially-defined operator delete seems like a reasonable
+  // thing to also skip. e.g., void operator delete(void *) {}
+  // However, I think it's more reasonable to warn in this case as the user
+  // should really be writing that as a deleted function.
+  Finder->addMatcher(
+      functionDecl(unless(anyOf(isImplicit(), isPlacementOverload(),
+                                isDeleted(), cxxMethodDecl(isPrivate()))),
+                   anyOf(hasOverloadedOperatorName("new"),
+                         hasOverloadedOperatorName("new[]"),
+                         hasOverloadedOperatorName("delete"),
+                         hasOverloadedOperatorName("delete[]")))
+          .bind("func"),
+      this);
+}
+
+void NewDeleteOverloadsCheck::check(const MatchFinder::MatchResult &Result) {
+  // Add any matches we locate to the list of things to be checked at the
+  // end of the translation unit.
+  const auto *FD = Result.Nodes.getNodeAs<FunctionDecl>("func");
+  const CXXRecordDecl *RD = nullptr;
+  if (const auto *MD = dyn_cast<CXXMethodDecl>(FD))
+    RD = MD->getParent();
+  Overloads[RD].push_back(FD);
+}
+
+void NewDeleteOverloadsCheck::onEndOfTranslationUnit() {
+  // Walk over the list of declarations we've found to see if there is a
+  // corresponding overload at the same declaration context or within a base
+  // class. If there is not, add the element to the list of declarations to
+  // diagnose.
+  SmallVector<const FunctionDecl *, 4> Diagnose;
+  for (const auto &RP : Overloads) {
+    // We don't care about the CXXRecordDecl key in the map; we use it as a way
+    // to shard the overloads by declaration context to reduce the algorithmic
+    // complexity when searching for corresponding free store functions.
+    for (const auto *Overload : RP.second) {
+      const auto *Match =
+          std::find_if(RP.second.begin(), RP.second.end(),
+                       [&Overload](const FunctionDecl *FD) {
+                         if (FD == Overload)
+                           return false;
+                         // If the declaration contexts don't match, we don't
+                         // need to check any further.
+                         if (FD->getDeclContext() != Overload->getDeclContext())
+                           return false;
+
+                         // Since the declaration contexts match, see whether
+                         // the current element is the corresponding operator.
+                         if (!areCorrespondingOverloads(Overload, FD))
+                           return false;
+
+                         return true;
+                       });
+
+      if (Match == RP.second.end()) {
+        // Check to see if there is a corresponding overload in a base class
+        // context. If there isn't, or if the overload is not a class member
+        // function, then we should diagnose.
+        const auto *MD = dyn_cast<CXXMethodDecl>(Overload);
+        if (!MD || !hasCorrespondingOverloadInBaseClass(MD))
+          Diagnose.push_back(Overload);
+      }
+    }
+  }
+
+  for (const auto *FD : Diagnose)
+    diag(FD->getLocation(), "declaration of %0 has no matching declaration "
+                            "of '%1' at the same scope")
+        << FD << getOperatorName(getCorrespondingOverload(FD));
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/misc/NewDeleteOverloadsCheck.h b/clang-tidy/misc/NewDeleteOverloadsCheck.h
new file mode 100644 (file)
index 0000000..3e99892
--- /dev/null
@@ -0,0 +1,38 @@
+//===--- NewDeleteOverloadsCheck.h - clang-tidy----------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_NEWDELETEOVERLOADS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_NEWDELETEOVERLOADS_H
+
+#include "../ClangTidy.h"
+#include "llvm/ADT/SmallVector.h"
+#include <map>
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+class NewDeleteOverloadsCheck : public ClangTidyCheck {
+  std::map<const clang::CXXRecordDecl *,
+           llvm::SmallVector<const clang::FunctionDecl *, 4>>
+      Overloads;
+
+public:
+  NewDeleteOverloadsCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+  void onEndOfTranslationUnit() override;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_NEWDELETEOVERLOADS_H
diff --git a/clang-tidy/misc/NoexceptMoveConstructorCheck.cpp b/clang-tidy/misc/NoexceptMoveConstructorCheck.cpp
new file mode 100644 (file)
index 0000000..12a360f
--- /dev/null
@@ -0,0 +1,77 @@
+//===--- NoexceptMoveConstructorCheck.cpp - clang-tidy---------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "NoexceptMoveConstructorCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+void NoexceptMoveConstructorCheck::registerMatchers(MatchFinder *Finder) {
+  // Only register the matchers for C++11; the functionality currently does not
+  // provide any benefit to other languages, despite being benign.
+  if (!getLangOpts().CPlusPlus11)
+    return;
+
+  Finder->addMatcher(
+      cxxMethodDecl(anyOf(cxxConstructorDecl(), hasOverloadedOperatorName("=")),
+                    unless(isImplicit()), unless(isDeleted()))
+          .bind("decl"),
+      this);
+}
+
+void NoexceptMoveConstructorCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  if (const auto *Decl = Result.Nodes.getNodeAs<CXXMethodDecl>("decl")) {
+    StringRef MethodType = "assignment operator";
+    if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(Decl)) {
+      if (!Ctor->isMoveConstructor())
+        return;
+      MethodType = "constructor";
+    } else if (!Decl->isMoveAssignmentOperator()) {
+      return;
+    }
+
+    const auto *ProtoType = Decl->getType()->getAs<FunctionProtoType>();
+
+    if (isUnresolvedExceptionSpec(ProtoType->getExceptionSpecType()))
+      return;
+
+    switch (ProtoType->getNoexceptSpec(*Result.Context)) {
+      case FunctionProtoType::NR_NoNoexcept:
+        diag(Decl->getLocation(), "move %0s should be marked noexcept")
+            << MethodType;
+        // FIXME: Add a fixit.
+        break;
+      case FunctionProtoType::NR_Throw:
+        // Don't complain about nothrow(false), but complain on nothrow(expr)
+        // where expr evaluates to false.
+        if (const Expr *E = ProtoType->getNoexceptExpr()) {
+          if (isa<CXXBoolLiteralExpr>(E))
+            break;
+          diag(E->getExprLoc(),
+               "noexcept specifier on the move %0 evaluates to 'false'")
+              << MethodType;
+        }
+        break;
+      case FunctionProtoType::NR_Nothrow:
+      case FunctionProtoType::NR_Dependent:
+      case FunctionProtoType::NR_BadNoexcept:
+        break;
+    }
+  }
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/misc/NoexceptMoveConstructorCheck.h b/clang-tidy/misc/NoexceptMoveConstructorCheck.h
new file mode 100644 (file)
index 0000000..e6a0ef9
--- /dev/null
@@ -0,0 +1,38 @@
+//===--- NoexceptMoveConstructorCheck.h - clang-tidy-------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_NOEXCEPTMOVECONSTRUCTORCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_NOEXCEPTMOVECONSTRUCTORCHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// The check flags user-defined move constructors and assignment operators not
+/// marked with `noexcept` or marked with `noexcept(expr)` where `expr`
+/// evaluates to `false` (but is not a `false` literal itself).
+///
+/// Move constructors of all the types used with STL containers, for example,
+/// need to be declared `noexcept`. Otherwise STL will choose copy constructors
+/// instead. The same is valid for move assignment operations.
+class NoexceptMoveConstructorCheck : public ClangTidyCheck {
+public:
+  NoexceptMoveConstructorCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_NOEXCEPTMOVECONSTRUCTORCHECK_H
diff --git a/clang-tidy/misc/NonCopyableObjects.cpp b/clang-tidy/misc/NonCopyableObjects.cpp
new file mode 100644 (file)
index 0000000..de15275
--- /dev/null
@@ -0,0 +1,74 @@
+//===--- NonCopyableObjects.cpp - clang-tidy-------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "NonCopyableObjects.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include <algorithm>
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+void NonCopyableObjectsCheck::registerMatchers(MatchFinder *Finder) {
+  // There are two ways to get into trouble with objects like FILE *:
+  // dereferencing the pointer type to be a non-pointer type, and declaring
+  // the type as a non-pointer type in the first place. While the declaration
+  // itself could technically be well-formed in the case where the type is not
+  // an opaque type, it's highly suspicious behavior.
+  //
+  // POSIX types are a bit different in that it's reasonable to declare a
+  // non-pointer variable or data member of the type, but it is not reasonable
+  // to dereference a pointer to the type, or declare a parameter of non-pointer
+  // type.
+  // FIXME: it would be good to make a list that is also user-configurable so
+  // that users can add their own elements to the list. However, it may require
+  // some extra thought since POSIX types and FILE types are usable in different
+  // ways.
+
+  auto BadFILEType = hasType(
+      namedDecl(hasAnyName("::FILE", "FILE", "std::FILE")).bind("type_decl"));
+  auto BadPOSIXType =
+      hasType(namedDecl(hasAnyName("::pthread_cond_t", "::pthread_mutex_t",
+                                   "pthread_cond_t", "pthread_mutex_t"))
+                  .bind("type_decl"));
+  auto BadEitherType = anyOf(BadFILEType, BadPOSIXType);
+
+  Finder->addMatcher(
+      namedDecl(anyOf(varDecl(BadFILEType), fieldDecl(BadFILEType)))
+          .bind("decl"),
+      this);
+  Finder->addMatcher(parmVarDecl(BadPOSIXType).bind("decl"), this);
+  Finder->addMatcher(
+      expr(unaryOperator(hasOperatorName("*"), BadEitherType)).bind("expr"),
+      this);
+}
+
+void NonCopyableObjectsCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *D = Result.Nodes.getNodeAs<NamedDecl>("decl");
+  const auto *BD = Result.Nodes.getNodeAs<NamedDecl>("type_decl");
+  const auto *E = Result.Nodes.getNodeAs<Expr>("expr");
+
+  if (D && BD)
+    diag(D->getLocation(), "%0 declared as type '%1', which is unsafe to copy"
+                           "; did you mean '%1 *'?")
+        << D << BD->getName();
+  else if (E)
+    diag(E->getExprLoc(),
+         "expression has opaque data structure type %0; type should only be "
+         "used as a pointer and not dereferenced")
+        << BD;
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
diff --git a/clang-tidy/misc/NonCopyableObjects.h b/clang-tidy/misc/NonCopyableObjects.h
new file mode 100644 (file)
index 0000000..38a45fd
--- /dev/null
@@ -0,0 +1,33 @@
+//===--- NonCopyableObjects.h - clang-tidy-----------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_NONCOPYABLEOBJECTS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_NONCOPYABLEOBJECTS_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// The check flags dereferences and non-pointer declarations of objects that
+/// are not meant to be passed by value, such as C FILE objects.
+class NonCopyableObjectsCheck : public ClangTidyCheck {
+public:
+  NonCopyableObjectsCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_NONCOPYABLEOBJECTS_H
diff --git a/clang-tidy/misc/RedundantExpressionCheck.cpp b/clang-tidy/misc/RedundantExpressionCheck.cpp
new file mode 100644 (file)
index 0000000..901b54c
--- /dev/null
@@ -0,0 +1,745 @@
+//===--- RedundantExpressionCheck.cpp - clang-tidy-------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "RedundantExpressionCheck.h"
+#include "../utils/Matchers.h"
+#include "../utils/OptionsUtils.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Lex/Lexer.h"
+#include "llvm/ADT/APInt.h"
+#include "llvm/ADT/APSInt.h"
+#include "llvm/ADT/FoldingSet.h"
+#include "llvm/Support/Casting.h"
+#include <algorithm>
+#include <cassert>
+#include <cstdint>
+#include <set>
+#include <string>
+#include <vector>
+
+using namespace clang::ast_matchers;
+using namespace clang::tidy::matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+namespace {
+using llvm::APSInt;
+} // namespace
+
+static const char KnownBannedMacroNames[] =
+    "EAGAIN;EWOULDBLOCK;SIGCLD;SIGCHLD;";
+
+static bool incrementWithoutOverflow(const APSInt &Value, APSInt &Result) {
+  Result = Value;
+  ++Result;
+  return Value < Result;
+}
+
+static bool areEquivalentNameSpecifier(const NestedNameSpecifier *Left,
+                                       const NestedNameSpecifier *Right) {
+  llvm::FoldingSetNodeID LeftID, RightID;
+  Left->Profile(LeftID);
+  Right->Profile(RightID);
+  return LeftID == RightID;
+}
+
+static bool areEquivalentExpr(const Expr *Left, const Expr *Right) {
+  if (!Left || !Right)
+    return !Left && !Right;
+
+  Left = Left->IgnoreParens();
+  Right = Right->IgnoreParens();
+
+  // Compare classes.
+  if (Left->getStmtClass() != Right->getStmtClass())
+    return false;
+
+  // Compare children.
+  Expr::const_child_iterator LeftIter = Left->child_begin();
+  Expr::const_child_iterator RightIter = Right->child_begin();
+  while (LeftIter != Left->child_end() && RightIter != Right->child_end()) {
+    if (!areEquivalentExpr(dyn_cast<Expr>(*LeftIter),
+                           dyn_cast<Expr>(*RightIter)))
+      return false;
+    ++LeftIter;
+    ++RightIter;
+  }
+  if (LeftIter != Left->child_end() || RightIter != Right->child_end())
+    return false;
+
+  // Perform extra checks.
+  switch (Left->getStmtClass()) {
+  default:
+    return false;
+
+  case Stmt::CharacterLiteralClass:
+    return cast<CharacterLiteral>(Left)->getValue() ==
+           cast<CharacterLiteral>(Right)->getValue();
+  case Stmt::IntegerLiteralClass: {
+    llvm::APInt LeftLit = cast<IntegerLiteral>(Left)->getValue();
+    llvm::APInt RightLit = cast<IntegerLiteral>(Right)->getValue();
+    return LeftLit.getBitWidth() == RightLit.getBitWidth() &&
+           LeftLit == RightLit;
+  }
+  case Stmt::FloatingLiteralClass:
+    return cast<FloatingLiteral>(Left)->getValue().bitwiseIsEqual(
+        cast<FloatingLiteral>(Right)->getValue());
+  case Stmt::StringLiteralClass:
+    return cast<StringLiteral>(Left)->getBytes() ==
+           cast<StringLiteral>(Right)->getBytes();
+
+  case Stmt::DependentScopeDeclRefExprClass:
+    if (cast<DependentScopeDeclRefExpr>(Left)->getDeclName() !=
+        cast<DependentScopeDeclRefExpr>(Right)->getDeclName())
+      return false;
+    return areEquivalentNameSpecifier(
+        cast<DependentScopeDeclRefExpr>(Left)->getQualifier(),
+        cast<DependentScopeDeclRefExpr>(Right)->getQualifier());
+  case Stmt::DeclRefExprClass:
+    return cast<DeclRefExpr>(Left)->getDecl() ==
+           cast<DeclRefExpr>(Right)->getDecl();
+  case Stmt::MemberExprClass:
+    return cast<MemberExpr>(Left)->getMemberDecl() ==
+           cast<MemberExpr>(Right)->getMemberDecl();
+
+  case Stmt::CStyleCastExprClass:
+    return cast<CStyleCastExpr>(Left)->getTypeAsWritten() ==
+           cast<CStyleCastExpr>(Right)->getTypeAsWritten();
+
+  case Stmt::CallExprClass:
+  case Stmt::ImplicitCastExprClass:
+  case Stmt::ArraySubscriptExprClass:
+    return true;
+
+  case Stmt::UnaryOperatorClass:
+    if (cast<UnaryOperator>(Left)->isIncrementDecrementOp())
+      return false;
+    return cast<UnaryOperator>(Left)->getOpcode() ==
+           cast<UnaryOperator>(Right)->getOpcode();
+  case Stmt::BinaryOperatorClass:
+    return cast<BinaryOperator>(Left)->getOpcode() ==
+           cast<BinaryOperator>(Right)->getOpcode();
+  }
+}
+
+// For a given expression 'x', returns whether the ranges covered by the
+// relational operators are equivalent (i.e.  x <= 4 is equivalent to x < 5).
+static bool areEquivalentRanges(BinaryOperatorKind OpcodeLHS,
+                                const APSInt &ValueLHS,
+                                BinaryOperatorKind OpcodeRHS,
+                                const APSInt &ValueRHS) {
+  assert(APSInt::compareValues(ValueLHS, ValueRHS) <= 0 &&
+         "Values must be ordered");
+  // Handle the case where constants are the same: x <= 4  <==>  x <= 4.
+  if (APSInt::compareValues(ValueLHS, ValueRHS) == 0)
+    return OpcodeLHS == OpcodeRHS;
+
+  // Handle the case where constants are off by one: x <= 4  <==>  x < 5.
+  APSInt ValueLHS_plus1;
+  return ((OpcodeLHS == BO_LE && OpcodeRHS == BO_LT) ||
+          (OpcodeLHS == BO_GT && OpcodeRHS == BO_GE)) &&
+         incrementWithoutOverflow(ValueLHS, ValueLHS_plus1) &&
+         APSInt::compareValues(ValueLHS_plus1, ValueRHS) == 0;
+}
+
+// For a given expression 'x', returns whether the ranges covered by the
+// relational operators are fully disjoint (i.e. x < 4  and  x > 7).
+static bool areExclusiveRanges(BinaryOperatorKind OpcodeLHS,
+                               const APSInt &ValueLHS,
+                               BinaryOperatorKind OpcodeRHS,
+                               const APSInt &ValueRHS) {
+  assert(APSInt::compareValues(ValueLHS, ValueRHS) <= 0 &&
+         "Values must be ordered");
+
+  // Handle cases where the constants are the same.
+  if (APSInt::compareValues(ValueLHS, ValueRHS) == 0) {
+    switch (OpcodeLHS) {
+    case BO_EQ:
+      return OpcodeRHS == BO_NE || OpcodeRHS == BO_GT || OpcodeRHS == BO_LT;
+    case BO_NE:
+      return OpcodeRHS == BO_EQ;
+    case BO_LE:
+      return OpcodeRHS == BO_GT;
+    case BO_GE:
+      return OpcodeRHS == BO_LT;
+    case BO_LT:
+      return OpcodeRHS == BO_EQ || OpcodeRHS == BO_GT || OpcodeRHS == BO_GE;
+    case BO_GT:
+      return OpcodeRHS == BO_EQ || OpcodeRHS == BO_LT || OpcodeRHS == BO_LE;
+    default:
+      return false;
+    }
+  }
+
+  // Handle cases where the constants are different.
+  if ((OpcodeLHS == BO_EQ || OpcodeLHS == BO_LT || OpcodeLHS == BO_LE) &&
+      (OpcodeRHS == BO_EQ || OpcodeRHS == BO_GT || OpcodeRHS == BO_GE))
+    return true;
+
+  // Handle the case where constants are off by one: x > 5 && x < 6.
+  APSInt ValueLHS_plus1;
+  if (OpcodeLHS == BO_GT && OpcodeRHS == BO_LT &&
+      incrementWithoutOverflow(ValueLHS, ValueLHS_plus1) &&
+      APSInt::compareValues(ValueLHS_plus1, ValueRHS) == 0)
+    return true;
+
+  return false;
+}
+
+// Returns whether the ranges covered by the union of both relational
+// expressions covers the whole domain (i.e. x < 10  and  x > 0).
+static bool rangesFullyCoverDomain(BinaryOperatorKind OpcodeLHS,
+                                   const APSInt &ValueLHS,
+                                   BinaryOperatorKind OpcodeRHS,
+                                   const APSInt &ValueRHS) {
+  assert(APSInt::compareValues(ValueLHS, ValueRHS) <= 0 &&
+         "Values must be ordered");
+
+  // Handle cases where the constants are the same:  x < 5 || x >= 5.
+  if (APSInt::compareValues(ValueLHS, ValueRHS) == 0) {
+    switch (OpcodeLHS) {
+    case BO_EQ:
+      return OpcodeRHS == BO_NE;
+    case BO_NE:
+      return OpcodeRHS == BO_EQ;
+    case BO_LE:
+      return OpcodeRHS == BO_GT || OpcodeRHS == BO_GE;
+    case BO_LT:
+      return OpcodeRHS == BO_GE;
+    case BO_GE:
+      return OpcodeRHS == BO_LT || OpcodeRHS == BO_LE;
+    case BO_GT:
+      return OpcodeRHS == BO_LE;
+    default:
+      return false;
+    }
+  }
+
+  // Handle the case where constants are off by one: x <= 4 || x >= 5.
+  APSInt ValueLHS_plus1;
+  if (OpcodeLHS == BO_LE && OpcodeRHS == BO_GE &&
+      incrementWithoutOverflow(ValueLHS, ValueLHS_plus1) &&
+      APSInt::compareValues(ValueLHS_plus1, ValueRHS) == 0)
+    return true;
+
+  // Handle cases where the constants are different: x > 4 || x <= 7.
+  if ((OpcodeLHS == BO_GT || OpcodeLHS == BO_GE) &&
+      (OpcodeRHS == BO_LT || OpcodeRHS == BO_LE))
+    return true;
+
+  // Handle cases where constants are different but both ops are !=, like:
+  // x != 5 || x != 10
+  if (OpcodeLHS == BO_NE && OpcodeRHS == BO_NE)
+    return true;
+
+  return false;
+}
+
+static bool rangeSubsumesRange(BinaryOperatorKind OpcodeLHS,
+                               const APSInt &ValueLHS,
+                               BinaryOperatorKind OpcodeRHS,
+                               const APSInt &ValueRHS) {
+  int Comparison = APSInt::compareValues(ValueLHS, ValueRHS);
+  switch (OpcodeLHS) {
+  case BO_EQ:
+    return OpcodeRHS == BO_EQ && Comparison == 0;
+  case BO_NE:
+    return (OpcodeRHS == BO_NE && Comparison == 0) ||
+           (OpcodeRHS == BO_EQ && Comparison != 0) ||
+           (OpcodeRHS == BO_LT && Comparison >= 0) ||
+           (OpcodeRHS == BO_LE && Comparison > 0) ||
+           (OpcodeRHS == BO_GT && Comparison <= 0) ||
+           (OpcodeRHS == BO_GE && Comparison < 0);
+
+  case BO_LT:
+    return ((OpcodeRHS == BO_LT && Comparison >= 0) ||
+            (OpcodeRHS == BO_LE && Comparison > 0) ||
+            (OpcodeRHS == BO_EQ && Comparison > 0));
+  case BO_GT:
+    return ((OpcodeRHS == BO_GT && Comparison <= 0) ||
+            (OpcodeRHS == BO_GE && Comparison < 0) ||
+            (OpcodeRHS == BO_EQ && Comparison < 0));
+  case BO_LE:
+    return (OpcodeRHS == BO_LT || OpcodeRHS == BO_LE || OpcodeRHS == BO_EQ) &&
+           Comparison >= 0;
+  case BO_GE:
+    return (OpcodeRHS == BO_GT || OpcodeRHS == BO_GE || OpcodeRHS == BO_EQ) &&
+           Comparison <= 0;
+  default:
+    return false;
+  }
+}
+
+static void canonicalNegateExpr(BinaryOperatorKind &Opcode, APSInt &Value) {
+  if (Opcode == BO_Sub) {
+    Opcode = BO_Add;
+    Value = -Value;
+  }
+}
+
+AST_MATCHER(Expr, isIntegerConstantExpr) {
+  if (Node.isInstantiationDependent())
+    return false;
+  return Node.isIntegerConstantExpr(Finder->getASTContext());
+}
+
+// Returns a matcher for integer constant expression.
+static ast_matchers::internal::Matcher<Expr>
+matchIntegerConstantExpr(StringRef Id) {
+  std::string CstId = (Id + "-const").str();
+  return expr(isIntegerConstantExpr()).bind(CstId);
+}
+
+// Retrieve the integer value matched by 'matchIntegerConstantExpr' with name
+// 'Id' and store it into 'Value'.
+static bool retrieveIntegerConstantExpr(const MatchFinder::MatchResult &Result,
+                                        StringRef Id, APSInt &Value) {
+  std::string CstId = (Id + "-const").str();
+  const auto *CstExpr = Result.Nodes.getNodeAs<Expr>(CstId);
+  return CstExpr && CstExpr->isIntegerConstantExpr(Value, *Result.Context);
+}
+
+// Returns a matcher for a symbolic expression (any expression except ingeter
+// constant expression).
+static ast_matchers::internal::Matcher<Expr> matchSymbolicExpr(StringRef Id) {
+  std::string SymId = (Id + "-sym").str();
+  return ignoringParenImpCasts(
+      expr(unless(isIntegerConstantExpr())).bind(SymId));
+}
+
+// Retrieve the expression matched by 'matchSymbolicExpr' with name 'Id' and
+// store it into 'SymExpr'.
+static bool retrieveSymbolicExpr(const MatchFinder::MatchResult &Result,
+                                 StringRef Id, const Expr *&SymExpr) {
+  std::string SymId = (Id + "-sym").str();
+  if (const auto *Node = Result.Nodes.getNodeAs<Expr>(SymId)) {
+    SymExpr = Node;
+    return true;
+  }
+  return false;
+}
+
+// Match a binary operator between a symbolic expression and an integer constant
+// expression.
+static ast_matchers::internal::Matcher<Expr>
+matchBinOpIntegerConstantExpr(StringRef Id) {
+  const auto BinOpCstExpr =
+      expr(
+          anyOf(binaryOperator(anyOf(hasOperatorName("+"), hasOperatorName("|"),
+                                     hasOperatorName("&")),
+                               hasEitherOperand(matchSymbolicExpr(Id)),
+                               hasEitherOperand(matchIntegerConstantExpr(Id))),
+                binaryOperator(hasOperatorName("-"),
+                               hasLHS(matchSymbolicExpr(Id)),
+                               hasRHS(matchIntegerConstantExpr(Id)))))
+          .bind(Id);
+  return ignoringParenImpCasts(BinOpCstExpr);
+}
+
+// Retrieve sub-expressions matched by 'matchBinOpIntegerConstantExpr' with
+// name 'Id'.
+static bool
+retrieveBinOpIntegerConstantExpr(const MatchFinder::MatchResult &Result,
+                                 StringRef Id, BinaryOperatorKind &Opcode,
+                                 const Expr *&Symbol, APSInt &Value) {
+  if (const auto *BinExpr = Result.Nodes.getNodeAs<BinaryOperator>(Id)) {
+    Opcode = BinExpr->getOpcode();
+    return retrieveSymbolicExpr(Result, Id, Symbol) &&
+           retrieveIntegerConstantExpr(Result, Id, Value);
+  }
+  return false;
+}
+
+// Matches relational expression: 'Expr <op> k' (i.e. x < 2, x != 3, 12 <= x).
+static ast_matchers::internal::Matcher<Expr>
+matchRelationalIntegerConstantExpr(StringRef Id) {
+  std::string CastId = (Id + "-cast").str();
+  std::string SwapId = (Id + "-swap").str();
+  std::string NegateId = (Id + "-negate").str();
+
+  const auto RelationalExpr = ignoringParenImpCasts(binaryOperator(
+      isComparisonOperator(), expr().bind(Id),
+      anyOf(allOf(hasLHS(matchSymbolicExpr(Id)),
+                  hasRHS(matchIntegerConstantExpr(Id))),
+            allOf(hasLHS(matchIntegerConstantExpr(Id)),
+                  hasRHS(matchSymbolicExpr(Id)), expr().bind(SwapId)))));
+
+  // A cast can be matched as a comparator to zero. (i.e. if (x) is equivalent
+  // to if (x != 0)).
+  const auto CastExpr =
+      implicitCastExpr(hasCastKind(CK_IntegralToBoolean),
+                       hasSourceExpression(matchSymbolicExpr(Id)))
+          .bind(CastId);
+
+  const auto NegateRelationalExpr =
+      unaryOperator(hasOperatorName("!"),
+                    hasUnaryOperand(anyOf(CastExpr, RelationalExpr)))
+          .bind(NegateId);
+
+  const auto NegateNegateRelationalExpr =
+      unaryOperator(hasOperatorName("!"),
+                    hasUnaryOperand(unaryOperator(
+                        hasOperatorName("!"),
+                        hasUnaryOperand(anyOf(CastExpr, RelationalExpr)))));
+
+  return anyOf(RelationalExpr, CastExpr, NegateRelationalExpr,
+               NegateNegateRelationalExpr);
+}
+
+// Retrieve sub-expressions matched by 'matchRelationalIntegerConstantExpr' with
+// name 'Id'.
+static bool
+retrieveRelationalIntegerConstantExpr(const MatchFinder::MatchResult &Result,
+                                      StringRef Id, const Expr *&OperandExpr,
+                                      BinaryOperatorKind &Opcode,
+                                      const Expr *&Symbol, APSInt &Value) {
+  std::string CastId = (Id + "-cast").str();
+  std::string SwapId = (Id + "-swap").str();
+  std::string NegateId = (Id + "-negate").str();
+
+  if (const auto *Bin = Result.Nodes.getNodeAs<BinaryOperator>(Id)) {
+    // Operand received with explicit comparator.
+    Opcode = Bin->getOpcode();
+    OperandExpr = Bin;
+    if (!retrieveIntegerConstantExpr(Result, Id, Value))
+      return false;
+  } else if (const auto *Cast = Result.Nodes.getNodeAs<CastExpr>(CastId)) {
+    // Operand received with implicit comparator (cast).
+    Opcode = BO_NE;
+    OperandExpr = Cast;
+    Value = APSInt(32, false);
+  } else {
+    return false;
+  }
+
+  if (!retrieveSymbolicExpr(Result, Id, Symbol))
+    return false;
+
+  if (Result.Nodes.getNodeAs<Expr>(SwapId))
+    Opcode = BinaryOperator::reverseComparisonOp(Opcode);
+  if (Result.Nodes.getNodeAs<Expr>(NegateId))
+    Opcode = BinaryOperator::negateComparisonOp(Opcode);
+
+  return true;
+}
+
+AST_MATCHER(BinaryOperator, operandsAreEquivalent) {
+  return areEquivalentExpr(Node.getLHS(), Node.getRHS());
+}
+
+AST_MATCHER(ConditionalOperator, expressionsAreEquivalent) {
+  return areEquivalentExpr(Node.getTrueExpr(), Node.getFalseExpr());
+}
+
+AST_MATCHER(CallExpr, parametersAreEquivalent) {
+  return Node.getNumArgs() == 2 &&
+         areEquivalentExpr(Node.getArg(0), Node.getArg(1));
+}
+
+AST_MATCHER(BinaryOperator, binaryOperatorIsInMacro) {
+  return Node.getOperatorLoc().isMacroID();
+}
+
+AST_MATCHER(ConditionalOperator, conditionalOperatorIsInMacro) {
+  return Node.getQuestionLoc().isMacroID() || Node.getColonLoc().isMacroID();
+}
+
+AST_MATCHER(Expr, isMacro) { return Node.getExprLoc().isMacroID(); }
+
+AST_MATCHER_P(Expr, expandedByMacro, std::set<std::string>, Names) {
+  const SourceManager &SM = Finder->getASTContext().getSourceManager();
+  const LangOptions &LO = Finder->getASTContext().getLangOpts();
+  SourceLocation Loc = Node.getExprLoc();
+  while (Loc.isMacroID()) {
+    std::string MacroName = Lexer::getImmediateMacroName(Loc, SM, LO);
+    if (Names.find(MacroName) != Names.end())
+      return true;
+    Loc = SM.getImmediateMacroCallerLoc(Loc);
+  }
+  return false;
+}
+
+void RedundantExpressionCheck::registerMatchers(MatchFinder *Finder) {
+  const auto AnyLiteralExpr = ignoringParenImpCasts(
+      anyOf(cxxBoolLiteral(), characterLiteral(), integerLiteral()));
+
+  std::vector<std::string> MacroNames =
+      utils::options::parseStringList(KnownBannedMacroNames);
+  std::set<std::string> Names(MacroNames.begin(), MacroNames.end());
+
+  const auto BannedIntegerLiteral = integerLiteral(expandedByMacro(Names));
+
+  Finder->addMatcher(
+      binaryOperator(anyOf(hasOperatorName("-"), hasOperatorName("/"),
+                           hasOperatorName("%"), hasOperatorName("|"),
+                           hasOperatorName("&"), hasOperatorName("^"),
+                           matchers::isComparisonOperator(),
+                           hasOperatorName("&&"), hasOperatorName("||"),
+                           hasOperatorName("=")),
+                     operandsAreEquivalent(),
+                     // Filter noisy false positives.
+                     unless(isInTemplateInstantiation()),
+                     unless(binaryOperatorIsInMacro()),
+                     unless(hasType(realFloatingPointType())),
+                     unless(hasEitherOperand(hasType(realFloatingPointType()))),
+                     unless(hasLHS(AnyLiteralExpr)),
+                     unless(hasDescendant(BannedIntegerLiteral)))
+          .bind("binary"),
+      this);
+
+  Finder->addMatcher(
+      conditionalOperator(expressionsAreEquivalent(),
+                          // Filter noisy false positives.
+                          unless(conditionalOperatorIsInMacro()),
+                          unless(hasTrueExpression(AnyLiteralExpr)),
+                          unless(isInTemplateInstantiation()))
+          .bind("cond"),
+      this);
+
+  Finder->addMatcher(
+      cxxOperatorCallExpr(
+          anyOf(
+              hasOverloadedOperatorName("-"), hasOverloadedOperatorName("/"),
+              hasOverloadedOperatorName("%"), hasOverloadedOperatorName("|"),
+              hasOverloadedOperatorName("&"), hasOverloadedOperatorName("^"),
+              hasOverloadedOperatorName("=="), hasOverloadedOperatorName("!="),
+              hasOverloadedOperatorName("<"), hasOverloadedOperatorName("<="),
+              hasOverloadedOperatorName(">"), hasOverloadedOperatorName(">="),
+              hasOverloadedOperatorName("&&"), hasOverloadedOperatorName("||"),
+              hasOverloadedOperatorName("=")),
+          parametersAreEquivalent(),
+          // Filter noisy false positives.
+          unless(isMacro()), unless(isInTemplateInstantiation()))
+          .bind("call"),
+      this);
+
+  // Match common expressions and apply more checks to find redundant
+  // sub-expressions.
+  //   a) Expr <op> K1 == K2
+  //   b) Expr <op> K1 == Expr
+  //   c) Expr <op> K1 == Expr <op> K2
+  // see: 'checkArithmeticExpr' and 'checkBitwiseExpr'
+  const auto BinOpCstLeft = matchBinOpIntegerConstantExpr("lhs");
+  const auto BinOpCstRight = matchBinOpIntegerConstantExpr("rhs");
+  const auto CstRight = matchIntegerConstantExpr("rhs");
+  const auto SymRight = matchSymbolicExpr("rhs");
+
+  // Match expressions like: x <op> 0xFF == 0xF00.
+  Finder->addMatcher(binaryOperator(isComparisonOperator(),
+                                    hasEitherOperand(BinOpCstLeft),
+                                    hasEitherOperand(CstRight))
+                         .bind("binop-const-compare-to-const"),
+                     this);
+
+  // Match expressions like: x <op> 0xFF == x.
+  Finder->addMatcher(
+      binaryOperator(isComparisonOperator(),
+                     anyOf(allOf(hasLHS(BinOpCstLeft), hasRHS(SymRight)),
+                           allOf(hasLHS(SymRight), hasRHS(BinOpCstLeft))))
+          .bind("binop-const-compare-to-sym"),
+      this);
+
+  // Match expressions like: x <op> 10 == x <op> 12.
+  Finder->addMatcher(binaryOperator(isComparisonOperator(),
+                                    hasLHS(BinOpCstLeft), hasRHS(BinOpCstRight),
+                                    // Already reported as redundant.
+                                    unless(operandsAreEquivalent()))
+                         .bind("binop-const-compare-to-binop-const"),
+                     this);
+
+  // Match relational expressions combined with logical operators and find
+  // redundant sub-expressions.
+  // see: 'checkRelationalExpr'
+
+  // Match expressions like: x < 2 && x > 2.
+  const auto ComparisonLeft = matchRelationalIntegerConstantExpr("lhs");
+  const auto ComparisonRight = matchRelationalIntegerConstantExpr("rhs");
+  Finder->addMatcher(
+      binaryOperator(anyOf(hasOperatorName("||"), hasOperatorName("&&")),
+                     hasLHS(ComparisonLeft), hasRHS(ComparisonRight),
+                     // Already reported as redundant.
+                     unless(operandsAreEquivalent()))
+          .bind("comparisons-of-symbol-and-const"),
+      this);
+}
+
+void RedundantExpressionCheck::checkArithmeticExpr(
+    const MatchFinder::MatchResult &Result) {
+  APSInt LhsValue, RhsValue;
+  const Expr *LhsSymbol = nullptr, *RhsSymbol = nullptr;
+  BinaryOperatorKind LhsOpcode, RhsOpcode;
+
+  if (const auto *ComparisonOperator = Result.Nodes.getNodeAs<BinaryOperator>(
+          "binop-const-compare-to-sym")) {
+    BinaryOperatorKind Opcode = ComparisonOperator->getOpcode();
+    if (!retrieveBinOpIntegerConstantExpr(Result, "lhs", LhsOpcode, LhsSymbol,
+                                          LhsValue) ||
+        !retrieveSymbolicExpr(Result, "rhs", RhsSymbol) ||
+        !areEquivalentExpr(LhsSymbol, RhsSymbol))
+      return;
+
+    // Check expressions: x + k == x  or  x - k == x.
+    if (LhsOpcode == BO_Add || LhsOpcode == BO_Sub) {
+      if ((LhsValue != 0 && Opcode == BO_EQ) ||
+          (LhsValue == 0 && Opcode == BO_NE))
+        diag(ComparisonOperator->getOperatorLoc(),
+             "logical expression is always false");
+      else if ((LhsValue == 0 && Opcode == BO_EQ) ||
+               (LhsValue != 0 && Opcode == BO_NE))
+        diag(ComparisonOperator->getOperatorLoc(),
+             "logical expression is always true");
+    }
+  } else if (const auto *ComparisonOperator =
+                 Result.Nodes.getNodeAs<BinaryOperator>(
+                     "binop-const-compare-to-binop-const")) {
+    BinaryOperatorKind Opcode = ComparisonOperator->getOpcode();
+
+    if (!retrieveBinOpIntegerConstantExpr(Result, "lhs", LhsOpcode, LhsSymbol,
+                                          LhsValue) ||
+        !retrieveBinOpIntegerConstantExpr(Result, "rhs", RhsOpcode, RhsSymbol,
+                                          RhsValue) ||
+        !areEquivalentExpr(LhsSymbol, RhsSymbol))
+      return;
+
+    canonicalNegateExpr(LhsOpcode, LhsValue);
+    canonicalNegateExpr(RhsOpcode, RhsValue);
+
+    // Check expressions: x + 1 == x + 2  or  x + 1 != x + 2.
+    if (LhsOpcode == BO_Add && RhsOpcode == BO_Add) {
+      if ((Opcode == BO_EQ && APSInt::compareValues(LhsValue, RhsValue) == 0) ||
+          (Opcode == BO_NE && APSInt::compareValues(LhsValue, RhsValue) != 0)) {
+        diag(ComparisonOperator->getOperatorLoc(),
+             "logical expression is always true");
+      } else if ((Opcode == BO_EQ &&
+                  APSInt::compareValues(LhsValue, RhsValue) != 0) ||
+                 (Opcode == BO_NE &&
+                  APSInt::compareValues(LhsValue, RhsValue) == 0)) {
+        diag(ComparisonOperator->getOperatorLoc(),
+             "logical expression is always false");
+      }
+    }
+  }
+}
+
+void RedundantExpressionCheck::checkBitwiseExpr(
+    const MatchFinder::MatchResult &Result) {
+  if (const auto *ComparisonOperator = Result.Nodes.getNodeAs<BinaryOperator>(
+          "binop-const-compare-to-const")) {
+    BinaryOperatorKind Opcode = ComparisonOperator->getOpcode();
+
+    APSInt LhsValue, RhsValue;
+    const Expr *LhsSymbol = nullptr;
+    BinaryOperatorKind LhsOpcode;
+    if (!retrieveBinOpIntegerConstantExpr(Result, "lhs", LhsOpcode, LhsSymbol,
+                                          LhsValue) ||
+        !retrieveIntegerConstantExpr(Result, "rhs", RhsValue))
+      return;
+
+    uint64_t LhsConstant = LhsValue.getZExtValue();
+    uint64_t RhsConstant = RhsValue.getZExtValue();
+    SourceLocation Loc = ComparisonOperator->getOperatorLoc();
+
+    // Check expression: x & k1 == k2  (i.e. x & 0xFF == 0xF00)
+    if (LhsOpcode == BO_And && (LhsConstant & RhsConstant) != RhsConstant) {
+      if (Opcode == BO_EQ)
+        diag(Loc, "logical expression is always false");
+      else if (Opcode == BO_NE)
+        diag(Loc, "logical expression is always true");
+    }
+
+    // Check expression: x | k1 == k2  (i.e. x | 0xFF == 0xF00)
+    if (LhsOpcode == BO_Or && (LhsConstant | RhsConstant) != RhsConstant) {
+      if (Opcode == BO_EQ)
+        diag(Loc, "logical expression is always false");
+      else if (Opcode == BO_NE)
+        diag(Loc, "logical expression is always true");
+    }
+  }
+}
+
+void RedundantExpressionCheck::checkRelationalExpr(
+    const MatchFinder::MatchResult &Result) {
+  if (const auto *ComparisonOperator = Result.Nodes.getNodeAs<BinaryOperator>(
+          "comparisons-of-symbol-and-const")) {
+    // Matched expressions are: (x <op> k1) <REL> (x <op> k2).
+    BinaryOperatorKind Opcode = ComparisonOperator->getOpcode();
+
+    const Expr *LhsExpr = nullptr, *RhsExpr = nullptr;
+    APSInt LhsValue, RhsValue;
+    const Expr *LhsSymbol = nullptr, *RhsSymbol = nullptr;
+    BinaryOperatorKind LhsOpcode, RhsOpcode;
+    if (!retrieveRelationalIntegerConstantExpr(
+            Result, "lhs", LhsExpr, LhsOpcode, LhsSymbol, LhsValue) ||
+        !retrieveRelationalIntegerConstantExpr(
+            Result, "rhs", RhsExpr, RhsOpcode, RhsSymbol, RhsValue) ||
+        !areEquivalentExpr(LhsSymbol, RhsSymbol))
+      return;
+
+    // Bring to a canonical form: smallest constant must be on the left side.
+    if (APSInt::compareValues(LhsValue, RhsValue) > 0) {
+      std::swap(LhsExpr, RhsExpr);
+      std::swap(LhsValue, RhsValue);
+      std::swap(LhsSymbol, RhsSymbol);
+      std::swap(LhsOpcode, RhsOpcode);
+    }
+
+    if ((Opcode == BO_LAnd || Opcode == BO_LOr) &&
+        areEquivalentRanges(LhsOpcode, LhsValue, RhsOpcode, RhsValue)) {
+      diag(ComparisonOperator->getOperatorLoc(),
+           "equivalent expression on both side of logical operator");
+      return;
+    }
+
+    if (Opcode == BO_LAnd) {
+      if (areExclusiveRanges(LhsOpcode, LhsValue, RhsOpcode, RhsValue)) {
+        diag(ComparisonOperator->getOperatorLoc(),
+             "logical expression is always false");
+      } else if (rangeSubsumesRange(LhsOpcode, LhsValue, RhsOpcode, RhsValue)) {
+        diag(LhsExpr->getExprLoc(), "expression is redundant");
+      } else if (rangeSubsumesRange(RhsOpcode, RhsValue, LhsOpcode, LhsValue)) {
+        diag(RhsExpr->getExprLoc(), "expression is redundant");
+      }
+    }
+
+    if (Opcode == BO_LOr) {
+      if (rangesFullyCoverDomain(LhsOpcode, LhsValue, RhsOpcode, RhsValue)) {
+        diag(ComparisonOperator->getOperatorLoc(),
+             "logical expression is always true");
+      } else if (rangeSubsumesRange(LhsOpcode, LhsValue, RhsOpcode, RhsValue)) {
+        diag(RhsExpr->getExprLoc(), "expression is redundant");
+      } else if (rangeSubsumesRange(RhsOpcode, RhsValue, LhsOpcode, LhsValue)) {
+        diag(LhsExpr->getExprLoc(), "expression is redundant");
+      }
+    }
+  }
+}
+
+void RedundantExpressionCheck::check(const MatchFinder::MatchResult &Result) {
+  if (const auto *BinOp = Result.Nodes.getNodeAs<BinaryOperator>("binary"))
+    diag(BinOp->getOperatorLoc(), "both side of operator are equivalent");
+  if (const auto *CondOp = Result.Nodes.getNodeAs<ConditionalOperator>("cond"))
+    diag(CondOp->getColonLoc(), "'true' and 'false' expression are equivalent");
+  if (const auto *Call = Result.Nodes.getNodeAs<CXXOperatorCallExpr>("call"))
+    diag(Call->getOperatorLoc(),
+         "both side of overloaded operator are equivalent");
+
+  checkArithmeticExpr(Result);
+  checkBitwiseExpr(Result);
+  checkRelationalExpr(Result);
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/misc/RedundantExpressionCheck.h b/clang-tidy/misc/RedundantExpressionCheck.h
new file mode 100644 (file)
index 0000000..59d2c8f
--- /dev/null
@@ -0,0 +1,40 @@
+//===--- RedundantExpressionCheck.h - clang-tidy-----------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_REDUNDANT_EXPRESSION_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_REDUNDANT_EXPRESSION_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Detect useless or suspicious redundant expressions.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/misc-redundant-expression.html
+class RedundantExpressionCheck : public ClangTidyCheck {
+public:
+  RedundantExpressionCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+  void checkArithmeticExpr(const ast_matchers::MatchFinder::MatchResult &R);
+  void checkBitwiseExpr(const ast_matchers::MatchFinder::MatchResult &R);
+  void checkRelationalExpr(const ast_matchers::MatchFinder::MatchResult &R);
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_REDUNDANT_EXPRESSION_H
diff --git a/clang-tidy/misc/SizeofContainerCheck.cpp b/clang-tidy/misc/SizeofContainerCheck.cpp
new file mode 100644 (file)
index 0000000..de4e8ad
--- /dev/null
@@ -0,0 +1,49 @@
+//===--- SizeofContainerCheck.cpp - clang-tidy-----------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "SizeofContainerCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+void SizeofContainerCheck::registerMatchers(MatchFinder *Finder) {
+  Finder->addMatcher(
+      expr(unless(isInTemplateInstantiation()),
+           expr(sizeOfExpr(has(ignoringParenImpCasts(
+                    expr(hasType(hasCanonicalType(hasDeclaration(cxxRecordDecl(
+                        matchesName("^(::std::|::string)"),
+                        unless(matchesName("^::std::(bitset|array)$")),
+                        hasMethod(cxxMethodDecl(hasName("size"), isPublic(),
+                                                isConst())))))))))))
+               .bind("sizeof"),
+           // Ignore ARRAYSIZE(<array of containers>) pattern.
+           unless(hasAncestor(binaryOperator(
+               anyOf(hasOperatorName("/"), hasOperatorName("%")),
+               hasLHS(ignoringParenCasts(sizeOfExpr(expr()))),
+               hasRHS(ignoringParenCasts(equalsBoundNode("sizeof"))))))),
+      this);
+}
+
+void SizeofContainerCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *SizeOf =
+      Result.Nodes.getNodeAs<UnaryExprOrTypeTraitExpr>("sizeof");
+
+  auto Diag =
+      diag(SizeOf->getLocStart(), "sizeof() doesn't return the size of the "
+                                  "container; did you mean .size()?");
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/misc/SizeofContainerCheck.h b/clang-tidy/misc/SizeofContainerCheck.h
new file mode 100644 (file)
index 0000000..ed13ca5
--- /dev/null
@@ -0,0 +1,36 @@
+//===--- SizeofContainerCheck.h - clang-tidy---------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_SIZEOF_CONTAINER_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_SIZEOF_CONTAINER_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Find usages of sizeof on expressions of STL container types. Most likely the
+/// user wanted to use `.size()` instead.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/misc-sizeof-container.html
+class SizeofContainerCheck : public ClangTidyCheck {
+public:
+  SizeofContainerCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_SIZEOF_CONTAINER_H
diff --git a/clang-tidy/misc/SizeofExpressionCheck.cpp b/clang-tidy/misc/SizeofExpressionCheck.cpp
new file mode 100644 (file)
index 0000000..5b8e7e7
--- /dev/null
@@ -0,0 +1,265 @@
+//===--- SizeofExpressionCheck.cpp - clang-tidy----------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "SizeofExpressionCheck.h"
+#include "../utils/Matchers.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+namespace {
+
+AST_MATCHER_P(IntegerLiteral, isBiggerThan, unsigned, N) {
+  return Node.getValue().getZExtValue() > N;
+}
+
+AST_MATCHER_P2(Expr, hasSizeOfDescendant, int, Depth,
+               ast_matchers::internal::Matcher<Expr>, InnerMatcher) {
+  if (Depth < 0)
+    return false;
+
+  const Expr *E = Node.IgnoreParenImpCasts();
+  if (InnerMatcher.matches(*E, Finder, Builder))
+    return true;
+
+  if (const auto *CE = dyn_cast<CastExpr>(E)) {
+    const auto M = hasSizeOfDescendant(Depth - 1, InnerMatcher);
+    return M.matches(*CE->getSubExpr(), Finder, Builder);
+  } else if (const auto *UE = dyn_cast<UnaryOperator>(E)) {
+    const auto M = hasSizeOfDescendant(Depth - 1, InnerMatcher);
+    return M.matches(*UE->getSubExpr(), Finder, Builder);
+  } else if (const auto *BE = dyn_cast<BinaryOperator>(E)) {
+    const auto LHS = hasSizeOfDescendant(Depth - 1, InnerMatcher);
+    const auto RHS = hasSizeOfDescendant(Depth - 1, InnerMatcher);
+    return LHS.matches(*BE->getLHS(), Finder, Builder) ||
+           RHS.matches(*BE->getRHS(), Finder, Builder);
+  }
+
+  return false;
+}
+
+CharUnits getSizeOfType(const ASTContext &Ctx, const Type *Ty) {
+  if (!Ty || Ty->isIncompleteType() || Ty->isDependentType() ||
+      isa<DependentSizedArrayType>(Ty) || !Ty->isConstantSizeType())
+    return CharUnits::Zero();
+  return Ctx.getTypeSizeInChars(Ty);
+}
+
+} // namespace
+
+SizeofExpressionCheck::SizeofExpressionCheck(StringRef Name,
+                                             ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      WarnOnSizeOfConstant(Options.get("WarnOnSizeOfConstant", 1) != 0),
+      WarnOnSizeOfThis(Options.get("WarnOnSizeOfThis", 1) != 0),
+      WarnOnSizeOfCompareToConstant(
+          Options.get("WarnOnSizeOfCompareToConstant", 1) != 0) {}
+
+void SizeofExpressionCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "WarnOnSizeOfConstant", WarnOnSizeOfConstant);
+  Options.store(Opts, "WarnOnSizeOfThis", WarnOnSizeOfThis);
+  Options.store(Opts, "WarnOnSizeOfCompareToConstant",
+                WarnOnSizeOfCompareToConstant);
+}
+
+void SizeofExpressionCheck::registerMatchers(MatchFinder *Finder) {
+  const auto IntegerExpr = ignoringParenImpCasts(integerLiteral());
+  const auto ConstantExpr = expr(ignoringParenImpCasts(
+      anyOf(integerLiteral(), unaryOperator(hasUnaryOperand(IntegerExpr)),
+            binaryOperator(hasLHS(IntegerExpr), hasRHS(IntegerExpr)))));
+  const auto SizeOfExpr =
+      expr(anyOf(sizeOfExpr(has(type())), sizeOfExpr(has(expr()))));
+  const auto SizeOfZero = expr(
+      sizeOfExpr(has(ignoringParenImpCasts(expr(integerLiteral(equals(0)))))));
+
+  // Detect expression like: sizeof(ARRAYLEN);
+  // Note: The expression 'sizeof(sizeof(0))' is a portable trick used to know
+  //       the sizeof size_t.
+  if (WarnOnSizeOfConstant) {
+    Finder->addMatcher(
+        expr(sizeOfExpr(has(ignoringParenImpCasts(ConstantExpr))),
+             unless(SizeOfZero))
+            .bind("sizeof-constant"),
+        this);
+  }
+
+  // Detect expression like: sizeof(this);
+  if (WarnOnSizeOfThis) {
+    Finder->addMatcher(
+        expr(sizeOfExpr(has(ignoringParenImpCasts(expr(cxxThisExpr())))))
+            .bind("sizeof-this"),
+        this);
+  }
+
+  // Detect sizeof(kPtr) where kPtr is 'const char* kPtr = "abc"';
+  const auto CharPtrType = pointerType(pointee(isAnyCharacter()));
+  const auto ConstStrLiteralDecl =
+      varDecl(isDefinition(), hasType(qualType(hasCanonicalType(CharPtrType))),
+              hasInitializer(ignoringParenImpCasts(stringLiteral())));
+  Finder->addMatcher(expr(sizeOfExpr(has(ignoringParenImpCasts(expr(
+                              hasType(qualType(hasCanonicalType(CharPtrType))),
+                              ignoringParenImpCasts(declRefExpr(
+                                  hasDeclaration(ConstStrLiteralDecl))))))))
+                         .bind("sizeof-charp"),
+                     this);
+
+  // Detect sizeof(ptr) where ptr points to an aggregate (i.e. sizeof(&S)).
+  const auto ArrayExpr = expr(ignoringParenImpCasts(
+      expr(hasType(qualType(hasCanonicalType(arrayType()))))));
+  const auto ArrayCastExpr = expr(anyOf(
+      unaryOperator(hasUnaryOperand(ArrayExpr), unless(hasOperatorName("*"))),
+      binaryOperator(hasEitherOperand(ArrayExpr)),
+      castExpr(hasSourceExpression(ArrayExpr))));
+  const auto PointerToArrayExpr = expr(ignoringParenImpCasts(expr(
+      hasType(qualType(hasCanonicalType(pointerType(pointee(arrayType()))))))));
+
+  const auto StructAddrOfExpr =
+      unaryOperator(hasOperatorName("&"),
+                    hasUnaryOperand(ignoringParenImpCasts(expr(
+                        hasType(qualType(hasCanonicalType(recordType())))))));
+
+  Finder->addMatcher(
+      expr(sizeOfExpr(has(expr(ignoringParenImpCasts(
+               anyOf(ArrayCastExpr, PointerToArrayExpr, StructAddrOfExpr))))))
+          .bind("sizeof-pointer-to-aggregate"),
+      this);
+
+  // Detect expression like: sizeof(epxr) <= k for a suspicious constant 'k'.
+  if (WarnOnSizeOfCompareToConstant) {
+    Finder->addMatcher(
+        binaryOperator(matchers::isRelationalOperator(),
+                       hasEitherOperand(ignoringParenImpCasts(SizeOfExpr)),
+                       hasEitherOperand(ignoringParenImpCasts(
+                           anyOf(integerLiteral(equals(0)),
+                                 integerLiteral(isBiggerThan(0x80000))))))
+            .bind("sizeof-compare-constant"),
+        this);
+  }
+
+  // Detect expression like: sizeof(expr, expr); most likely an error.
+  Finder->addMatcher(expr(sizeOfExpr(has(expr(ignoringParenImpCasts(
+                              binaryOperator(hasOperatorName(",")))))))
+                         .bind("sizeof-comma-expr"),
+                     this);
+
+  // Detect sizeof(...) /sizeof(...));
+  const auto ElemType =
+      arrayType(hasElementType(recordType().bind("elem-type")));
+  const auto ElemPtrType = pointerType(pointee(type().bind("elem-ptr-type")));
+  const auto NumType = qualType(hasCanonicalType(
+      type(anyOf(ElemType, ElemPtrType, type())).bind("num-type")));
+  const auto DenomType = qualType(hasCanonicalType(type().bind("denom-type")));
+
+  Finder->addMatcher(
+      binaryOperator(hasOperatorName("/"),
+                     hasLHS(expr(ignoringParenImpCasts(
+                         anyOf(sizeOfExpr(has(NumType)),
+                               sizeOfExpr(has(expr(hasType(NumType)))))))),
+                     hasRHS(expr(ignoringParenImpCasts(
+                         anyOf(sizeOfExpr(has(DenomType)),
+                               sizeOfExpr(has(expr(hasType(DenomType)))))))))
+          .bind("sizeof-divide-expr"),
+      this);
+
+  // Detect expression like: sizeof(...) * sizeof(...)); most likely an error.
+  Finder->addMatcher(binaryOperator(hasOperatorName("*"),
+                                    hasLHS(ignoringParenImpCasts(SizeOfExpr)),
+                                    hasRHS(ignoringParenImpCasts(SizeOfExpr)))
+                         .bind("sizeof-multiply-sizeof"),
+                     this);
+
+  Finder->addMatcher(
+      binaryOperator(hasOperatorName("*"),
+                     hasEitherOperand(ignoringParenImpCasts(SizeOfExpr)),
+                     hasEitherOperand(ignoringParenImpCasts(binaryOperator(
+                         hasOperatorName("*"),
+                         hasEitherOperand(ignoringParenImpCasts(SizeOfExpr))))))
+          .bind("sizeof-multiply-sizeof"),
+      this);
+
+  // Detect strange double-sizeof expression like: sizeof(sizeof(...));
+  // Note: The expression 'sizeof(sizeof(0))' is accepted.
+  Finder->addMatcher(
+      expr(sizeOfExpr(has(ignoringParenImpCasts(expr(
+               hasSizeOfDescendant(8, expr(SizeOfExpr, unless(SizeOfZero))))))))
+          .bind("sizeof-sizeof-expr"),
+      this);
+}
+
+void SizeofExpressionCheck::check(const MatchFinder::MatchResult &Result) {
+  const ASTContext &Ctx = *Result.Context;
+
+  if (const auto *E = Result.Nodes.getNodeAs<Expr>("sizeof-constant")) {
+    diag(E->getLocStart(),
+         "suspicious usage of 'sizeof(K)'; did you mean 'K'?");
+  } else if (const auto *E = Result.Nodes.getNodeAs<Expr>("sizeof-this")) {
+    diag(E->getLocStart(),
+         "suspicious usage of 'sizeof(this)'; did you mean 'sizeof(*this)'");
+  } else if (const auto *E = Result.Nodes.getNodeAs<Expr>("sizeof-charp")) {
+    diag(E->getLocStart(),
+         "suspicious usage of 'sizeof(char*)'; do you mean 'strlen'?");
+  } else if (const auto *E =
+                 Result.Nodes.getNodeAs<Expr>("sizeof-pointer-to-aggregate")) {
+    diag(E->getLocStart(),
+         "suspicious usage of 'sizeof(A*)'; pointer to aggregate");
+  } else if (const auto *E =
+                 Result.Nodes.getNodeAs<Expr>("sizeof-compare-constant")) {
+    diag(E->getLocStart(),
+         "suspicious comparison of 'sizeof(expr)' to a constant");
+  } else if (const auto *E =
+                 Result.Nodes.getNodeAs<Expr>("sizeof-comma-expr")) {
+    diag(E->getLocStart(), "suspicious usage of 'sizeof(..., ...)'");
+  } else if (const auto *E =
+                 Result.Nodes.getNodeAs<Expr>("sizeof-divide-expr")) {
+    const auto *NumTy = Result.Nodes.getNodeAs<Type>("num-type");
+    const auto *DenomTy = Result.Nodes.getNodeAs<Type>("denom-type");
+    const auto *ElementTy = Result.Nodes.getNodeAs<Type>("elem-type");
+    const auto *PointedTy = Result.Nodes.getNodeAs<Type>("elem-ptr-type");
+
+    CharUnits NumeratorSize = getSizeOfType(Ctx, NumTy);
+    CharUnits DenominatorSize = getSizeOfType(Ctx, DenomTy);
+    CharUnits ElementSize = getSizeOfType(Ctx, ElementTy);
+
+    if (DenominatorSize > CharUnits::Zero() &&
+        !NumeratorSize.isMultipleOf(DenominatorSize)) {
+      diag(E->getLocStart(), "suspicious usage of 'sizeof(...)/sizeof(...)';"
+                             " numerator is not a multiple of denominator");
+    } else if (ElementSize > CharUnits::Zero() &&
+               DenominatorSize > CharUnits::Zero() &&
+               ElementSize != DenominatorSize) {
+      diag(E->getLocStart(), "suspicious usage of 'sizeof(...)/sizeof(...)';"
+                             " numerator is not a multiple of denominator");
+    } else if (NumTy && DenomTy && NumTy == DenomTy) {
+      diag(E->getLocStart(),
+           "suspicious usage of sizeof pointer 'sizeof(T)/sizeof(T)'");
+    } else if (PointedTy && DenomTy && PointedTy == DenomTy) {
+      diag(E->getLocStart(),
+           "suspicious usage of sizeof pointer 'sizeof(T*)/sizeof(T)'");
+    } else if (NumTy && DenomTy && NumTy->isPointerType() &&
+               DenomTy->isPointerType()) {
+      diag(E->getLocStart(),
+           "suspicious usage of sizeof pointer 'sizeof(P*)/sizeof(Q*)'");
+    }
+  } else if (const auto *E =
+                 Result.Nodes.getNodeAs<Expr>("sizeof-sizeof-expr")) {
+    diag(E->getLocStart(), "suspicious usage of 'sizeof(sizeof(...))'");
+  } else if (const auto *E =
+                 Result.Nodes.getNodeAs<Expr>("sizeof-multiply-sizeof")) {
+    diag(E->getLocStart(), "suspicious 'sizeof' by 'sizeof' multiplication");
+  }
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/misc/SizeofExpressionCheck.h b/clang-tidy/misc/SizeofExpressionCheck.h
new file mode 100644 (file)
index 0000000..d2ba9da
--- /dev/null
@@ -0,0 +1,40 @@
+//===--- SizeofExpressionCheck.h - clang-tidy--------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_SIZEOF_EXPRESSION_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_SIZEOF_EXPRESSION_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Find suspicious usages of sizeof expression.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/misc-sizeof-expression.html
+class SizeofExpressionCheck : public ClangTidyCheck {
+public:
+  SizeofExpressionCheck(StringRef Name, ClangTidyContext *Context);
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+  const bool WarnOnSizeOfConstant;
+  const bool WarnOnSizeOfThis;
+  const bool WarnOnSizeOfCompareToConstant;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_SIZEOF_EXPRESSION_H
diff --git a/clang-tidy/misc/StaticAssertCheck.cpp b/clang-tidy/misc/StaticAssertCheck.cpp
new file mode 100644 (file)
index 0000000..77fb58d
--- /dev/null
@@ -0,0 +1,177 @@
+//===--- StaticAssertCheck.cpp - clang-tidy -------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "StaticAssertCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Expr.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Lex/Lexer.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Casting.h"
+#include <string>
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+StaticAssertCheck::StaticAssertCheck(StringRef Name, ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context) {}
+
+void StaticAssertCheck::registerMatchers(MatchFinder *Finder) {
+  // This checker only makes sense for languages that have static assertion
+  // capabilities: C++11 and C11.
+  if (!(getLangOpts().CPlusPlus11 || getLangOpts().C11))
+    return;
+
+  auto NegatedString = unaryOperator(
+      hasOperatorName("!"), hasUnaryOperand(ignoringImpCasts(stringLiteral())));
+  auto IsAlwaysFalse =
+      expr(anyOf(cxxBoolLiteral(equals(false)), integerLiteral(equals(0)),
+                 cxxNullPtrLiteralExpr(), gnuNullExpr(), NegatedString))
+          .bind("isAlwaysFalse");
+  auto IsAlwaysFalseWithCast = ignoringParenImpCasts(anyOf(
+      IsAlwaysFalse, cStyleCastExpr(has(ignoringParenImpCasts(IsAlwaysFalse)))
+                         .bind("castExpr")));
+  auto AssertExprRoot = anyOf(
+      binaryOperator(
+          anyOf(hasOperatorName("&&"), hasOperatorName("==")),
+          hasEitherOperand(ignoringImpCasts(stringLiteral().bind("assertMSG"))),
+          anyOf(binaryOperator(hasEitherOperand(IsAlwaysFalseWithCast)),
+                anything()))
+          .bind("assertExprRoot"),
+      IsAlwaysFalse);
+  auto NonConstexprFunctionCall =
+      callExpr(hasDeclaration(functionDecl(unless(isConstexpr()))));
+  auto AssertCondition =
+      expr(
+          anyOf(expr(ignoringParenCasts(anyOf(
+                    AssertExprRoot, unaryOperator(hasUnaryOperand(
+                                        ignoringParenCasts(AssertExprRoot)))))),
+                anything()),
+          unless(findAll(NonConstexprFunctionCall)))
+          .bind("condition");
+  auto Condition =
+      anyOf(ignoringParenImpCasts(callExpr(
+                hasDeclaration(functionDecl(hasName("__builtin_expect"))),
+                hasArgument(0, AssertCondition))),
+            AssertCondition);
+
+  Finder->addMatcher(conditionalOperator(hasCondition(Condition),
+                                         unless(isInTemplateInstantiation()))
+                         .bind("condStmt"),
+                     this);
+
+  Finder->addMatcher(
+      ifStmt(hasCondition(Condition), unless(isInTemplateInstantiation()))
+          .bind("condStmt"),
+      this);
+}
+
+void StaticAssertCheck::check(const MatchFinder::MatchResult &Result) {
+  const ASTContext *ASTCtx = Result.Context;
+  const LangOptions &Opts = ASTCtx->getLangOpts();
+  const SourceManager &SM = ASTCtx->getSourceManager();
+  const auto *CondStmt = Result.Nodes.getNodeAs<Stmt>("condStmt");
+  const auto *Condition = Result.Nodes.getNodeAs<Expr>("condition");
+  const auto *IsAlwaysFalse = Result.Nodes.getNodeAs<Expr>("isAlwaysFalse");
+  const auto *AssertMSG = Result.Nodes.getNodeAs<StringLiteral>("assertMSG");
+  const auto *AssertExprRoot =
+      Result.Nodes.getNodeAs<BinaryOperator>("assertExprRoot");
+  const auto *CastExpr = Result.Nodes.getNodeAs<CStyleCastExpr>("castExpr");
+  SourceLocation AssertExpansionLoc = CondStmt->getLocStart();
+
+  if (!AssertExpansionLoc.isValid() || !AssertExpansionLoc.isMacroID())
+    return;
+
+  StringRef MacroName =
+      Lexer::getImmediateMacroName(AssertExpansionLoc, SM, Opts);
+
+  if (MacroName != "assert" || Condition->isValueDependent() ||
+      Condition->isTypeDependent() || Condition->isInstantiationDependent() ||
+      !Condition->isEvaluatable(*ASTCtx))
+    return;
+
+  // False literal is not the result of macro expansion.
+  if (IsAlwaysFalse && (!CastExpr || CastExpr->getType()->isPointerType())) {
+    SourceLocation FalseLiteralLoc =
+        SM.getImmediateSpellingLoc(IsAlwaysFalse->getExprLoc());
+    if (!FalseLiteralLoc.isMacroID())
+      return;
+
+    StringRef FalseMacroName =
+        Lexer::getImmediateMacroName(FalseLiteralLoc, SM, Opts);
+    if (FalseMacroName.compare_lower("false") == 0 ||
+        FalseMacroName.compare_lower("null") == 0)
+      return;
+  }
+
+  SourceLocation AssertLoc = SM.getImmediateMacroCallerLoc(AssertExpansionLoc);
+
+  SmallVector<FixItHint, 4> FixItHints;
+  SourceLocation LastParenLoc;
+  if (AssertLoc.isValid() && !AssertLoc.isMacroID() &&
+      (LastParenLoc = getLastParenLoc(ASTCtx, AssertLoc)).isValid()) {
+    FixItHints.push_back(
+        FixItHint::CreateReplacement(SourceRange(AssertLoc), "static_assert"));
+
+    std::string StaticAssertMSG = ", \"\"";
+    if (AssertExprRoot) {
+      FixItHints.push_back(FixItHint::CreateRemoval(
+          SourceRange(AssertExprRoot->getOperatorLoc())));
+      FixItHints.push_back(FixItHint::CreateRemoval(
+          SourceRange(AssertMSG->getLocStart(), AssertMSG->getLocEnd())));
+      StaticAssertMSG = (Twine(", \"") + AssertMSG->getString() + "\"").str();
+    }
+
+    FixItHints.push_back(
+        FixItHint::CreateInsertion(LastParenLoc, StaticAssertMSG));
+  }
+
+  diag(AssertLoc, "found assert() that could be replaced by static_assert()")
+      << FixItHints;
+}
+
+SourceLocation StaticAssertCheck::getLastParenLoc(const ASTContext *ASTCtx,
+                                                  SourceLocation AssertLoc) {
+  const LangOptions &Opts = ASTCtx->getLangOpts();
+  const SourceManager &SM = ASTCtx->getSourceManager();
+
+  llvm::MemoryBuffer *Buffer = SM.getBuffer(SM.getFileID(AssertLoc));
+  if (!Buffer)
+    return SourceLocation();
+
+  const char *BufferPos = SM.getCharacterData(AssertLoc);
+
+  Token Token;
+  Lexer Lexer(SM.getLocForStartOfFile(SM.getFileID(AssertLoc)), Opts,
+              Buffer->getBufferStart(), BufferPos, Buffer->getBufferEnd());
+
+  //        assert                          first left parenthesis
+  if (Lexer.LexFromRawLexer(Token) || Lexer.LexFromRawLexer(Token) ||
+      !Token.is(tok::l_paren))
+    return SourceLocation();
+
+  unsigned int ParenCount = 1;
+  while (ParenCount && !Lexer.LexFromRawLexer(Token)) {
+    if (Token.is(tok::l_paren))
+      ++ParenCount;
+    else if (Token.is(tok::r_paren))
+      --ParenCount;
+  }
+
+  return Token.getLocation();
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/misc/StaticAssertCheck.h b/clang-tidy/misc/StaticAssertCheck.h
new file mode 100644 (file)
index 0000000..faefce1
--- /dev/null
@@ -0,0 +1,41 @@
+//===--- StaticAssertCheck.h - clang-tidy -----------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_STATICASSERTCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_STATICASSERTCHECK_H
+
+#include "../ClangTidy.h"
+#include "llvm/ADT/StringRef.h"
+#include <string>
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Replaces `assert()` with `static_assert()` if the condition is evaluatable
+/// at compile time.
+///
+/// The condition of `static_assert()` is evaluated at compile time which is
+/// safer and more efficient.
+class StaticAssertCheck : public ClangTidyCheck {
+public:
+  StaticAssertCheck(StringRef Name, ClangTidyContext *Context);
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+  SourceLocation getLastParenLoc(const ASTContext *ASTCtx,
+                                 SourceLocation AssertLoc);
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_STATICASSERTCHECK_H
diff --git a/clang-tidy/misc/StringCompareCheck.cpp b/clang-tidy/misc/StringCompareCheck.cpp
new file mode 100644 (file)
index 0000000..b6a4295
--- /dev/null
@@ -0,0 +1,82 @@
+//===--- MiscStringCompare.cpp - clang-tidy--------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "StringCompareCheck.h"
+#include "../utils/FixItHintUtils.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Tooling/FixIt.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+static const StringRef CompareMessage = "do not use 'compare' to test equality "
+                                        "of strings; use the string equality "
+                                        "operator instead";
+
+void StringCompareCheck::registerMatchers(MatchFinder *Finder) {
+  if (!getLangOpts().CPlusPlus)
+    return;
+
+  const auto StrCompare = cxxMemberCallExpr(
+      callee(cxxMethodDecl(hasName("compare"),
+                           ofClass(classTemplateSpecializationDecl(
+                               hasName("::std::basic_string"))))),
+      hasArgument(0, expr().bind("str2")), argumentCountIs(1),
+      callee(memberExpr().bind("str1")));
+
+  // First and second case: cast str.compare(str) to boolean.
+  Finder->addMatcher(implicitCastExpr(hasImplicitDestinationType(booleanType()),
+                                      has(StrCompare))
+                         .bind("match1"),
+                     this);
+
+  // Third and fourth case: str.compare(str) == 0 and str.compare(str) != 0.
+  Finder->addMatcher(
+      binaryOperator(anyOf(hasOperatorName("=="), hasOperatorName("!=")),
+                     hasEitherOperand(StrCompare.bind("compare")),
+                     hasEitherOperand(integerLiteral(equals(0)).bind("zero")))
+          .bind("match2"),
+      this);
+}
+
+void StringCompareCheck::check(const MatchFinder::MatchResult &Result) {
+  if (const auto *Matched = Result.Nodes.getNodeAs<Stmt>("match1")) {
+    diag(Matched->getLocStart(), CompareMessage);
+    return;
+  }
+
+  if (const auto *Matched = Result.Nodes.getNodeAs<Stmt>("match2")) {
+    const ASTContext &Ctx = *Result.Context;
+
+    if (const auto *Zero = Result.Nodes.getNodeAs<Stmt>("zero")) {
+      const auto *Str1 = Result.Nodes.getNodeAs<MemberExpr>("str1");
+      const auto *Str2 = Result.Nodes.getNodeAs<Stmt>("str2");
+      const auto *Compare = Result.Nodes.getNodeAs<Stmt>("compare");
+
+      auto Diag = diag(Matched->getLocStart(), CompareMessage);
+
+      if (Str1->isArrow())
+        Diag << FixItHint::CreateInsertion(Str1->getLocStart(), "*");
+
+      Diag << tooling::fixit::createReplacement(*Zero, *Str2, Ctx)
+           << tooling::fixit::createReplacement(*Compare, *Str1->getBase(),
+                                                Ctx);
+    }
+  }
+
+  // FIXME: Add fixit to fix the code for case one and two (match1).
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/misc/StringCompareCheck.h b/clang-tidy/misc/StringCompareCheck.h
new file mode 100644 (file)
index 0000000..2f3a7d6
--- /dev/null
@@ -0,0 +1,36 @@
+//===--- StringCompareCheck.h - clang-tidy-----------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_STRING_COMPARE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_STRING_COMPARE_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// This check flags all calls compare when used to check for string
+/// equality or inequality.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/misc-string-compare.html
+class StringCompareCheck : public ClangTidyCheck {
+public:
+  StringCompareCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_STRING_COMPARE_H
diff --git a/clang-tidy/misc/StringConstructorCheck.cpp b/clang-tidy/misc/StringConstructorCheck.cpp
new file mode 100644 (file)
index 0000000..7ff4881
--- /dev/null
@@ -0,0 +1,134 @@
+//===--- StringConstructorCheck.cpp - clang-tidy---------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "StringConstructorCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Tooling/FixIt.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+AST_MATCHER_P(IntegerLiteral, isBiggerThan, unsigned, N) {
+  return Node.getValue().getZExtValue() > N;
+}
+
+StringConstructorCheck::StringConstructorCheck(StringRef Name,
+                                               ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      WarnOnLargeLength(Options.get("WarnOnLargeLength", 1) != 0),
+      LargeLengthThreshold(Options.get("LargeLengthThreshold", 0x800000)) {}
+
+void StringConstructorCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "WarnOnLargeLength", WarnOnLargeLength);
+  Options.store(Opts, "LargeLengthThreshold", LargeLengthThreshold);
+}
+
+void StringConstructorCheck::registerMatchers(MatchFinder *Finder) {
+  if (!getLangOpts().CPlusPlus)
+    return;
+
+  const auto ZeroExpr = expr(ignoringParenImpCasts(integerLiteral(equals(0))));
+  const auto CharExpr = expr(ignoringParenImpCasts(characterLiteral()));
+  const auto NegativeExpr = expr(ignoringParenImpCasts(
+      unaryOperator(hasOperatorName("-"),
+                    hasUnaryOperand(integerLiteral(unless(equals(0)))))));
+  const auto LargeLengthExpr = expr(ignoringParenImpCasts(
+      integerLiteral(isBiggerThan(LargeLengthThreshold))));
+  const auto CharPtrType = type(anyOf(pointerType(), arrayType()));
+
+  // Match a string-literal; even through a declaration with initializer.
+  const auto BoundStringLiteral = stringLiteral().bind("str");
+  const auto ConstStrLiteralDecl = varDecl(
+      isDefinition(), hasType(constantArrayType()), hasType(isConstQualified()),
+      hasInitializer(ignoringParenImpCasts(BoundStringLiteral)));
+  const auto ConstPtrStrLiteralDecl = varDecl(
+      isDefinition(),
+      hasType(pointerType(pointee(isAnyCharacter(), isConstQualified()))),
+      hasInitializer(ignoringParenImpCasts(BoundStringLiteral)));
+  const auto ConstStrLiteral = expr(ignoringParenImpCasts(anyOf(
+      BoundStringLiteral, declRefExpr(hasDeclaration(anyOf(
+                              ConstPtrStrLiteralDecl, ConstStrLiteralDecl))))));
+
+  // Check the fill constructor. Fills the string with n consecutive copies of
+  // character c. [i.e string(size_t n, char c);].
+  Finder->addMatcher(
+      cxxConstructExpr(
+          hasDeclaration(cxxMethodDecl(hasName("basic_string"))),
+          hasArgument(0, hasType(qualType(isInteger()))),
+          hasArgument(1, hasType(qualType(isInteger()))),
+          anyOf(
+              // Detect the expression: string('x', 40);
+              hasArgument(0, CharExpr.bind("swapped-parameter")),
+              // Detect the expression: string(0, ...);
+              hasArgument(0, ZeroExpr.bind("empty-string")),
+              // Detect the expression: string(-4, ...);
+              hasArgument(0, NegativeExpr.bind("negative-length")),
+              // Detect the expression: string(0x1234567, ...);
+              hasArgument(0, LargeLengthExpr.bind("large-length"))))
+          .bind("constructor"),
+      this);
+
+  // Check the literal string constructor with char pointer and length
+  // parameters. [i.e. string (const char* s, size_t n);]
+  Finder->addMatcher(
+      cxxConstructExpr(
+          hasDeclaration(cxxMethodDecl(hasName("basic_string"))),
+          hasArgument(0, hasType(CharPtrType)),
+          hasArgument(1, hasType(isInteger())),
+          anyOf(
+              // Detect the expression: string("...", 0);
+              hasArgument(1, ZeroExpr.bind("empty-string")),
+              // Detect the expression: string("...", -4);
+              hasArgument(1, NegativeExpr.bind("negative-length")),
+              // Detect the expression: string("lit", 0x1234567);
+              hasArgument(1, LargeLengthExpr.bind("large-length")),
+              // Detect the expression: string("lit", 5)
+              allOf(hasArgument(0, ConstStrLiteral.bind("literal-with-length")),
+                    hasArgument(1, ignoringParenImpCasts(
+                                       integerLiteral().bind("int"))))))
+          .bind("constructor"),
+      this);
+}
+
+void StringConstructorCheck::check(const MatchFinder::MatchResult &Result) {
+  const ASTContext &Ctx = *Result.Context;
+  const auto *E = Result.Nodes.getNodeAs<CXXConstructExpr>("constructor");
+  assert(E && "missing constructor expression");
+  SourceLocation Loc = E->getLocStart();
+
+  if (Result.Nodes.getNodeAs<Expr>("swapped-parameter")) {
+    const Expr *P0 = E->getArg(0);
+    const Expr *P1 = E->getArg(1);
+    diag(Loc, "string constructor parameters are probably swapped;"
+              " expecting string(count, character)")
+        << tooling::fixit::createReplacement(*P0, *P1, Ctx)
+        << tooling::fixit::createReplacement(*P1, *P0, Ctx);
+  } else if (Result.Nodes.getNodeAs<Expr>("empty-string")) {
+    diag(Loc, "constructor creating an empty string");
+  } else if (Result.Nodes.getNodeAs<Expr>("negative-length")) {
+    diag(Loc, "negative value used as length parameter");
+  } else if (Result.Nodes.getNodeAs<Expr>("large-length")) {
+    if (WarnOnLargeLength)
+      diag(Loc, "suspicious large length parameter");
+  } else if (Result.Nodes.getNodeAs<Expr>("literal-with-length")) {
+    const auto *Str = Result.Nodes.getNodeAs<StringLiteral>("str");
+    const auto *Lit = Result.Nodes.getNodeAs<IntegerLiteral>("int");
+    if (Lit->getValue().ugt(Str->getLength())) {
+      diag(Loc, "length is bigger then string literal size");
+    }
+  }
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/misc/StringConstructorCheck.h b/clang-tidy/misc/StringConstructorCheck.h
new file mode 100644 (file)
index 0000000..ca32fb6
--- /dev/null
@@ -0,0 +1,39 @@
+//===--- StringConstructorCheck.h - clang-tidy-------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_STRING_CONSTRUCTOR_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_STRING_CONSTRUCTOR_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Finds suspicious string constructor and check their parameters.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/misc-string-constructor.html
+class StringConstructorCheck : public ClangTidyCheck {
+public:
+  StringConstructorCheck(StringRef Name, ClangTidyContext *Context);
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+  const bool WarnOnLargeLength;
+  const unsigned int LargeLengthThreshold;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_STRING_CONSTRUCTOR_H
diff --git a/clang-tidy/misc/StringIntegerAssignmentCheck.cpp b/clang-tidy/misc/StringIntegerAssignmentCheck.cpp
new file mode 100644 (file)
index 0000000..4e8c488
--- /dev/null
@@ -0,0 +1,86 @@
+//===--- StringIntegerAssignmentCheck.cpp - clang-tidy---------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "StringIntegerAssignmentCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+void StringIntegerAssignmentCheck::registerMatchers(MatchFinder *Finder) {
+  if (!getLangOpts().CPlusPlus)
+    return;
+  Finder->addMatcher(
+      cxxOperatorCallExpr(
+          anyOf(hasOverloadedOperatorName("="),
+                hasOverloadedOperatorName("+=")),
+          callee(cxxMethodDecl(ofClass(classTemplateSpecializationDecl(
+              hasName("::std::basic_string"),
+              hasTemplateArgument(0, refersToType(qualType().bind("type"))))))),
+          hasArgument(1,
+                      ignoringImpCasts(expr(hasType(isInteger()),
+                                            unless(hasType(isAnyCharacter())))
+                                           .bind("expr"))),
+          unless(isInTemplateInstantiation())),
+      this);
+}
+
+void StringIntegerAssignmentCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  const auto *Argument = Result.Nodes.getNodeAs<Expr>("expr");
+  SourceLocation Loc = Argument->getLocStart();
+
+  auto Diag =
+      diag(Loc, "an integer is interpreted as a character code when assigning "
+                "it to a string; if this is intended, cast the integer to the "
+                "appropriate character type; if you want a string "
+                "representation, use the appropriate conversion facility");
+
+  if (Loc.isMacroID())
+    return;
+
+  auto CharType = *Result.Nodes.getNodeAs<QualType>("type");
+  bool IsWideCharType = CharType->isWideCharType();
+  if (!CharType->isCharType() && !IsWideCharType)
+    return;
+  bool IsOneDigit = false;
+  bool IsLiteral = false;
+  if (const auto *Literal = dyn_cast<IntegerLiteral>(Argument)) {
+    IsOneDigit = Literal->getValue().getLimitedValue() < 10;
+    IsLiteral = true;
+  }
+
+  SourceLocation EndLoc = Lexer::getLocForEndOfToken(
+      Argument->getLocEnd(), 0, *Result.SourceManager, getLangOpts());
+  if (IsOneDigit) {
+    Diag << FixItHint::CreateInsertion(Loc, IsWideCharType ? "L'" : "'")
+         << FixItHint::CreateInsertion(EndLoc, "'");
+    return;
+  }
+  if (IsLiteral) {
+    Diag << FixItHint::CreateInsertion(Loc, IsWideCharType ? "L\"" : "\"")
+         << FixItHint::CreateInsertion(EndLoc, "\"");
+    return;
+  }
+
+  if (getLangOpts().CPlusPlus11) {
+    Diag << FixItHint::CreateInsertion(Loc, IsWideCharType ? "std::to_wstring("
+                                                           : "std::to_string(")
+         << FixItHint::CreateInsertion(EndLoc, ")");
+  }
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/misc/StringIntegerAssignmentCheck.h b/clang-tidy/misc/StringIntegerAssignmentCheck.h
new file mode 100644 (file)
index 0000000..7963362
--- /dev/null
@@ -0,0 +1,35 @@
+//===--- StringIntegerAssignmentCheck.h - clang-tidy-------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_STRING_INTEGER_ASSIGNMENT_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_STRING_INTEGER_ASSIGNMENT_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Finds instances where an integer is assigned to a string.
+///
+/// For more details see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/misc-string-assignment.html
+class StringIntegerAssignmentCheck : public ClangTidyCheck {
+public:
+  StringIntegerAssignmentCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_STRING_INTEGER_ASSIGNMENT_H
diff --git a/clang-tidy/misc/StringLiteralWithEmbeddedNulCheck.cpp b/clang-tidy/misc/StringLiteralWithEmbeddedNulCheck.cpp
new file mode 100644 (file)
index 0000000..335927b
--- /dev/null
@@ -0,0 +1,83 @@
+//===--- StringLiteralWithEmbeddedNulCheck.cpp - clang-tidy----------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "StringLiteralWithEmbeddedNulCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+AST_MATCHER(StringLiteral, containsNul) {
+  for (size_t i = 0; i < Node.getLength(); ++i)
+    if (Node.getCodeUnit(i) == '\0')
+      return true;
+  return false;
+}
+
+void StringLiteralWithEmbeddedNulCheck::registerMatchers(MatchFinder *Finder) {
+  // Match a string that contains embedded NUL character. Extra-checks are
+  // applied in |check| to find incorectly escaped characters.
+  Finder->addMatcher(stringLiteral(containsNul()).bind("strlit"), this);
+
+  // The remaining checks only apply to C++.
+  if (!getLangOpts().CPlusPlus)
+    return;
+
+  const auto StrLitWithNul =
+      ignoringParenImpCasts(stringLiteral(containsNul()).bind("truncated"));
+
+  // Match string constructor.
+  const auto StringConstructorExpr = expr(anyOf(
+      cxxConstructExpr(argumentCountIs(1),
+                       hasDeclaration(cxxMethodDecl(hasName("basic_string")))),
+      // If present, the second argument is the alloc object which must not
+      // be present explicitly.
+      cxxConstructExpr(argumentCountIs(2),
+                       hasDeclaration(cxxMethodDecl(hasName("basic_string"))),
+                       hasArgument(1, cxxDefaultArgExpr()))));
+
+  // Detect passing a suspicious string literal to a string constructor.
+  // example: std::string str = "abc\0def";
+  Finder->addMatcher(
+      cxxConstructExpr(StringConstructorExpr, hasArgument(0, StrLitWithNul)),
+      this);
+
+  // Detect passing a suspicious string literal through an overloaded operator.
+  Finder->addMatcher(cxxOperatorCallExpr(hasAnyArgument(StrLitWithNul)), this);
+}
+
+void StringLiteralWithEmbeddedNulCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  if (const auto *SL = Result.Nodes.getNodeAs<StringLiteral>("strlit")) {
+    for (size_t Offset = 0, Length = SL->getLength(); Offset < Length;
+         ++Offset) {
+      // Find a sequence of character like "\0x12".
+      if (Offset + 3 < Length && SL->getCodeUnit(Offset) == '\0' &&
+          SL->getCodeUnit(Offset + 1) == 'x' &&
+          isDigit(SL->getCodeUnit(Offset + 2)) &&
+          isDigit(SL->getCodeUnit(Offset + 3))) {
+        diag(SL->getLocStart(), "suspicious embedded NUL character");
+        return;
+      }
+    }
+  }
+
+  if (const auto *SL = Result.Nodes.getNodeAs<StringLiteral>("truncated")) {
+    diag(SL->getLocStart(),
+         "truncated string literal with embedded NUL character");
+  }
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/misc/StringLiteralWithEmbeddedNulCheck.h b/clang-tidy/misc/StringLiteralWithEmbeddedNulCheck.h
new file mode 100644 (file)
index 0000000..e4a87fc
--- /dev/null
@@ -0,0 +1,35 @@
+//===--- StringLiteralWithEmbeddedNulCheck.h - clang-tidy--------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_STRING_LITERAL_WITH_EMBEDDED_NUL_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_STRING_LITERAL_WITH_EMBEDDED_NUL_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Find suspicious string literals with embedded NUL characters.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/misc-string-literal-with-embedded-nul.html
+class StringLiteralWithEmbeddedNulCheck : public ClangTidyCheck {
+public:
+  StringLiteralWithEmbeddedNulCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_STRING_LITERAL_WITH_EMBEDDED_NUL_H
diff --git a/clang-tidy/misc/SuspiciousEnumUsageCheck.cpp b/clang-tidy/misc/SuspiciousEnumUsageCheck.cpp
new file mode 100644 (file)
index 0000000..501e2b2
--- /dev/null
@@ -0,0 +1,217 @@
+//===--- SuspiciousEnumUsageCheck.cpp - clang-tidy-------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "SuspiciousEnumUsageCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include <algorithm>
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+static const char DifferentEnumErrorMessage[] =
+    "enum values are from different enum types";
+
+static const char BitmaskErrorMessage[] =
+    "enum type seems like a bitmask (contains mostly "
+    "power-of-2 literals), but this literal is not a "
+    "power-of-2";
+
+static const char BitmaskVarErrorMessage[] =
+    "enum type seems like a bitmask (contains mostly "
+    "power-of-2 literals) but %plural{1:a literal is|:some literals are}0 not "
+    "power-of-2";
+
+static const char BitmaskNoteMessage[] = "used here as a bitmask";
+
+/// Stores a min and a max value which describe an interval.
+struct ValueRange {
+  llvm::APSInt MinVal;
+  llvm::APSInt MaxVal;
+
+  ValueRange(const EnumDecl *EnumDec) {
+    const auto MinMaxVal = std::minmax_element(
+        EnumDec->enumerator_begin(), EnumDec->enumerator_end(),
+        [](const EnumConstantDecl *E1, const EnumConstantDecl *E2) {
+          return E1->getInitVal() < E2->getInitVal();
+        });
+    MinVal = MinMaxVal.first->getInitVal();
+    MaxVal = MinMaxVal.second->getInitVal();
+  }
+};
+
+/// Return the number of EnumConstantDecls in an EnumDecl.
+static int enumLength(const EnumDecl *EnumDec) {
+  return std::distance(EnumDec->enumerator_begin(), EnumDec->enumerator_end());
+}
+
+static bool hasDisjointValueRange(const EnumDecl *Enum1,
+                                  const EnumDecl *Enum2) {
+  ValueRange Range1(Enum1), Range2(Enum2);
+  return (Range1.MaxVal < Range2.MinVal) || (Range2.MaxVal < Range1.MinVal);
+}
+
+static bool isNonPowerOf2NorNullLiteral(const EnumConstantDecl *EnumConst) {
+  llvm::APSInt Val = EnumConst->getInitVal();
+  if (Val.isPowerOf2() || !Val.getBoolValue())
+    return false;
+  const Expr *InitExpr = EnumConst->getInitExpr();
+  if (!InitExpr)
+    return true;
+  return isa<IntegerLiteral>(InitExpr->IgnoreImpCasts());
+}
+
+static bool isMaxValAllBitSetLiteral(const EnumDecl *EnumDec) {
+  auto EnumConst = std::max_element(
+      EnumDec->enumerator_begin(), EnumDec->enumerator_end(),
+      [](const EnumConstantDecl *E1, const EnumConstantDecl *E2) {
+        return E1->getInitVal() < E2->getInitVal();
+      });
+
+  if (const Expr *InitExpr = EnumConst->getInitExpr()) {
+    return EnumConst->getInitVal().countTrailingOnes() ==
+               EnumConst->getInitVal().getActiveBits() &&
+           isa<IntegerLiteral>(InitExpr->IgnoreImpCasts());
+  }
+  return false;
+}
+
+static int countNonPowOfTwoLiteralNum(const EnumDecl *EnumDec) {
+  return std::count_if(
+      EnumDec->enumerator_begin(), EnumDec->enumerator_end(),
+      [](const EnumConstantDecl *E) { return isNonPowerOf2NorNullLiteral(E); });
+}
+
+/// Check if there is one or two enumerators that are not a power of 2 and are
+/// initialized by a literal in the enum type, and that the enumeration contains
+/// enough elements to reasonably act as a bitmask. Exclude the case where the
+/// last enumerator is the sum of the lesser values (and initialized by a
+/// literal) or when it could contain consecutive values.
+static bool isPossiblyBitMask(const EnumDecl *EnumDec) {
+  ValueRange VR(EnumDec);
+  int EnumLen = enumLength(EnumDec);
+  int NonPowOfTwoCounter = countNonPowOfTwoLiteralNum(EnumDec);
+  return NonPowOfTwoCounter >= 1 && NonPowOfTwoCounter <= 2 &&
+         NonPowOfTwoCounter < EnumLen / 2 &&
+         (VR.MaxVal - VR.MinVal != EnumLen - 1) &&
+         !(NonPowOfTwoCounter == 1 && isMaxValAllBitSetLiteral(EnumDec));
+}
+
+SuspiciousEnumUsageCheck::SuspiciousEnumUsageCheck(StringRef Name,
+                                                   ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      StrictMode(Options.getLocalOrGlobal("StrictMode", 0)) {}
+
+void SuspiciousEnumUsageCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "StrictMode", StrictMode);
+}
+
+void SuspiciousEnumUsageCheck::registerMatchers(MatchFinder *Finder) {
+  const auto enumExpr = [](StringRef RefName, StringRef DeclName) {
+    return allOf(ignoringImpCasts(expr().bind(RefName)),
+                 ignoringImpCasts(hasType(enumDecl().bind(DeclName))));
+  };
+
+  Finder->addMatcher(
+      binaryOperator(hasOperatorName("|"), hasLHS(enumExpr("", "enumDecl")),
+                     hasRHS(allOf(enumExpr("", "otherEnumDecl"),
+                                  ignoringImpCasts(hasType(enumDecl(
+                                      unless(equalsBoundNode("enumDecl"))))))))
+          .bind("diffEnumOp"),
+      this);
+
+  Finder->addMatcher(
+      binaryOperator(anyOf(hasOperatorName("+"), hasOperatorName("|")),
+                     hasLHS(enumExpr("lhsExpr", "enumDecl")),
+                     hasRHS(allOf(enumExpr("rhsExpr", ""),
+                                  ignoringImpCasts(hasType(enumDecl(
+                                      equalsBoundNode("enumDecl"))))))),
+      this);
+
+  Finder->addMatcher(
+      binaryOperator(anyOf(hasOperatorName("+"), hasOperatorName("|")),
+                     hasEitherOperand(
+                         allOf(hasType(isInteger()), unless(enumExpr("", "")))),
+                     hasEitherOperand(enumExpr("enumExpr", "enumDecl"))),
+      this);
+
+  Finder->addMatcher(
+      binaryOperator(anyOf(hasOperatorName("|="), hasOperatorName("+=")),
+                     hasRHS(enumExpr("enumExpr", "enumDecl"))),
+      this);
+}
+
+void SuspiciousEnumUsageCheck::checkSuspiciousBitmaskUsage(
+    const Expr *NodeExpr, const EnumDecl *EnumDec) {
+  const auto *EnumExpr = dyn_cast<DeclRefExpr>(NodeExpr);
+  const auto *EnumConst =
+      EnumExpr ? dyn_cast<EnumConstantDecl>(EnumExpr->getDecl()) : nullptr;
+
+  // Report the parameter if neccessary.
+  if (!EnumConst) {
+    diag(EnumDec->getInnerLocStart(), BitmaskVarErrorMessage)
+        << countNonPowOfTwoLiteralNum(EnumDec);
+    diag(EnumExpr->getExprLoc(), BitmaskNoteMessage, DiagnosticIDs::Note);
+  } else if (isNonPowerOf2NorNullLiteral(EnumConst)) {
+    diag(EnumConst->getSourceRange().getBegin(), BitmaskErrorMessage);
+    diag(EnumExpr->getExprLoc(), BitmaskNoteMessage, DiagnosticIDs::Note);
+  }
+}
+
+void SuspiciousEnumUsageCheck::check(const MatchFinder::MatchResult &Result) {
+  // Case 1: The two enum values come from different types.
+  if (const auto *DiffEnumOp =
+          Result.Nodes.getNodeAs<BinaryOperator>("diffEnumOp")) {
+    const auto *EnumDec = Result.Nodes.getNodeAs<EnumDecl>("enumDecl");
+    const auto *OtherEnumDec =
+        Result.Nodes.getNodeAs<EnumDecl>("otherEnumDecl");
+    // Skip when one of the parameters is an empty enum. The
+    // hasDisjointValueRange function could not decide the values properly in
+    // case of an empty enum.
+    if (EnumDec->enumerator_begin() == EnumDec->enumerator_end() ||
+        OtherEnumDec->enumerator_begin() == OtherEnumDec->enumerator_end())
+      return;
+
+    if (!hasDisjointValueRange(EnumDec, OtherEnumDec))
+      diag(DiffEnumOp->getOperatorLoc(), DifferentEnumErrorMessage);
+    return;
+  }
+
+  // Case 2 and 3 only checked in strict mode. The checker tries to detect
+  // suspicious bitmasks which contains values initialized by non power-of-2
+  // literals.
+  if (!StrictMode)
+    return;
+  const auto *EnumDec = Result.Nodes.getNodeAs<EnumDecl>("enumDecl");
+  if (!isPossiblyBitMask(EnumDec))
+    return;
+
+  // Case 2:
+  //   a. Investigating the right hand side of `+=` or `|=` operator.
+  //   b. When the operator is `|` or `+` but only one of them is an EnumExpr
+  if (const auto *EnumExpr = Result.Nodes.getNodeAs<Expr>("enumExpr")) {
+    checkSuspiciousBitmaskUsage(EnumExpr, EnumDec);
+    return;
+  }
+
+  // Case 3:
+  // '|' or '+' operator where both argument comes from the same enum type
+  const auto *LhsExpr = Result.Nodes.getNodeAs<Expr>("lhsExpr");
+  checkSuspiciousBitmaskUsage(LhsExpr, EnumDec);
+
+  const auto *RhsExpr = Result.Nodes.getNodeAs<Expr>("rhsExpr");
+  checkSuspiciousBitmaskUsage(RhsExpr, EnumDec);
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/misc/SuspiciousEnumUsageCheck.h b/clang-tidy/misc/SuspiciousEnumUsageCheck.h
new file mode 100644 (file)
index 0000000..d532179
--- /dev/null
@@ -0,0 +1,39 @@
+//===--- SuspiciousEnumUsageCheck.h - clang-tidy--------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_SUSPICIOUS_ENUM_USAGE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_SUSPICIOUS_ENUM_USAGE_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// The checker detects various cases when an enum is probably misused (as a
+/// bitmask).
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/misc-suspicious-enum-usage.html
+class SuspiciousEnumUsageCheck : public ClangTidyCheck {
+public:
+  SuspiciousEnumUsageCheck(StringRef Name, ClangTidyContext *Context);
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+
+private:
+  void checkSuspiciousBitmaskUsage(const Expr*, const EnumDecl*);
+  const bool StrictMode;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_SUSPICIOUS_ENUM_USAGE_H
diff --git a/clang-tidy/misc/SuspiciousMissingCommaCheck.cpp b/clang-tidy/misc/SuspiciousMissingCommaCheck.cpp
new file mode 100644 (file)
index 0000000..a3ed38f
--- /dev/null
@@ -0,0 +1,129 @@
+//===--- SuspiciousMissingCommaCheck.cpp - clang-tidy----------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "SuspiciousMissingCommaCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+namespace {
+
+bool isConcatenatedLiteralsOnPurpose(ASTContext *Ctx,
+                                     const StringLiteral *Lit) {
+  // String literals surrounded by parentheses are assumed to be on purpose.
+  //    i.e.:  const char* Array[] = { ("a" "b" "c"), "d", [...] };
+  auto Parents = Ctx->getParents(*Lit);
+  if (Parents.size() == 1 && Parents[0].get<ParenExpr>() != nullptr)
+    return true;
+
+  // Appropriately indented string literals are assumed to be on purpose.
+  // The following frequent indentation is accepted:
+  //     const char* Array[] = {
+  //       "first literal"
+  //           "indented literal"
+  //           "indented literal",
+  //       "second literal",
+  //       [...]
+  //     };
+  const SourceManager &SM = Ctx->getSourceManager();
+  bool IndentedCorrectly = true;
+  SourceLocation FirstToken = Lit->getStrTokenLoc(0);
+  FileID BaseFID = SM.getFileID(FirstToken);
+  unsigned int BaseIndent = SM.getSpellingColumnNumber(FirstToken);
+  unsigned int BaseLine = SM.getSpellingLineNumber(FirstToken);
+  for (unsigned int TokNum = 1; TokNum < Lit->getNumConcatenated(); ++TokNum) {
+    SourceLocation Token = Lit->getStrTokenLoc(TokNum);
+    FileID FID = SM.getFileID(Token);
+    unsigned int Indent = SM.getSpellingColumnNumber(Token);
+    unsigned int Line = SM.getSpellingLineNumber(Token);
+    if (FID != BaseFID || Line != BaseLine + TokNum || Indent <= BaseIndent) {
+      IndentedCorrectly = false;
+      break;
+    }
+  }
+  if (IndentedCorrectly)
+    return true;
+
+  // There is no pattern recognized by the checker, assume it's not on purpose.
+  return false;
+}
+
+AST_MATCHER_P(StringLiteral, isConcatenatedLiteral, unsigned,
+              MaxConcatenatedTokens) {
+  return Node.getNumConcatenated() > 1 &&
+         Node.getNumConcatenated() < MaxConcatenatedTokens &&
+         !isConcatenatedLiteralsOnPurpose(&Finder->getASTContext(), &Node);
+}
+
+} // namespace
+
+SuspiciousMissingCommaCheck::SuspiciousMissingCommaCheck(
+    StringRef Name, ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      SizeThreshold(Options.get("SizeThreshold", 5U)),
+      RatioThreshold(std::stod(Options.get("RatioThreshold", ".2"))),
+      MaxConcatenatedTokens(Options.get("MaxConcatenatedTokens", 5U)) {}
+
+void SuspiciousMissingCommaCheck::storeOptions(
+    ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "SizeThreshold", SizeThreshold);
+  Options.store(Opts, "RatioThreshold", std::to_string(RatioThreshold));
+  Options.store(Opts, "MaxConcatenatedTokens", MaxConcatenatedTokens);
+}
+
+void SuspiciousMissingCommaCheck::registerMatchers(MatchFinder *Finder) {
+  const auto ConcatenatedStringLiteral =
+      stringLiteral(isConcatenatedLiteral(MaxConcatenatedTokens)).bind("str");
+
+  const auto StringsInitializerList =
+      initListExpr(hasType(constantArrayType()),
+                   has(ignoringParenImpCasts(expr(ConcatenatedStringLiteral))));
+
+  Finder->addMatcher(StringsInitializerList.bind("list"), this);
+}
+
+void SuspiciousMissingCommaCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  const auto *InitializerList = Result.Nodes.getNodeAs<InitListExpr>("list");
+  const auto *ConcatenatedLiteral =
+      Result.Nodes.getNodeAs<StringLiteral>("str");
+  assert(InitializerList && ConcatenatedLiteral);
+
+  // Skip small arrays as they often generate false-positive.
+  unsigned int Size = InitializerList->getNumInits();
+  if (Size < SizeThreshold)
+    return;
+
+  // Count the number of occurence of concatenated string literal.
+  unsigned int Count = 0;
+  for (unsigned int i = 0; i < Size; ++i) {
+    const Expr *Child = InitializerList->getInit(i)->IgnoreImpCasts();
+    if (const auto *Literal = dyn_cast<StringLiteral>(Child)) {
+      if (Literal->getNumConcatenated() > 1)
+        ++Count;
+    }
+  }
+
+  // Warn only when concatenation is not common in this initializer list.
+  // The current threshold is set to less than 1/5 of the string literals.
+  if (double(Count) / Size > RatioThreshold)
+    return;
+
+  diag(ConcatenatedLiteral->getLocStart(),
+       "suspicious string literal, probably missing a comma");
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/misc/SuspiciousMissingCommaCheck.h b/clang-tidy/misc/SuspiciousMissingCommaCheck.h
new file mode 100644 (file)
index 0000000..bf74ad1
--- /dev/null
@@ -0,0 +1,44 @@
+//===--- SuspiciousMissingCommaCheck.h - clang-tidy--------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_SUSPICIOUS_MISSING_COMMA_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_SUSPICIOUS_MISSING_COMMA_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// This check finds string literals which are probably concatenated
+/// accidentally.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/misc-suspicious-missing-comma.html
+class SuspiciousMissingCommaCheck : public ClangTidyCheck {
+public:
+  SuspiciousMissingCommaCheck(StringRef Name, ClangTidyContext *Context);
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+  // Minimal size of a string literals array to be considered by the checker.
+  const unsigned SizeThreshold;
+  // Maximal threshold ratio of suspicious string literals to be considered.
+  const double RatioThreshold;
+  // Maximal number of concatenated tokens.
+  const unsigned MaxConcatenatedTokens;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_SUSPICIOUS_MISSING_COMMA_H
diff --git a/clang-tidy/misc/SuspiciousSemicolonCheck.cpp b/clang-tidy/misc/SuspiciousSemicolonCheck.cpp
new file mode 100644 (file)
index 0000000..e39a459
--- /dev/null
@@ -0,0 +1,77 @@
+//===--- SuspiciousSemicolonCheck.cpp - clang-tidy-------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "SuspiciousSemicolonCheck.h"
+#include "../utils/LexerUtils.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+void SuspiciousSemicolonCheck::registerMatchers(MatchFinder *Finder) {
+  Finder->addMatcher(
+      stmt(anyOf(ifStmt(hasThen(nullStmt().bind("semi")),
+                        unless(hasElse(stmt()))),
+                 forStmt(hasBody(nullStmt().bind("semi"))),
+                 cxxForRangeStmt(hasBody(nullStmt().bind("semi"))),
+                 whileStmt(hasBody(nullStmt().bind("semi")))))
+          .bind("stmt"),
+      this);
+}
+
+void SuspiciousSemicolonCheck::check(const MatchFinder::MatchResult &Result) {
+  if (Result.Context->getDiagnostics().hasUncompilableErrorOccurred())
+    return;
+
+  const auto *Semicolon = Result.Nodes.getNodeAs<NullStmt>("semi");
+  SourceLocation LocStart = Semicolon->getLocStart();
+
+  if (LocStart.isMacroID())
+    return;
+
+  ASTContext &Ctxt = *Result.Context;
+  auto Token = utils::lexer::getPreviousToken(Ctxt, LocStart);
+  auto &SM = *Result.SourceManager;
+  unsigned SemicolonLine = SM.getSpellingLineNumber(LocStart);
+
+  const auto *Statement = Result.Nodes.getNodeAs<Stmt>("stmt");
+  const bool IsIfStmt = isa<IfStmt>(Statement);
+
+  if (!IsIfStmt &&
+      SM.getSpellingLineNumber(Token.getLocation()) != SemicolonLine)
+    return;
+
+  SourceLocation LocEnd = Semicolon->getLocEnd();
+  FileID FID = SM.getFileID(LocEnd);
+  llvm::MemoryBuffer *Buffer = SM.getBuffer(FID, LocEnd);
+  Lexer Lexer(SM.getLocForStartOfFile(FID), Ctxt.getLangOpts(),
+              Buffer->getBufferStart(), SM.getCharacterData(LocEnd) + 1,
+              Buffer->getBufferEnd());
+  if (Lexer.LexFromRawLexer(Token))
+    return;
+
+  unsigned BaseIndent = SM.getSpellingColumnNumber(Statement->getLocStart());
+  unsigned NewTokenIndent = SM.getSpellingColumnNumber(Token.getLocation());
+  unsigned NewTokenLine = SM.getSpellingLineNumber(Token.getLocation());
+
+  if (!IsIfStmt && NewTokenIndent <= BaseIndent &&
+      Token.getKind() != tok::l_brace && NewTokenLine != SemicolonLine)
+    return;
+
+  diag(LocStart, "potentially unintended semicolon")
+      << FixItHint::CreateRemoval(SourceRange(LocStart, LocEnd));
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/misc/SuspiciousSemicolonCheck.h b/clang-tidy/misc/SuspiciousSemicolonCheck.h
new file mode 100644 (file)
index 0000000..6791ba6
--- /dev/null
@@ -0,0 +1,36 @@
+//===--- SuspiciousSemicolonCheck.h - clang-tidy-----------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_SUSPICIOUS_SEMICOLON_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_SUSPICIOUS_SEMICOLON_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// This check finds semicolon that modifies the meaning of the program
+/// unintendedly.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/misc-suspicious-semicolon.html
+class SuspiciousSemicolonCheck : public ClangTidyCheck {
+public:
+  SuspiciousSemicolonCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_SUSPICIOUS_SEMICOLON_H
diff --git a/clang-tidy/misc/SuspiciousStringCompareCheck.cpp b/clang-tidy/misc/SuspiciousStringCompareCheck.cpp
new file mode 100644 (file)
index 0000000..641200c
--- /dev/null
@@ -0,0 +1,218 @@
+//===--- SuspiciousStringCompareCheck.cpp - clang-tidy---------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "SuspiciousStringCompareCheck.h"
+#include "../utils/Matchers.h"
+#include "../utils/OptionsUtils.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+// Semicolon separated list of known string compare-like functions. The list
+// must ends with a semicolon.
+static const char KnownStringCompareFunctions[] = "__builtin_memcmp;"
+                                                  "__builtin_strcasecmp;"
+                                                  "__builtin_strcmp;"
+                                                  "__builtin_strncasecmp;"
+                                                  "__builtin_strncmp;"
+                                                  "_mbscmp;"
+                                                  "_mbscmp_l;"
+                                                  "_mbsicmp;"
+                                                  "_mbsicmp_l;"
+                                                  "_mbsnbcmp;"
+                                                  "_mbsnbcmp_l;"
+                                                  "_mbsnbicmp;"
+                                                  "_mbsnbicmp_l;"
+                                                  "_mbsncmp;"
+                                                  "_mbsncmp_l;"
+                                                  "_mbsnicmp;"
+                                                  "_mbsnicmp_l;"
+                                                  "_memicmp;"
+                                                  "_memicmp_l;"
+                                                  "_stricmp;"
+                                                  "_stricmp_l;"
+                                                  "_strnicmp;"
+                                                  "_strnicmp_l;"
+                                                  "_wcsicmp;"
+                                                  "_wcsicmp_l;"
+                                                  "_wcsnicmp;"
+                                                  "_wcsnicmp_l;"
+                                                  "lstrcmp;"
+                                                  "lstrcmpi;"
+                                                  "memcmp;"
+                                                  "memicmp;"
+                                                  "strcasecmp;"
+                                                  "strcmp;"
+                                                  "strcmpi;"
+                                                  "stricmp;"
+                                                  "strncasecmp;"
+                                                  "strncmp;"
+                                                  "strnicmp;"
+                                                  "wcscasecmp;"
+                                                  "wcscmp;"
+                                                  "wcsicmp;"
+                                                  "wcsncmp;"
+                                                  "wcsnicmp;"
+                                                  "wmemcmp;";
+
+SuspiciousStringCompareCheck::SuspiciousStringCompareCheck(
+    StringRef Name, ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      WarnOnImplicitComparison(Options.get("WarnOnImplicitComparison", 1)),
+      WarnOnLogicalNotComparison(Options.get("WarnOnLogicalNotComparison", 0)),
+      StringCompareLikeFunctions(
+          Options.get("StringCompareLikeFunctions", "")) {}
+
+void SuspiciousStringCompareCheck::storeOptions(
+    ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "WarnOnImplicitComparison", WarnOnImplicitComparison);
+  Options.store(Opts, "WarnOnLogicalNotComparison", WarnOnLogicalNotComparison);
+  Options.store(Opts, "StringCompareLikeFunctions", StringCompareLikeFunctions);
+}
+
+void SuspiciousStringCompareCheck::registerMatchers(MatchFinder *Finder) {
+  // Match relational operators.
+  const auto ComparisonUnaryOperator = unaryOperator(hasOperatorName("!"));
+  const auto ComparisonBinaryOperator =
+      binaryOperator(matchers::isComparisonOperator());
+  const auto ComparisonOperator =
+      expr(anyOf(ComparisonUnaryOperator, ComparisonBinaryOperator));
+
+  // Add the list of known string compare-like functions and add user-defined
+  // functions.
+  std::vector<std::string> FunctionNames = utils::options::parseStringList(
+      (llvm::Twine(KnownStringCompareFunctions) + StringCompareLikeFunctions)
+          .str());
+
+  // Match a call to a string compare functions.
+  const auto FunctionCompareDecl =
+      functionDecl(hasAnyName(std::vector<StringRef>(FunctionNames.begin(),
+                                                     FunctionNames.end())))
+          .bind("decl");
+  const auto DirectStringCompareCallExpr =
+      callExpr(hasDeclaration(FunctionCompareDecl)).bind("call");
+  const auto MacroStringCompareCallExpr = conditionalOperator(anyOf(
+      hasTrueExpression(ignoringParenImpCasts(DirectStringCompareCallExpr)),
+      hasFalseExpression(ignoringParenImpCasts(DirectStringCompareCallExpr))));
+  // The implicit cast is not present in C.
+  const auto StringCompareCallExpr = ignoringParenImpCasts(
+      anyOf(DirectStringCompareCallExpr, MacroStringCompareCallExpr));
+
+  if (WarnOnImplicitComparison) {
+    // Detect suspicious calls to string compare:
+    //     'if (strcmp())'  ->  'if (strcmp() != 0)'
+    Finder->addMatcher(
+        stmt(anyOf(ifStmt(hasCondition(StringCompareCallExpr)),
+                   whileStmt(hasCondition(StringCompareCallExpr)),
+                   doStmt(hasCondition(StringCompareCallExpr)),
+                   forStmt(hasCondition(StringCompareCallExpr)),
+                   binaryOperator(
+                       anyOf(hasOperatorName("&&"), hasOperatorName("||")),
+                       hasEitherOperand(StringCompareCallExpr))))
+            .bind("missing-comparison"),
+        this);
+  }
+
+  if (WarnOnLogicalNotComparison) {
+    // Detect suspicious calls to string compared with '!' operator:
+    //     'if (!strcmp())'  ->  'if (strcmp() == 0)'
+    Finder->addMatcher(unaryOperator(hasOperatorName("!"),
+                                     hasUnaryOperand(ignoringParenImpCasts(
+                                         StringCompareCallExpr)))
+                           .bind("logical-not-comparison"),
+                       this);
+  }
+
+  // Detect suspicious cast to an inconsistant type (i.e. not integer type).
+  Finder->addMatcher(
+      implicitCastExpr(unless(hasType(isInteger())),
+                       hasSourceExpression(StringCompareCallExpr))
+          .bind("invalid-conversion"),
+      this);
+
+  // Detect suspicious operator with string compare function as operand.
+  Finder->addMatcher(
+      binaryOperator(
+          unless(anyOf(matchers::isComparisonOperator(), hasOperatorName("&&"),
+                       hasOperatorName("||"), hasOperatorName("="))),
+          hasEitherOperand(StringCompareCallExpr))
+          .bind("suspicious-operator"),
+      this);
+
+  // Detect comparison to invalid constant: 'strcmp() == -1'.
+  const auto InvalidLiteral = ignoringParenImpCasts(
+      anyOf(integerLiteral(unless(equals(0))),
+            unaryOperator(
+                hasOperatorName("-"),
+                has(ignoringParenImpCasts(integerLiteral(unless(equals(0)))))),
+            characterLiteral(), cxxBoolLiteral()));
+
+  Finder->addMatcher(binaryOperator(matchers::isComparisonOperator(),
+                                    hasEitherOperand(StringCompareCallExpr),
+                                    hasEitherOperand(InvalidLiteral))
+                         .bind("invalid-comparison"),
+                     this);
+}
+
+void SuspiciousStringCompareCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  const auto *Decl = Result.Nodes.getNodeAs<FunctionDecl>("decl");
+  const auto *Call = Result.Nodes.getNodeAs<CallExpr>("call");
+  assert(Decl != nullptr && Call != nullptr);
+
+  if (Result.Nodes.getNodeAs<Stmt>("missing-comparison")) {
+    SourceLocation EndLoc = Lexer::getLocForEndOfToken(
+        Call->getRParenLoc(), 0, Result.Context->getSourceManager(),
+        getLangOpts());
+
+    diag(Call->getLocStart(),
+         "function %0 is called without explicitly comparing result")
+        << Decl << FixItHint::CreateInsertion(EndLoc, " != 0");
+  }
+
+  if (const auto *E = Result.Nodes.getNodeAs<Expr>("logical-not-comparison")) {
+    SourceLocation EndLoc = Lexer::getLocForEndOfToken(
+        Call->getRParenLoc(), 0, Result.Context->getSourceManager(),
+        getLangOpts());
+    SourceLocation NotLoc = E->getLocStart();
+
+    diag(Call->getLocStart(),
+         "function %0 is compared using logical not operator")
+        << Decl << FixItHint::CreateRemoval(
+                       CharSourceRange::getTokenRange(NotLoc, NotLoc))
+        << FixItHint::CreateInsertion(EndLoc, " == 0");
+  }
+
+  if (Result.Nodes.getNodeAs<Stmt>("invalid-comparison")) {
+    diag(Call->getLocStart(),
+         "function %0 is compared to a suspicious constant")
+        << Decl;
+  }
+
+  if (const auto *BinOp =
+          Result.Nodes.getNodeAs<BinaryOperator>("suspicious-operator")) {
+    diag(Call->getLocStart(), "results of function %0 used by operator '%1'")
+        << Decl << BinOp->getOpcodeStr();
+  }
+
+  if (Result.Nodes.getNodeAs<Stmt>("invalid-conversion")) {
+    diag(Call->getLocStart(), "function %0 has suspicious implicit cast")
+        << Decl;
+  }
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/misc/SuspiciousStringCompareCheck.h b/clang-tidy/misc/SuspiciousStringCompareCheck.h
new file mode 100644 (file)
index 0000000..0e9bd45
--- /dev/null
@@ -0,0 +1,40 @@
+//===--- SuspiciousStringCompareCheck.h - clang-tidy-------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_SUSPICIOUS_STRING_COMPARE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_SUSPICIOUS_STRING_COMPARE_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Find suspicious calls to string compare functions.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/misc-suspicious-string-compare.html
+class SuspiciousStringCompareCheck : public ClangTidyCheck {
+public:
+  SuspiciousStringCompareCheck(StringRef Name, ClangTidyContext *Context);
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+  const bool WarnOnImplicitComparison;
+  const bool WarnOnLogicalNotComparison;
+  const std::string StringCompareLikeFunctions;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_SUSPICIOUS_STRING_COMPARE_H
diff --git a/clang-tidy/misc/SwappedArgumentsCheck.cpp b/clang-tidy/misc/SwappedArgumentsCheck.cpp
new file mode 100644 (file)
index 0000000..e4dc5ca
--- /dev/null
@@ -0,0 +1,102 @@
+//===--- SwappedArgumentsCheck.cpp - clang-tidy ---------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "SwappedArgumentsCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Tooling/FixIt.h"
+#include "llvm/ADT/SmallPtrSet.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+void SwappedArgumentsCheck::registerMatchers(MatchFinder *Finder) {
+  Finder->addMatcher(callExpr().bind("call"), this);
+}
+
+/// \brief Look through lvalue to rvalue and nop casts. This filters out
+/// implicit conversions that have no effect on the input but block our view for
+/// other implicit casts.
+static const Expr *ignoreNoOpCasts(const Expr *E) {
+  if (auto *Cast = dyn_cast<CastExpr>(E))
+    if (Cast->getCastKind() == CK_LValueToRValue ||
+        Cast->getCastKind() == CK_NoOp)
+      return ignoreNoOpCasts(Cast->getSubExpr());
+  return E;
+}
+
+/// \brief Restrict the warning to implicit casts that are most likely
+/// accidental. User defined or integral conversions fit in this category,
+/// lvalue to rvalue or derived to base does not.
+static bool isImplicitCastCandidate(const CastExpr *Cast) {
+  return Cast->getCastKind() == CK_UserDefinedConversion ||
+         Cast->getCastKind() == CK_FloatingToBoolean ||
+         Cast->getCastKind() == CK_FloatingToIntegral ||
+         Cast->getCastKind() == CK_IntegralToBoolean ||
+         Cast->getCastKind() == CK_IntegralToFloating ||
+         Cast->getCastKind() == CK_MemberPointerToBoolean ||
+         Cast->getCastKind() == CK_PointerToBoolean;
+}
+
+void SwappedArgumentsCheck::check(const MatchFinder::MatchResult &Result) {
+  const ASTContext &Ctx = *Result.Context;
+  const auto *Call = Result.Nodes.getNodeAs<CallExpr>("call");
+
+  llvm::SmallPtrSet<const Expr *, 4> UsedArgs;
+  for (unsigned I = 1, E = Call->getNumArgs(); I < E; ++I) {
+    const Expr *LHS = Call->getArg(I - 1);
+    const Expr *RHS = Call->getArg(I);
+
+    // Only need to check RHS, as LHS has already been covered. We don't want to
+    // emit two warnings for a single argument.
+    if (UsedArgs.count(RHS))
+      continue;
+
+    const auto *LHSCast = dyn_cast<ImplicitCastExpr>(ignoreNoOpCasts(LHS));
+    const auto *RHSCast = dyn_cast<ImplicitCastExpr>(ignoreNoOpCasts(RHS));
+
+    // Look if this is a potentially swapped argument pair. First look for
+    // implicit casts.
+    if (!LHSCast || !RHSCast || !isImplicitCastCandidate(LHSCast) ||
+        !isImplicitCastCandidate(RHSCast))
+      continue;
+
+    // If the types that go into the implicit casts match the types of the other
+    // argument in the declaration there is a high probability that the
+    // arguments were swapped.
+    // TODO: We could make use of the edit distance between the argument name
+    // and the name of the passed variable in addition to this type based
+    // heuristic.
+    const Expr *LHSFrom = ignoreNoOpCasts(LHSCast->getSubExpr());
+    const Expr *RHSFrom = ignoreNoOpCasts(RHSCast->getSubExpr());
+    if (LHS->getType() == RHS->getType() ||
+        LHS->getType() != RHSFrom->getType() ||
+        RHS->getType() != LHSFrom->getType())
+      continue;
+
+    // Emit a warning and fix-its that swap the arguments.
+    diag(Call->getLocStart(), "argument with implicit conversion from %0 "
+                              "to %1 followed by argument converted from "
+                              "%2 to %3, potentially swapped arguments.")
+        << LHS->getType() << LHSFrom->getType() << RHS->getType()
+        << RHSFrom->getType()
+        << tooling::fixit::createReplacement(*LHS, *RHS, Ctx)
+        << tooling::fixit::createReplacement(*RHS, *LHS, Ctx);
+
+    // Remember that we emitted a warning for this argument.
+    UsedArgs.insert(RHSCast);
+  }
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/misc/SwappedArgumentsCheck.h b/clang-tidy/misc/SwappedArgumentsCheck.h
new file mode 100644 (file)
index 0000000..ed32661
--- /dev/null
@@ -0,0 +1,32 @@
+//===--- SwappedArgumentsCheck.h - clang-tidy -------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_SWAPPEDARGUMENTSCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_SWAPPEDARGUMENTSCHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Finds potentially swapped arguments by looking at implicit conversions.
+class SwappedArgumentsCheck : public ClangTidyCheck {
+public:
+  SwappedArgumentsCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_SWAPPEDARGUMENTSCHECK_H
diff --git a/clang-tidy/misc/ThrowByValueCatchByReferenceCheck.cpp b/clang-tidy/misc/ThrowByValueCatchByReferenceCheck.cpp
new file mode 100644 (file)
index 0000000..90398cc
--- /dev/null
@@ -0,0 +1,161 @@
+//===--- ThrowByValueCatchByReferenceCheck.cpp - clang-tidy----------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ThrowByValueCatchByReferenceCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/OperationKinds.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+ThrowByValueCatchByReferenceCheck::ThrowByValueCatchByReferenceCheck(
+    StringRef Name, ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      CheckAnonymousTemporaries(Options.get("CheckThrowTemporaries", true)) {}
+
+void ThrowByValueCatchByReferenceCheck::registerMatchers(MatchFinder *Finder) {
+  // This is a C++ only check thus we register the matchers only for C++
+  if (!getLangOpts().CPlusPlus)
+    return;
+
+  Finder->addMatcher(cxxThrowExpr().bind("throw"), this);
+  Finder->addMatcher(cxxCatchStmt().bind("catch"), this);
+}
+
+void ThrowByValueCatchByReferenceCheck::storeOptions(
+    ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "CheckThrowTemporaries", true);
+}
+
+void ThrowByValueCatchByReferenceCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  diagnoseThrowLocations(Result.Nodes.getNodeAs<CXXThrowExpr>("throw"));
+  diagnoseCatchLocations(Result.Nodes.getNodeAs<CXXCatchStmt>("catch"),
+                         *Result.Context);
+}
+
+bool ThrowByValueCatchByReferenceCheck::isFunctionParameter(
+    const DeclRefExpr *declRefExpr) {
+  return isa<ParmVarDecl>(declRefExpr->getDecl());
+}
+
+bool ThrowByValueCatchByReferenceCheck::isCatchVariable(
+    const DeclRefExpr *declRefExpr) {
+  auto *valueDecl = declRefExpr->getDecl();
+  if (auto *varDecl = dyn_cast<VarDecl>(valueDecl))
+    return varDecl->isExceptionVariable();
+  return false;
+}
+
+bool ThrowByValueCatchByReferenceCheck::isFunctionOrCatchVar(
+    const DeclRefExpr *declRefExpr) {
+  return isFunctionParameter(declRefExpr) || isCatchVariable(declRefExpr);
+}
+
+void ThrowByValueCatchByReferenceCheck::diagnoseThrowLocations(
+    const CXXThrowExpr *throwExpr) {
+  if (!throwExpr)
+    return;
+  auto *subExpr = throwExpr->getSubExpr();
+  if (!subExpr)
+    return;
+  auto qualType = subExpr->getType();
+  if (qualType->isPointerType()) {
+    // The code is throwing a pointer.
+    // In case it is strng literal, it is safe and we return.
+    auto *inner = subExpr->IgnoreParenImpCasts();
+    if (isa<StringLiteral>(inner))
+      return;
+    // If it's a variable from a catch statement, we return as well.
+    auto *declRef = dyn_cast<DeclRefExpr>(inner);
+    if (declRef && isCatchVariable(declRef)) {
+      return;
+    }
+    diag(subExpr->getLocStart(), "throw expression throws a pointer; it should "
+                                 "throw a non-pointer value instead");
+  }
+  // If the throw statement does not throw by pointer then it throws by value
+  // which is ok.
+  // There are addition checks that emit diagnosis messages if the thrown value
+  // is not an RValue. See:
+  // https://www.securecoding.cert.org/confluence/display/cplusplus/ERR09-CPP.+Throw+anonymous+temporaries
+  // This behavior can be influenced by an option.
+
+  // If we encounter a CXXThrowExpr, we move through all casts until you either
+  // encounter a DeclRefExpr or a CXXConstructExpr.
+  // If it's a DeclRefExpr, we emit a message if the referenced variable is not
+  // a catch variable or function parameter.
+  // When encountering a CopyOrMoveConstructor: emit message if after casts,
+  // the expression is a LValue
+  if (CheckAnonymousTemporaries) {
+    bool emit = false;
+    auto *currentSubExpr = subExpr->IgnoreImpCasts();
+    const auto *variableReference = dyn_cast<DeclRefExpr>(currentSubExpr);
+    const auto *constructorCall = dyn_cast<CXXConstructExpr>(currentSubExpr);
+    // If we have a DeclRefExpr, we flag for emitting a diagnosis message in
+    // case the referenced variable is neither a function parameter nor a
+    // variable declared in the catch statement.
+    if (variableReference)
+      emit = !isFunctionOrCatchVar(variableReference);
+    else if (constructorCall &&
+             constructorCall->getConstructor()->isCopyOrMoveConstructor()) {
+      // If we have a copy / move construction, we emit a diagnosis message if
+      // the object that we copy construct from is neither a function parameter
+      // nor a variable declared in a catch statement
+      auto argIter =
+          constructorCall
+              ->arg_begin(); // there's only one for copy constructors
+      auto *currentSubExpr = (*argIter)->IgnoreImpCasts();
+      if (currentSubExpr->isLValue()) {
+        if (auto *tmp = dyn_cast<DeclRefExpr>(currentSubExpr))
+          emit = !isFunctionOrCatchVar(tmp);
+        else if (isa<CallExpr>(currentSubExpr))
+          emit = true;
+      }
+    }
+    if (emit)
+      diag(subExpr->getLocStart(),
+           "throw expression should throw anonymous temporary values instead");
+  }
+}
+
+void ThrowByValueCatchByReferenceCheck::diagnoseCatchLocations(
+    const CXXCatchStmt *catchStmt, ASTContext &context) {
+  if (!catchStmt)
+    return;
+  auto caughtType = catchStmt->getCaughtType();
+  if (caughtType.isNull())
+    return;
+  auto *varDecl = catchStmt->getExceptionDecl();
+  if (const auto *PT = caughtType.getCanonicalType()->getAs<PointerType>()) {
+    const char *diagMsgCatchReference = "catch handler catches a pointer value; "
+                                        "should throw a non-pointer value and "
+                                        "catch by reference instead";
+    // We do not diagnose when catching pointer to strings since we also allow
+    // throwing string literals.
+    if (!PT->getPointeeType()->isAnyCharacterType())
+      diag(varDecl->getLocStart(), diagMsgCatchReference);
+  } else if (!caughtType->isReferenceType()) {
+    const char *diagMsgCatchReference = "catch handler catches by value; "
+                                        "should catch by reference instead";
+    // If it's not a pointer and not a reference then it must be caught "by
+    // value". In this case we should emit a diagnosis message unless the type
+    // is trivial.
+    if (!caughtType.isTrivialType(context))
+      diag(varDecl->getLocStart(), diagMsgCatchReference);
+  }
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/misc/ThrowByValueCatchByReferenceCheck.h b/clang-tidy/misc/ThrowByValueCatchByReferenceCheck.h
new file mode 100644 (file)
index 0000000..a2e7df7
--- /dev/null
@@ -0,0 +1,51 @@
+//===--- ThrowByValueCatchByReferenceCheck.h - clang-tidy--------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_THROW_BY_VALUE_CATCH_BY_REFERENCE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_THROW_BY_VALUE_CATCH_BY_REFERENCE_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+///\brief checks for locations that do not throw by value
+// or catch by reference.
+// The check is C++ only. It checks that all throw locations
+// throw by value and not by pointer. Additionally it
+// contains an option ("CheckThrowTemporaries", default value "true") that
+// checks that thrown objects are anonymous temporaries. It is also
+// acceptable for this check to throw string literals.
+// This test checks that exceptions are caught by reference
+// and not by value or pointer. It will not warn when catching
+// pointer to char, wchar_t, char16_t or char32_t. This is
+// due to not warning on throwing string literals.
+class ThrowByValueCatchByReferenceCheck : public ClangTidyCheck {
+public:
+  ThrowByValueCatchByReferenceCheck(StringRef Name, ClangTidyContext *Context);
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+  void diagnoseThrowLocations(const CXXThrowExpr *throwExpr);
+  void diagnoseCatchLocations(const CXXCatchStmt *catchStmt,
+                              ASTContext &context);
+  bool isFunctionParameter(const DeclRefExpr *declRefExpr);
+  bool isCatchVariable(const DeclRefExpr *declRefExpr);
+  bool isFunctionOrCatchVar(const DeclRefExpr *declRefExpr);
+  const bool CheckAnonymousTemporaries;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_THROW_BY_VALUE_CATCH_BY_REFERENCE_H
diff --git a/clang-tidy/misc/UnconventionalAssignOperatorCheck.cpp b/clang-tidy/misc/UnconventionalAssignOperatorCheck.cpp
new file mode 100644 (file)
index 0000000..cde631e
--- /dev/null
@@ -0,0 +1,94 @@
+//===--- UnconventionalAssignOperatorCheck.cpp - clang-tidy -----*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "UnconventionalAssignOperatorCheck.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+void UnconventionalAssignOperatorCheck::registerMatchers(
+    ast_matchers::MatchFinder *Finder) {
+  // Only register the matchers for C++; the functionality currently does not
+  // provide any benefit to other languages, despite being benign.
+  if (!getLangOpts().CPlusPlus)
+    return;
+
+  const auto HasGoodReturnType = cxxMethodDecl(returns(
+      lValueReferenceType(pointee(unless(isConstQualified()),
+                                  hasDeclaration(equalsBoundNode("class"))))));
+
+  const auto IsSelf = qualType(
+      anyOf(hasDeclaration(equalsBoundNode("class")),
+            referenceType(pointee(hasDeclaration(equalsBoundNode("class"))))));
+  const auto IsAssign =
+      cxxMethodDecl(unless(anyOf(isDeleted(), isPrivate(), isImplicit())),
+                    hasName("operator="), ofClass(recordDecl().bind("class")))
+          .bind("method");
+  const auto IsSelfAssign =
+      cxxMethodDecl(IsAssign, hasParameter(0, parmVarDecl(hasType(IsSelf))))
+          .bind("method");
+
+  Finder->addMatcher(
+      cxxMethodDecl(IsAssign, unless(HasGoodReturnType)).bind("ReturnType"),
+      this);
+
+  const auto BadSelf = referenceType(
+      anyOf(lValueReferenceType(pointee(unless(isConstQualified()))),
+            rValueReferenceType(pointee(isConstQualified()))));
+
+  Finder->addMatcher(
+      cxxMethodDecl(IsSelfAssign,
+                    hasParameter(0, parmVarDecl(hasType(BadSelf))))
+          .bind("ArgumentType"),
+      this);
+
+  Finder->addMatcher(
+      cxxMethodDecl(IsSelfAssign, anyOf(isConst(), isVirtual())).bind("cv"),
+      this);
+
+  const auto IsBadReturnStatement = returnStmt(unless(has(ignoringParenImpCasts(
+      anyOf(unaryOperator(hasOperatorName("*"), hasUnaryOperand(cxxThisExpr())),
+            cxxOperatorCallExpr(argumentCountIs(1),
+                                callee(unresolvedLookupExpr()),
+                                hasArgument(0, cxxThisExpr())))))));
+  const auto IsGoodAssign = cxxMethodDecl(IsAssign, HasGoodReturnType);
+
+  Finder->addMatcher(returnStmt(IsBadReturnStatement, forFunction(IsGoodAssign))
+                         .bind("returnStmt"),
+                     this);
+}
+
+void UnconventionalAssignOperatorCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  if (const auto *RetStmt = Result.Nodes.getNodeAs<ReturnStmt>("returnStmt")) {
+    diag(RetStmt->getLocStart(), "operator=() should always return '*this'");
+  } else {
+    static const char *const Messages[][2] = {
+        {"ReturnType", "operator=() should return '%0&'"},
+        {"ArgumentType", "operator=() should take '%0 const&', '%0&&' or '%0'"},
+        {"cv", "operator=() should not be marked '%1'"}};
+
+    const auto *Method = Result.Nodes.getNodeAs<CXXMethodDecl>("method");
+    for (const auto &Message : Messages) {
+      if (Result.Nodes.getNodeAs<Decl>(Message[0]))
+        diag(Method->getLocStart(), Message[1])
+            << Method->getParent()->getName()
+            << (Method->isConst() ? "const" : "virtual");
+    }
+  }
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/misc/UnconventionalAssignOperatorCheck.h b/clang-tidy/misc/UnconventionalAssignOperatorCheck.h
new file mode 100644 (file)
index 0000000..ee91dca
--- /dev/null
@@ -0,0 +1,42 @@
+//===--- UnconventionalAssignOperatorCheck.h - clang-tidy -------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_ASSIGNOPERATORSIGNATURECHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_ASSIGNOPERATORSIGNATURECHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Finds declarations of assignment operators with the wrong return and/or
+/// argument types and definitions with good return type but wrong return
+/// statements.
+///
+///   * The return type must be `Class&`.
+///   * Works with move-assign and assign by value.
+///   * Private and deleted operators are ignored.
+///   * The operator must always return ``*this``.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/misc-unconventional-assign-operator.html
+class UnconventionalAssignOperatorCheck : public ClangTidyCheck {
+public:
+  UnconventionalAssignOperatorCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_ASSIGNOPERATORSIGNATURECHECK_H
diff --git a/clang-tidy/misc/UndelegatedConstructor.cpp b/clang-tidy/misc/UndelegatedConstructor.cpp
new file mode 100644 (file)
index 0000000..f42f1c5
--- /dev/null
@@ -0,0 +1,84 @@
+//===--- UndelegatedConstructor.cpp - clang-tidy --------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "UndelegatedConstructor.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+namespace {
+AST_MATCHER_P(Stmt, ignoringTemporaryExpr,
+              ast_matchers::internal::Matcher<Stmt>, InnerMatcher) {
+  const Stmt *E = &Node;
+  for (;;) {
+    // Temporaries with non-trivial dtors.
+    if (const auto *EWC = dyn_cast<ExprWithCleanups>(E))
+      E = EWC->getSubExpr();
+    // Temporaries with zero or more than two ctor arguments.
+    else if (const auto *BTE = dyn_cast<CXXBindTemporaryExpr>(E))
+      E = BTE->getSubExpr();
+    // Temporaries with exactly one ctor argument.
+    else if (const auto *FCE = dyn_cast<CXXFunctionalCastExpr>(E))
+      E = FCE->getSubExpr();
+    else
+      break;
+  }
+
+  return InnerMatcher.matches(*E, Finder, Builder);
+}
+
+// Finds a node if it's a base of an already bound node.
+AST_MATCHER_P(CXXRecordDecl, baseOfBoundNode, std::string, ID) {
+  return Builder->removeBindings(
+      [&](const ast_matchers::internal::BoundNodesMap &Nodes) {
+        const auto *Derived = Nodes.getNodeAs<CXXRecordDecl>(ID);
+        return Derived != &Node && !Derived->isDerivedFrom(&Node);
+      });
+}
+} // namespace
+
+void UndelegatedConstructorCheck::registerMatchers(MatchFinder *Finder) {
+  // We look for calls to constructors of the same type in constructors. To do
+  // this we have to look through a variety of nodes that occur in the path,
+  // depending on the type's destructor and the number of arguments on the
+  // constructor call, this is handled by ignoringTemporaryExpr. Ignore template
+  // instantiations to reduce the number of duplicated warnings.
+  //
+  // Only register the matchers for C++11; the functionality currently does not
+  // provide any benefit to other languages, despite being benign.
+  if (!getLangOpts().CPlusPlus11)
+    return;
+
+  Finder->addMatcher(
+      compoundStmt(
+          hasParent(
+              cxxConstructorDecl(ofClass(cxxRecordDecl().bind("parent")))),
+          forEach(ignoringTemporaryExpr(
+              cxxConstructExpr(hasDeclaration(cxxConstructorDecl(ofClass(
+                                   cxxRecordDecl(baseOfBoundNode("parent"))))))
+                  .bind("construct"))),
+          unless(isInTemplateInstantiation())),
+      this);
+}
+
+void UndelegatedConstructorCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  const auto *E = Result.Nodes.getNodeAs<CXXConstructExpr>("construct");
+  diag(E->getLocStart(), "did you intend to call a delegated constructor? "
+                         "A temporary object is created here instead");
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/misc/UndelegatedConstructor.h b/clang-tidy/misc/UndelegatedConstructor.h
new file mode 100644 (file)
index 0000000..bba36ed
--- /dev/null
@@ -0,0 +1,36 @@
+//===--- UndelegatedConstructor.h - clang-tidy ------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_UNDELEGATEDCONSTRUCTOR_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_UNDELEGATEDCONSTRUCTOR_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Finds creation of temporary objects in constructors that look like a
+/// function call to another constructor of the same class.
+///
+/// The user most likely meant to use a delegating constructor or base class
+/// initializer.
+class UndelegatedConstructorCheck : public ClangTidyCheck {
+public:
+  UndelegatedConstructorCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_UNDELEGATEDCONSTRUCTOR_H
diff --git a/clang-tidy/misc/UniqueptrResetReleaseCheck.cpp b/clang-tidy/misc/UniqueptrResetReleaseCheck.cpp
new file mode 100644 (file)
index 0000000..99758d3
--- /dev/null
@@ -0,0 +1,136 @@
+//===--- UniqueptrResetReleaseCheck.cpp - clang-tidy ----------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "UniqueptrResetReleaseCheck.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+void UniqueptrResetReleaseCheck::registerMatchers(MatchFinder *Finder) {
+  // Only register the matchers for C++11; the functionality currently does not
+  // provide any benefit to other languages, despite being benign.
+  if (!getLangOpts().CPlusPlus11)
+    return;
+
+  Finder->addMatcher(
+      cxxMemberCallExpr(
+          on(expr().bind("left")), callee(memberExpr().bind("reset_member")),
+          callee(
+              cxxMethodDecl(hasName("reset"),
+                            ofClass(cxxRecordDecl(hasName("::std::unique_ptr"),
+                                                  decl().bind("left_class"))))),
+          has(ignoringParenImpCasts(cxxMemberCallExpr(
+              on(expr().bind("right")),
+              callee(memberExpr().bind("release_member")),
+              callee(cxxMethodDecl(
+                  hasName("release"),
+                  ofClass(cxxRecordDecl(hasName("::std::unique_ptr"),
+                                        decl().bind("right_class")))))))))
+          .bind("reset_call"),
+      this);
+}
+
+namespace {
+const Type *getDeleterForUniquePtr(const MatchFinder::MatchResult &Result,
+                                   StringRef ID) {
+  const auto *Class =
+      Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>(ID);
+  if (!Class)
+    return nullptr;
+  auto DeleterArgument = Class->getTemplateArgs()[1];
+  if (DeleterArgument.getKind() != TemplateArgument::Type)
+    return nullptr;
+  return DeleterArgument.getAsType().getTypePtr();
+}
+
+bool areDeletersCompatible(const MatchFinder::MatchResult &Result) {
+  const Type *LeftDeleterType = getDeleterForUniquePtr(Result, "left_class");
+  const Type *RightDeleterType = getDeleterForUniquePtr(Result, "right_class");
+
+  if (LeftDeleterType->getUnqualifiedDesugaredType() ==
+      RightDeleterType->getUnqualifiedDesugaredType()) {
+    // Same type. We assume they are compatible.
+    // This check handles the case where the deleters are function pointers.
+    return true;
+  }
+
+  const CXXRecordDecl *LeftDeleter = LeftDeleterType->getAsCXXRecordDecl();
+  const CXXRecordDecl *RightDeleter = RightDeleterType->getAsCXXRecordDecl();
+  if (!LeftDeleter || !RightDeleter)
+    return false;
+
+  if (LeftDeleter->getCanonicalDecl() == RightDeleter->getCanonicalDecl()) {
+    // Same class. We assume they are compatible.
+    return true;
+  }
+
+  const auto *LeftAsTemplate =
+      dyn_cast<ClassTemplateSpecializationDecl>(LeftDeleter);
+  const auto *RightAsTemplate =
+      dyn_cast<ClassTemplateSpecializationDecl>(RightDeleter);
+  if (LeftAsTemplate && RightAsTemplate &&
+      LeftAsTemplate->getSpecializedTemplate() ==
+          RightAsTemplate->getSpecializedTemplate()) {
+    // They are different instantiations of the same template. We assume they
+    // are compatible.
+    // This handles things like std::default_delete<Base> vs.
+    // std::default_delete<Derived>.
+    return true;
+  }
+  return false;
+}
+
+} // namespace
+
+void UniqueptrResetReleaseCheck::check(const MatchFinder::MatchResult &Result) {
+  if (!areDeletersCompatible(Result))
+    return;
+
+  const auto *ResetMember = Result.Nodes.getNodeAs<MemberExpr>("reset_member");
+  const auto *ReleaseMember =
+      Result.Nodes.getNodeAs<MemberExpr>("release_member");
+  const auto *Right = Result.Nodes.getNodeAs<Expr>("right");
+  const auto *Left = Result.Nodes.getNodeAs<Expr>("left");
+  const auto *ResetCall =
+      Result.Nodes.getNodeAs<CXXMemberCallExpr>("reset_call");
+
+  std::string LeftText = clang::Lexer::getSourceText(
+      CharSourceRange::getTokenRange(Left->getSourceRange()),
+      *Result.SourceManager, getLangOpts());
+  std::string RightText = clang::Lexer::getSourceText(
+      CharSourceRange::getTokenRange(Right->getSourceRange()),
+      *Result.SourceManager, getLangOpts());
+
+  if (ResetMember->isArrow())
+    LeftText = "*" + LeftText;
+  if (ReleaseMember->isArrow())
+    RightText = "*" + RightText;
+  std::string DiagText;
+  // Even if x was rvalue, *x is not rvalue anymore.
+  if (!Right->isRValue() || ReleaseMember->isArrow()) {
+    RightText = "std::move(" + RightText + ")";
+    DiagText = "prefer ptr1 = std::move(ptr2) over ptr1.reset(ptr2.release())";
+  } else {
+    DiagText =
+        "prefer ptr = ReturnUnique() over ptr.reset(ReturnUnique().release())";
+  }
+  std::string NewText = LeftText + " = " + RightText;
+
+  diag(ResetMember->getExprLoc(), DiagText) << FixItHint::CreateReplacement(
+      CharSourceRange::getTokenRange(ResetCall->getSourceRange()), NewText);
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/misc/UniqueptrResetReleaseCheck.h b/clang-tidy/misc/UniqueptrResetReleaseCheck.h
new file mode 100644 (file)
index 0000000..cf18a5a
--- /dev/null
@@ -0,0 +1,43 @@
+//===--- UniqueptrResetReleaseCheck.h - clang-tidy --------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_UNIQUEPTRRESETRELEASECHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_UNIQUEPTRRESETRELEASECHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Find and replace `unique_ptr::reset(release())` with `std::move()`.
+///
+/// Example:
+///
+/// \code
+///   std::unique_ptr<Foo> x, y;
+///   x.reset(y.release()); -> x = std::move(y);
+/// \endcode
+///
+/// If `y` is already rvalue, `std::move()` is not added.  `x` and `y` can also
+/// be `std::unique_ptr<Foo>*`.
+class UniqueptrResetReleaseCheck : public ClangTidyCheck {
+public:
+  UniqueptrResetReleaseCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_UNIQUEPTRRESETRELEASECHECK_H
diff --git a/clang-tidy/misc/UnusedAliasDeclsCheck.cpp b/clang-tidy/misc/UnusedAliasDeclsCheck.cpp
new file mode 100644 (file)
index 0000000..07165d6
--- /dev/null
@@ -0,0 +1,64 @@
+//===--- UnusedAliasDeclsCheck.cpp - clang-tidy----------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "UnusedAliasDeclsCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+void UnusedAliasDeclsCheck::registerMatchers(MatchFinder *Finder) {
+  // Only register the matchers for C++11; the functionality currently does not
+  // provide any benefit to other languages, despite being benign.
+  if (!getLangOpts().CPlusPlus11)
+    return;
+
+  // We cannot do anything about headers (yet), as the alias declarations
+  // used in one header could be used by some other translation unit.
+  Finder->addMatcher(namespaceAliasDecl(isExpansionInMainFile()).bind("alias"),
+                     this);
+  Finder->addMatcher(nestedNameSpecifier().bind("nns"), this);
+}
+
+void UnusedAliasDeclsCheck::check(const MatchFinder::MatchResult &Result) {
+  if (const auto *AliasDecl = Result.Nodes.getNodeAs<NamedDecl>("alias")) {
+    FoundDecls[AliasDecl] = CharSourceRange::getCharRange(
+        AliasDecl->getLocStart(),
+        Lexer::findLocationAfterToken(
+            AliasDecl->getLocEnd(), tok::semi, *Result.SourceManager,
+            getLangOpts(),
+            /*SkipTrailingWhitespaceAndNewLine=*/true));
+    return;
+  }
+
+  if (const auto *NestedName =
+          Result.Nodes.getNodeAs<NestedNameSpecifier>("nns")) {
+    if (const auto *AliasDecl = NestedName->getAsNamespaceAlias()) {
+      FoundDecls[AliasDecl] = CharSourceRange();
+    }
+  }
+}
+
+void UnusedAliasDeclsCheck::onEndOfTranslationUnit() {
+  for (const auto &FoundDecl : FoundDecls) {
+    if (!FoundDecl.second.isValid())
+      continue;
+    diag(FoundDecl.first->getLocation(), "namespace alias decl %0 is unused")
+        << FoundDecl.first << FixItHint::CreateRemoval(FoundDecl.second);
+  }
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/misc/UnusedAliasDeclsCheck.h b/clang-tidy/misc/UnusedAliasDeclsCheck.h
new file mode 100644 (file)
index 0000000..8cce375
--- /dev/null
@@ -0,0 +1,37 @@
+//===--- UnusedAliasDeclsCheck.h - clang-tidy--------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_UNUSED_ALIAS_DECLS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_UNUSED_ALIAS_DECLS_H
+
+#include "../ClangTidy.h"
+#include "llvm/ADT/DenseMap.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Finds unused namespace alias declarations.
+class UnusedAliasDeclsCheck : public ClangTidyCheck {
+public:
+  UnusedAliasDeclsCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+  void onEndOfTranslationUnit() override;
+
+private:
+  llvm::DenseMap<const NamedDecl *, CharSourceRange> FoundDecls;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_UNUSED_ALIAS_DECLS_H
diff --git a/clang-tidy/misc/UnusedParametersCheck.cpp b/clang-tidy/misc/UnusedParametersCheck.cpp
new file mode 100644 (file)
index 0000000..07e82f0
--- /dev/null
@@ -0,0 +1,178 @@
+//===--- UnusedParametersCheck.cpp - clang-tidy----------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "UnusedParametersCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+#include <unordered_set>
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+namespace {
+bool isOverrideMethod(const FunctionDecl *Function) {
+  if (const auto *MD = dyn_cast<CXXMethodDecl>(Function))
+    return MD->size_overridden_methods() > 0 || MD->hasAttr<OverrideAttr>();
+  return false;
+}
+} // namespace
+
+void UnusedParametersCheck::registerMatchers(MatchFinder *Finder) {
+  Finder->addMatcher(
+      functionDecl(isDefinition(), hasBody(stmt()), hasAnyParameter(decl()))
+          .bind("function"),
+      this);
+}
+
+template <typename T>
+static CharSourceRange removeNode(const MatchFinder::MatchResult &Result,
+                                  const T *PrevNode, const T *Node,
+                                  const T *NextNode) {
+  if (NextNode)
+    return CharSourceRange::getCharRange(Node->getLocStart(),
+                                         NextNode->getLocStart());
+
+  if (PrevNode)
+    return CharSourceRange::getTokenRange(
+        Lexer::getLocForEndOfToken(PrevNode->getLocEnd(), 0,
+                                   *Result.SourceManager,
+                                   Result.Context->getLangOpts()),
+        Node->getLocEnd());
+
+  return CharSourceRange::getTokenRange(Node->getSourceRange());
+}
+
+static FixItHint removeParameter(const MatchFinder::MatchResult &Result,
+                                 const FunctionDecl *Function, unsigned Index) {
+  return FixItHint::CreateRemoval(removeNode(
+      Result, Index > 0 ? Function->getParamDecl(Index - 1) : nullptr,
+      Function->getParamDecl(Index),
+      Index + 1 < Function->getNumParams() ? Function->getParamDecl(Index + 1)
+                                           : nullptr));
+}
+
+static FixItHint removeArgument(const MatchFinder::MatchResult &Result,
+                                const CallExpr *Call, unsigned Index) {
+  return FixItHint::CreateRemoval(removeNode(
+      Result, Index > 0 ? Call->getArg(Index - 1) : nullptr,
+      Call->getArg(Index),
+      Index + 1 < Call->getNumArgs() ? Call->getArg(Index + 1) : nullptr));
+}
+
+class UnusedParametersCheck::IndexerVisitor
+    : public RecursiveASTVisitor<IndexerVisitor> {
+public:
+  IndexerVisitor(TranslationUnitDecl *Top) { TraverseDecl(Top); }
+
+  const std::unordered_set<const CallExpr *> &
+  getFnCalls(const FunctionDecl *Fn) {
+    return Index[Fn->getCanonicalDecl()].Calls;
+  }
+
+  const std::unordered_set<const DeclRefExpr *> &
+  getOtherRefs(const FunctionDecl *Fn) {
+    return Index[Fn->getCanonicalDecl()].OtherRefs;
+  }
+
+  bool shouldTraversePostOrder() const { return true; }
+
+  bool WalkUpFromDeclRefExpr(DeclRefExpr *DeclRef) {
+    if (const auto *Fn = dyn_cast<FunctionDecl>(DeclRef->getDecl())) {
+      Fn = Fn->getCanonicalDecl();
+      Index[Fn].OtherRefs.insert(DeclRef);
+    }
+    return true;
+  }
+
+  bool WalkUpFromCallExpr(CallExpr *Call) {
+    if (const auto *Fn =
+            dyn_cast_or_null<FunctionDecl>(Call->getCalleeDecl())) {
+      Fn = Fn->getCanonicalDecl();
+      if (const auto *Ref =
+              dyn_cast<DeclRefExpr>(Call->getCallee()->IgnoreImplicit())) {
+        Index[Fn].OtherRefs.erase(Ref);
+      }
+      Index[Fn].Calls.insert(Call);
+    }
+    return true;
+  }
+
+private:
+  struct IndexEntry {
+    std::unordered_set<const CallExpr *> Calls;
+    std::unordered_set<const DeclRefExpr *> OtherRefs;
+  };
+
+  std::unordered_map<const FunctionDecl *, IndexEntry> Index;
+};
+
+UnusedParametersCheck::~UnusedParametersCheck() = default;
+
+UnusedParametersCheck::UnusedParametersCheck(StringRef Name,
+                                             ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context) {}
+
+void UnusedParametersCheck::warnOnUnusedParameter(
+    const MatchFinder::MatchResult &Result, const FunctionDecl *Function,
+    unsigned ParamIndex) {
+  const auto *Param = Function->getParamDecl(ParamIndex);
+  auto MyDiag = diag(Param->getLocation(), "parameter %0 is unused") << Param;
+
+  if (!Indexer) {
+    Indexer = llvm::make_unique<IndexerVisitor>(
+        Result.Context->getTranslationUnitDecl());
+  }
+
+  // Comment out parameter name for non-local functions.
+  if (Function->isExternallyVisible() ||
+      !Result.SourceManager->isInMainFile(Function->getLocation()) ||
+      !Indexer->getOtherRefs(Function).empty() || isOverrideMethod(Function)) {
+    SourceRange RemovalRange(Param->getLocation(), Param->getLocEnd());
+    // Note: We always add a space before the '/*' to not accidentally create a
+    // '*/*' for pointer types, which doesn't start a comment. clang-format will
+    // clean this up afterwards.
+    MyDiag << FixItHint::CreateReplacement(
+        RemovalRange, (Twine(" /*") + Param->getName() + "*/").str());
+    return;
+  }
+
+  // Fix all redeclarations.
+  for (const FunctionDecl *FD : Function->redecls())
+    if (FD->param_size())
+      MyDiag << removeParameter(Result, FD, ParamIndex);
+
+  // Fix all call sites.
+  for (const auto *Call : Indexer->getFnCalls(Function))
+    MyDiag << removeArgument(Result, Call, ParamIndex);
+}
+
+void UnusedParametersCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *Function = Result.Nodes.getNodeAs<FunctionDecl>("function");
+  if (!Function->hasWrittenPrototype() || Function->isTemplateInstantiation())
+    return;
+  if (const auto *Method = dyn_cast<CXXMethodDecl>(Function))
+    if (Method->isLambdaStaticInvoker())
+      return;
+  for (unsigned i = 0, e = Function->getNumParams(); i != e; ++i) {
+    const auto *Param = Function->getParamDecl(i);
+    if (Param->isUsed() || Param->isReferenced() || !Param->getDeclName() ||
+        Param->hasAttr<UnusedAttr>())
+      continue;
+    warnOnUnusedParameter(Result, Function, i);
+  }
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/misc/UnusedParametersCheck.h b/clang-tidy/misc/UnusedParametersCheck.h
new file mode 100644 (file)
index 0000000..414ef11
--- /dev/null
@@ -0,0 +1,41 @@
+//===--- UnusedParametersCheck.h - clang-tidy--------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_UNUSED_PARAMETERS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_UNUSED_PARAMETERS_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Finds unused parameters and fixes them, so that `-Wunused-parameter` can be
+/// turned on.
+class UnusedParametersCheck : public ClangTidyCheck {
+public:
+  UnusedParametersCheck(StringRef Name, ClangTidyContext *Context);
+  ~UnusedParametersCheck();
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+  class IndexerVisitor;
+  std::unique_ptr<IndexerVisitor> Indexer;
+
+  void
+  warnOnUnusedParameter(const ast_matchers::MatchFinder::MatchResult &Result,
+                        const FunctionDecl *Function, unsigned ParamIndex);
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_UNUSED_PARAMETERS_H
diff --git a/clang-tidy/misc/UnusedRAIICheck.cpp b/clang-tidy/misc/UnusedRAIICheck.cpp
new file mode 100644 (file)
index 0000000..e1acfe9
--- /dev/null
@@ -0,0 +1,94 @@
+//===--- UnusedRAIICheck.cpp - clang-tidy ---------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "UnusedRAIICheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+namespace {
+AST_MATCHER(CXXRecordDecl, hasNonTrivialDestructor) {
+  // TODO: If the dtor is there but empty we don't want to warn either.
+  return Node.hasDefinition() && Node.hasNonTrivialDestructor();
+}
+} // namespace
+
+void UnusedRAIICheck::registerMatchers(MatchFinder *Finder) {
+  // Only register the matchers for C++; the functionality currently does not
+  // provide any benefit to other languages, despite being benign.
+  if (!getLangOpts().CPlusPlus)
+    return;
+
+  // Look for temporaries that are constructed in-place and immediately
+  // destroyed. Look for temporaries created by a functional cast but not for
+  // those returned from a call.
+  auto BindTemp =
+      cxxBindTemporaryExpr(unless(has(ignoringParenImpCasts(callExpr()))))
+          .bind("temp");
+  Finder->addMatcher(
+      exprWithCleanups(unless(isInTemplateInstantiation()),
+                       hasParent(compoundStmt().bind("compound")),
+                       hasType(cxxRecordDecl(hasNonTrivialDestructor())),
+                       anyOf(has(ignoringParenImpCasts(BindTemp)),
+                             has(ignoringParenImpCasts(cxxFunctionalCastExpr(
+                                 has(ignoringParenImpCasts(BindTemp)))))))
+          .bind("expr"),
+      this);
+}
+
+void UnusedRAIICheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *E = Result.Nodes.getNodeAs<Expr>("expr");
+
+  // We ignore code expanded from macros to reduce the number of false
+  // positives.
+  if (E->getLocStart().isMacroID())
+    return;
+
+  // Don't emit a warning for the last statement in the surrounding compund
+  // statement.
+  const auto *CS = Result.Nodes.getNodeAs<CompoundStmt>("compound");
+  if (E == CS->body_back())
+    return;
+
+  // Emit a warning.
+  auto D = diag(E->getLocStart(), "object destroyed immediately after "
+                                  "creation; did you mean to name the object?");
+  const char *Replacement = " give_me_a_name";
+
+  // If this is a default ctor we have to remove the parens or we'll introduce a
+  // most vexing parse.
+  const auto *BTE = Result.Nodes.getNodeAs<CXXBindTemporaryExpr>("temp");
+  if (const auto *TOE = dyn_cast<CXXTemporaryObjectExpr>(BTE->getSubExpr()))
+    if (TOE->getNumArgs() == 0) {
+      D << FixItHint::CreateReplacement(
+          CharSourceRange::getTokenRange(TOE->getParenOrBraceRange()),
+          Replacement);
+      return;
+    }
+
+  // Otherwise just suggest adding a name. To find the place to insert the name
+  // find the first TypeLoc in the children of E, which always points to the
+  // written type.
+  auto Matches =
+      match(expr(hasDescendant(typeLoc().bind("t"))), *E, *Result.Context);
+  const auto *TL = selectFirst<TypeLoc>("t", Matches);
+  D << FixItHint::CreateInsertion(
+      Lexer::getLocForEndOfToken(TL->getLocEnd(), 0, *Result.SourceManager,
+                                 getLangOpts()),
+      Replacement);
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/misc/UnusedRAIICheck.h b/clang-tidy/misc/UnusedRAIICheck.h
new file mode 100644 (file)
index 0000000..40f44e3
--- /dev/null
@@ -0,0 +1,35 @@
+//===--- UnusedRAIICheck.h - clang-tidy -------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_UNUSEDRAIICHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_UNUSEDRAIICHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Finds temporaries that look like RAII objects.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/misc-unused-raii.html
+class UnusedRAIICheck : public ClangTidyCheck {
+public:
+  UnusedRAIICheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_UNUSEDRAIICHECK_H
diff --git a/clang-tidy/misc/UnusedUsingDeclsCheck.cpp b/clang-tidy/misc/UnusedUsingDeclsCheck.cpp
new file mode 100644 (file)
index 0000000..b3eabdc
--- /dev/null
@@ -0,0 +1,167 @@
+//===--- UnusedUsingDeclsCheck.cpp - clang-tidy----------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "UnusedUsingDeclsCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+// A function that helps to tell whether a TargetDecl in a UsingDecl will be
+// checked. Only variable, function, function template, class template, class,
+// enum declaration and enum constant declaration are considered.
+static bool ShouldCheckDecl(const Decl *TargetDecl) {
+  return isa<RecordDecl>(TargetDecl) || isa<ClassTemplateDecl>(TargetDecl) ||
+         isa<FunctionDecl>(TargetDecl) || isa<VarDecl>(TargetDecl) ||
+         isa<FunctionTemplateDecl>(TargetDecl) || isa<EnumDecl>(TargetDecl) ||
+         isa<EnumConstantDecl>(TargetDecl);
+}
+
+void UnusedUsingDeclsCheck::registerMatchers(MatchFinder *Finder) {
+  Finder->addMatcher(usingDecl(isExpansionInMainFile()).bind("using"), this);
+  auto DeclMatcher = hasDeclaration(namedDecl().bind("used"));
+  Finder->addMatcher(loc(enumType(DeclMatcher)), this);
+  Finder->addMatcher(loc(recordType(DeclMatcher)), this);
+  Finder->addMatcher(loc(templateSpecializationType(DeclMatcher)), this);
+  Finder->addMatcher(declRefExpr().bind("used"), this);
+  Finder->addMatcher(callExpr(callee(unresolvedLookupExpr().bind("used"))),
+                     this);
+  Finder->addMatcher(
+      callExpr(hasDeclaration(functionDecl(hasAnyTemplateArgument(
+          anyOf(refersToTemplate(templateName().bind("used")),
+                refersToDeclaration(functionDecl().bind("used"))))))),
+      this);
+  Finder->addMatcher(loc(templateSpecializationType(hasAnyTemplateArgument(
+                         templateArgument().bind("used")))),
+                     this);
+}
+
+void UnusedUsingDeclsCheck::check(const MatchFinder::MatchResult &Result) {
+  if (Result.Context->getDiagnostics().hasUncompilableErrorOccurred())
+    return;
+
+  if (const auto *Using = Result.Nodes.getNodeAs<UsingDecl>("using")) {
+    // Ignores using-declarations defined in macros.
+    if (Using->getLocation().isMacroID())
+      return;
+
+    // Ignores using-declarations defined in class definition.
+    if (isa<CXXRecordDecl>(Using->getDeclContext()))
+      return;
+
+    // FIXME: We ignore using-decls defined in function definitions at the
+    // moment because of false positives caused by ADL and different function
+    // scopes.
+    if (isa<FunctionDecl>(Using->getDeclContext()))
+      return;
+
+    UsingDeclContext Context(Using);
+    Context.UsingDeclRange = CharSourceRange::getCharRange(
+        Using->getLocStart(),
+        Lexer::findLocationAfterToken(
+            Using->getLocEnd(), tok::semi, *Result.SourceManager, getLangOpts(),
+            /*SkipTrailingWhitespaceAndNewLine=*/true));
+    for (const auto *UsingShadow : Using->shadows()) {
+      const auto *TargetDecl = UsingShadow->getTargetDecl()->getCanonicalDecl();
+      if (ShouldCheckDecl(TargetDecl))
+        Context.UsingTargetDecls.insert(TargetDecl);
+    }
+    if (!Context.UsingTargetDecls.empty())
+      Contexts.push_back(Context);
+    return;
+  }
+  // Mark using declarations as used by setting FoundDecls' value to zero. As
+  // the AST is walked in order, usages are only marked after a the
+  // corresponding using declaration has been found.
+  // FIXME: This currently doesn't look at whether the type reference is
+  // actually found with the help of the using declaration.
+  if (const auto *Used = Result.Nodes.getNodeAs<NamedDecl>("used")) {
+    if (const auto *FD = dyn_cast<FunctionDecl>(Used)) {
+      removeFromFoundDecls(FD->getPrimaryTemplate());
+    } else if (const auto *Specialization =
+                   dyn_cast<ClassTemplateSpecializationDecl>(Used)) {
+      Used = Specialization->getSpecializedTemplate();
+    }
+    removeFromFoundDecls(Used);
+    return;
+  }
+
+  if (const auto *Used = Result.Nodes.getNodeAs<TemplateArgument>("used")) {
+    // FIXME: Support non-type template parameters.
+    if (Used->getKind() == TemplateArgument::Template) {
+      if (const auto *TD = Used->getAsTemplate().getAsTemplateDecl())
+        removeFromFoundDecls(TD);
+    } else if (Used->getKind() == TemplateArgument::Type) {
+      if (auto *RD = Used->getAsType()->getAsCXXRecordDecl())
+        removeFromFoundDecls(RD);
+    }
+    return;
+  }
+
+  if (const auto *Used = Result.Nodes.getNodeAs<TemplateName>("used")) {
+    removeFromFoundDecls(Used->getAsTemplateDecl());
+    return;
+  }
+
+  if (const auto *DRE = Result.Nodes.getNodeAs<DeclRefExpr>("used")) {
+    if (const auto *FD = dyn_cast<FunctionDecl>(DRE->getDecl())) {
+      if (const auto *FDT = FD->getPrimaryTemplate())
+        removeFromFoundDecls(FDT);
+      else
+        removeFromFoundDecls(FD);
+    } else if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
+      removeFromFoundDecls(VD);
+    } else if (const auto *ECD = dyn_cast<EnumConstantDecl>(DRE->getDecl())) {
+      removeFromFoundDecls(ECD);
+      if (const auto *ET = ECD->getType()->getAs<EnumType>())
+        removeFromFoundDecls(ET->getDecl());
+    }
+  }
+  // Check the uninstantiated template function usage.
+  if (const auto *ULE = Result.Nodes.getNodeAs<UnresolvedLookupExpr>("used")) {
+    for (const NamedDecl *ND : ULE->decls()) {
+      if (const auto *USD = dyn_cast<UsingShadowDecl>(ND))
+        removeFromFoundDecls(USD->getTargetDecl()->getCanonicalDecl());
+    }
+  }
+}
+
+void UnusedUsingDeclsCheck::removeFromFoundDecls(const Decl *D) {
+  if (!D)
+    return;
+  // FIXME: Currently, we don't handle the using-decls being used in different
+  // scopes (such as different namespaces, different functions). Instead of
+  // giving an incorrect message, we mark all of them as used.
+  //
+  // FIXME: Use a more efficient way to find a matching context.
+  for (auto &Context : Contexts) {
+    if (Context.UsingTargetDecls.count(D->getCanonicalDecl()) > 0)
+      Context.IsUsed = true;
+  }
+}
+
+void UnusedUsingDeclsCheck::onEndOfTranslationUnit() {
+  for (const auto &Context : Contexts) {
+    if (!Context.IsUsed) {
+      diag(Context.FoundUsingDecl->getLocation(), "using decl %0 is unused")
+          << Context.FoundUsingDecl
+          << FixItHint::CreateRemoval(Context.UsingDeclRange);
+    }
+  }
+  Contexts.clear();
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/misc/UnusedUsingDeclsCheck.h b/clang-tidy/misc/UnusedUsingDeclsCheck.h
new file mode 100644 (file)
index 0000000..2a41a8f
--- /dev/null
@@ -0,0 +1,58 @@
+//===--- UnusedUsingDeclsCheck.h - clang-tidy--------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_UNUSED_USING_DECLS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_UNUSED_USING_DECLS_H
+
+#include "../ClangTidy.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include <vector>
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Finds unused using declarations.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/misc-unused-using-decls.html
+class UnusedUsingDeclsCheck : public ClangTidyCheck {
+public:
+  UnusedUsingDeclsCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+  void onEndOfTranslationUnit() override;
+
+private:
+  void removeFromFoundDecls(const Decl *D);
+
+  struct UsingDeclContext {
+    explicit UsingDeclContext(const UsingDecl *FoundUsingDecl)
+        : FoundUsingDecl(FoundUsingDecl), IsUsed(false) {}
+    // A set saves all UsingShadowDecls introduced by a UsingDecl. A UsingDecl
+    // can introduce multiple UsingShadowDecls in some cases (such as
+    // overloaded functions).
+    llvm::SmallPtrSet<const Decl *, 4> UsingTargetDecls;
+    // The original UsingDecl.
+    const UsingDecl *FoundUsingDecl;
+    // The source range of the UsingDecl.
+    CharSourceRange UsingDeclRange;
+    // Whether the UsingDecl is used.
+    bool IsUsed;
+  };
+
+  std::vector<UsingDeclContext> Contexts;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_UNUSED_USING_DECLS_H
diff --git a/clang-tidy/misc/UseAfterMoveCheck.cpp b/clang-tidy/misc/UseAfterMoveCheck.cpp
new file mode 100644 (file)
index 0000000..842aa5b
--- /dev/null
@@ -0,0 +1,432 @@
+//===--- UseAfterMoveCheck.cpp - clang-tidy -------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "UseAfterMoveCheck.h"
+
+#include "clang/Analysis/CFG.h"
+#include "clang/Lex/Lexer.h"
+
+#include "../utils/ExprSequence.h"
+
+using namespace clang::ast_matchers;
+using namespace clang::tidy::utils;
+
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+namespace {
+
+/// Contains information about a use-after-move.
+struct UseAfterMove {
+  // The DeclRefExpr that constituted the use of the object.
+  const DeclRefExpr *DeclRef;
+
+  // Is the order in which the move and the use are evaluated undefined?
+  bool EvaluationOrderUndefined;
+};
+
+/// Finds uses of a variable after a move (and maintains state required by the
+/// various internal helper functions).
+class UseAfterMoveFinder {
+public:
+  UseAfterMoveFinder(ASTContext *TheContext);
+
+  // Within the given function body, finds the first use of 'MovedVariable' that
+  // occurs after 'MovingCall' (the expression that performs the move). If a
+  // use-after-move is found, writes information about it to 'TheUseAfterMove'.
+  // Returns whether a use-after-move was found.
+  bool find(Stmt *FunctionBody, const Expr *MovingCall,
+            const ValueDecl *MovedVariable, UseAfterMove *TheUseAfterMove);
+
+private:
+  bool findInternal(const CFGBlock *Block, const Expr *MovingCall,
+                    const ValueDecl *MovedVariable,
+                    UseAfterMove *TheUseAfterMove);
+  void getUsesAndReinits(const CFGBlock *Block, const ValueDecl *MovedVariable,
+                         llvm::SmallVectorImpl<const DeclRefExpr *> *Uses,
+                         llvm::SmallPtrSetImpl<const Stmt *> *Reinits);
+  void getDeclRefs(const CFGBlock *Block, const Decl *MovedVariable,
+                   llvm::SmallPtrSetImpl<const DeclRefExpr *> *DeclRefs);
+  void getReinits(const CFGBlock *Block, const ValueDecl *MovedVariable,
+                  llvm::SmallPtrSetImpl<const Stmt *> *Stmts,
+                  llvm::SmallPtrSetImpl<const DeclRefExpr *> *DeclRefs);
+
+  ASTContext *Context;
+  std::unique_ptr<ExprSequence> Sequence;
+  std::unique_ptr<StmtToBlockMap> BlockMap;
+  llvm::SmallPtrSet<const CFGBlock *, 8> Visited;
+};
+
+} // namespace
+
+
+// Matches nodes that are
+// - Part of a decltype argument or class template argument (we check this by
+//   seeing if they are children of a TypeLoc), or
+// - Part of a function template argument (we check this by seeing if they are
+//   children of a DeclRefExpr that references a function template).
+// DeclRefExprs that fulfill these conditions should not be counted as a use or
+// move.
+static StatementMatcher inDecltypeOrTemplateArg() {
+  return anyOf(hasAncestor(typeLoc()),
+               hasAncestor(declRefExpr(
+                   to(functionDecl(ast_matchers::isTemplateInstantiation())))));
+}
+
+UseAfterMoveFinder::UseAfterMoveFinder(ASTContext *TheContext)
+    : Context(TheContext) {}
+
+bool UseAfterMoveFinder::find(Stmt *FunctionBody, const Expr *MovingCall,
+                              const ValueDecl *MovedVariable,
+                              UseAfterMove *TheUseAfterMove) {
+  // Generate the CFG manually instead of through an AnalysisDeclContext because
+  // it seems the latter can't be used to generate a CFG for the body of a
+  // labmda.
+  //
+  // We include implicit and temporary destructors in the CFG so that
+  // destructors marked [[noreturn]] are handled correctly in the control flow
+  // analysis. (These are used in some styles of assertion macros.)
+  CFG::BuildOptions Options;
+  Options.AddImplicitDtors = true;
+  Options.AddTemporaryDtors = true;
+  std::unique_ptr<CFG> TheCFG =
+      CFG::buildCFG(nullptr, FunctionBody, Context, Options);
+  if (!TheCFG)
+    return false;
+
+  Sequence.reset(new ExprSequence(TheCFG.get(), Context));
+  BlockMap.reset(new StmtToBlockMap(TheCFG.get(), Context));
+  Visited.clear();
+
+  const CFGBlock *Block = BlockMap->blockContainingStmt(MovingCall);
+  if (!Block)
+    return false;
+
+  return findInternal(Block, MovingCall, MovedVariable, TheUseAfterMove);
+}
+
+bool UseAfterMoveFinder::findInternal(const CFGBlock *Block,
+                                      const Expr *MovingCall,
+                                      const ValueDecl *MovedVariable,
+                                      UseAfterMove *TheUseAfterMove) {
+  if (Visited.count(Block))
+    return false;
+
+  // Mark the block as visited (except if this is the block containing the
+  // std::move() and it's being visited the first time).
+  if (!MovingCall)
+    Visited.insert(Block);
+
+  // Get all uses and reinits in the block.
+  llvm::SmallVector<const DeclRefExpr *, 1> Uses;
+  llvm::SmallPtrSet<const Stmt *, 1> Reinits;
+  getUsesAndReinits(Block, MovedVariable, &Uses, &Reinits);
+
+  // Ignore all reinitializations where the move potentially comes after the
+  // reinit.
+  llvm::SmallVector<const Stmt *, 1> ReinitsToDelete;
+  for (const Stmt *Reinit : Reinits) {
+    if (MovingCall && Sequence->potentiallyAfter(MovingCall, Reinit))
+      ReinitsToDelete.push_back(Reinit);
+  }
+  for (const Stmt *Reinit : ReinitsToDelete) {
+    Reinits.erase(Reinit);
+  }
+
+  // Find all uses that potentially come after the move.
+  for (const DeclRefExpr *Use : Uses) {
+    if (!MovingCall || Sequence->potentiallyAfter(Use, MovingCall)) {
+      // Does the use have a saving reinit? A reinit is saving if it definitely
+      // comes before the use, i.e. if there's no potential that the reinit is
+      // after the use.
+      bool HaveSavingReinit = false;
+      for (const Stmt *Reinit : Reinits) {
+        if (!Sequence->potentiallyAfter(Reinit, Use))
+          HaveSavingReinit = true;
+      }
+
+      if (!HaveSavingReinit) {
+        TheUseAfterMove->DeclRef = Use;
+
+        // Is this a use-after-move that depends on order of evaluation?
+        // This is the case if the move potentially comes after the use (and we
+        // already know that use potentially comes after the move, which taken
+        // together tells us that the ordering is unclear).
+        TheUseAfterMove->EvaluationOrderUndefined =
+            MovingCall != nullptr &&
+            Sequence->potentiallyAfter(MovingCall, Use);
+
+        return true;
+      }
+    }
+  }
+
+  // If the object wasn't reinitialized, call ourselves recursively on all
+  // successors.
+  if (Reinits.empty()) {
+    for (const auto &Succ : Block->succs()) {
+      if (Succ && findInternal(Succ, nullptr, MovedVariable, TheUseAfterMove))
+        return true;
+    }
+  }
+
+  return false;
+}
+
+void UseAfterMoveFinder::getUsesAndReinits(
+    const CFGBlock *Block, const ValueDecl *MovedVariable,
+    llvm::SmallVectorImpl<const DeclRefExpr *> *Uses,
+    llvm::SmallPtrSetImpl<const Stmt *> *Reinits) {
+  llvm::SmallPtrSet<const DeclRefExpr *, 1> DeclRefs;
+  llvm::SmallPtrSet<const DeclRefExpr *, 1> ReinitDeclRefs;
+
+  getDeclRefs(Block, MovedVariable, &DeclRefs);
+  getReinits(Block, MovedVariable, Reinits, &ReinitDeclRefs);
+
+  // All references to the variable that aren't reinitializations are uses.
+  Uses->clear();
+  for (const DeclRefExpr *DeclRef : DeclRefs) {
+    if (!ReinitDeclRefs.count(DeclRef))
+      Uses->push_back(DeclRef);
+  }
+
+  // Sort the uses by their occurrence in the source code.
+  std::sort(Uses->begin(), Uses->end(),
+            [](const DeclRefExpr *D1, const DeclRefExpr *D2) {
+              return D1->getExprLoc() < D2->getExprLoc();
+            });
+}
+
+bool isStandardSmartPointer(const ValueDecl *VD) {
+  const Type *TheType = VD->getType().getTypePtrOrNull();
+  if (!TheType)
+    return false;
+
+  const CXXRecordDecl *RecordDecl = TheType->getAsCXXRecordDecl();
+  if (!RecordDecl)
+    return false;
+
+  const IdentifierInfo *ID = RecordDecl->getIdentifier();
+  if (!ID)
+    return false;
+
+  StringRef Name = ID->getName();
+  if (Name != "unique_ptr" && Name != "shared_ptr" && Name != "weak_ptr")
+    return false;
+
+  return RecordDecl->getDeclContext()->isStdNamespace();
+}
+
+void UseAfterMoveFinder::getDeclRefs(
+    const CFGBlock *Block, const Decl *MovedVariable,
+    llvm::SmallPtrSetImpl<const DeclRefExpr *> *DeclRefs) {
+  DeclRefs->clear();
+  for (const auto &Elem : *Block) {
+    Optional<CFGStmt> S = Elem.getAs<CFGStmt>();
+    if (!S)
+      continue;
+
+    auto addDeclRefs = [this, Block,
+                        DeclRefs](const ArrayRef<BoundNodes> Matches) {
+      for (const auto &Match : Matches) {
+        const auto *DeclRef = Match.getNodeAs<DeclRefExpr>("declref");
+        const auto *Operator = Match.getNodeAs<CXXOperatorCallExpr>("operator");
+        if (DeclRef && BlockMap->blockContainingStmt(DeclRef) == Block) {
+          // Ignore uses of a standard smart pointer that don't dereference the
+          // pointer.
+          if (Operator || !isStandardSmartPointer(DeclRef->getDecl())) {
+            DeclRefs->insert(DeclRef);
+          }
+        }
+      }
+    };
+
+    auto DeclRefMatcher = declRefExpr(hasDeclaration(equalsNode(MovedVariable)),
+                                      unless(inDecltypeOrTemplateArg()))
+                              .bind("declref");
+
+    addDeclRefs(match(findAll(DeclRefMatcher), *S->getStmt(), *Context));
+    addDeclRefs(match(
+        findAll(cxxOperatorCallExpr(anyOf(hasOverloadedOperatorName("*"),
+                                          hasOverloadedOperatorName("->"),
+                                          hasOverloadedOperatorName("[]")),
+                                    hasArgument(0, DeclRefMatcher))
+                    .bind("operator")),
+        *S->getStmt(), *Context));
+  }
+}
+
+void UseAfterMoveFinder::getReinits(
+    const CFGBlock *Block, const ValueDecl *MovedVariable,
+    llvm::SmallPtrSetImpl<const Stmt *> *Stmts,
+    llvm::SmallPtrSetImpl<const DeclRefExpr *> *DeclRefs) {
+  auto DeclRefMatcher =
+      declRefExpr(hasDeclaration(equalsNode(MovedVariable))).bind("declref");
+
+  auto StandardContainerTypeMatcher = hasType(cxxRecordDecl(
+      hasAnyName("::std::basic_string", "::std::vector", "::std::deque",
+                 "::std::forward_list", "::std::list", "::std::set",
+                 "::std::map", "::std::multiset", "::std::multimap",
+                 "::std::unordered_set", "::std::unordered_map",
+                 "::std::unordered_multiset", "::std::unordered_multimap")));
+
+  auto StandardSmartPointerTypeMatcher = hasType(cxxRecordDecl(
+      hasAnyName("::std::unique_ptr", "::std::shared_ptr", "::std::weak_ptr")));
+
+  // Matches different types of reinitialization.
+  auto ReinitMatcher =
+      stmt(anyOf(
+               // Assignment. In addition to the overloaded assignment operator,
+               // test for built-in assignment as well, since template functions
+               // may be instantiated to use std::move() on built-in types.
+               binaryOperator(hasOperatorName("="), hasLHS(DeclRefMatcher)),
+               cxxOperatorCallExpr(hasOverloadedOperatorName("="),
+                                   hasArgument(0, DeclRefMatcher)),
+               // Declaration. We treat this as a type of reinitialization too,
+               // so we don't need to treat it separately.
+               declStmt(hasDescendant(equalsNode(MovedVariable))),
+               // clear() and assign() on standard containers.
+               cxxMemberCallExpr(
+                   on(allOf(DeclRefMatcher, StandardContainerTypeMatcher)),
+                   // To keep the matcher simple, we check for assign() calls
+                   // on all standard containers, even though only vector,
+                   // deque, forward_list and list have assign(). If assign()
+                   // is called on any of the other containers, this will be
+                   // flagged by a compile error anyway.
+                   callee(cxxMethodDecl(hasAnyName("clear", "assign")))),
+               // reset() on standard smart pointers.
+               cxxMemberCallExpr(
+                   on(allOf(DeclRefMatcher, StandardSmartPointerTypeMatcher)),
+                   callee(cxxMethodDecl(hasName("reset")))),
+               // Passing variable to a function as a non-const pointer.
+               callExpr(forEachArgumentWithParam(
+                   unaryOperator(hasOperatorName("&"),
+                                 hasUnaryOperand(DeclRefMatcher)),
+                   unless(parmVarDecl(hasType(pointsTo(isConstQualified())))))),
+               // Passing variable to a function as a non-const lvalue reference
+               // (unless that function is std::move()).
+               callExpr(forEachArgumentWithParam(
+                            DeclRefMatcher,
+                            unless(parmVarDecl(hasType(
+                                references(qualType(isConstQualified())))))),
+                        unless(callee(functionDecl(hasName("::std::move")))))))
+          .bind("reinit");
+
+  Stmts->clear();
+  DeclRefs->clear();
+  for (const auto &Elem : *Block) {
+    Optional<CFGStmt> S = Elem.getAs<CFGStmt>();
+    if (!S)
+      continue;
+
+    SmallVector<BoundNodes, 1> Matches =
+        match(findAll(ReinitMatcher), *S->getStmt(), *Context);
+
+    for (const auto &Match : Matches) {
+      const auto *TheStmt = Match.getNodeAs<Stmt>("reinit");
+      const auto *TheDeclRef = Match.getNodeAs<DeclRefExpr>("declref");
+      if (TheStmt && BlockMap->blockContainingStmt(TheStmt) == Block) {
+        Stmts->insert(TheStmt);
+
+        // We count DeclStmts as reinitializations, but they don't have a
+        // DeclRefExpr associated with them -- so we need to check 'TheDeclRef'
+        // before adding it to the set.
+        if (TheDeclRef)
+          DeclRefs->insert(TheDeclRef);
+      }
+    }
+  }
+}
+
+static void emitDiagnostic(const Expr *MovingCall, const DeclRefExpr *MoveArg,
+                           const UseAfterMove &Use, ClangTidyCheck *Check,
+                           ASTContext *Context) {
+  SourceLocation UseLoc = Use.DeclRef->getExprLoc();
+  SourceLocation MoveLoc = MovingCall->getExprLoc();
+
+  Check->diag(UseLoc, "'%0' used after it was moved")
+      << MoveArg->getDecl()->getName();
+  Check->diag(MoveLoc, "move occurred here", DiagnosticIDs::Note);
+  if (Use.EvaluationOrderUndefined) {
+    Check->diag(UseLoc,
+                "the use and move are unsequenced, i.e. there is no guarantee "
+                "about the order in which they are evaluated",
+                DiagnosticIDs::Note);
+  } else if (UseLoc < MoveLoc || Use.DeclRef == MoveArg) {
+    Check->diag(UseLoc,
+                "the use happens in a later loop iteration than the move",
+                DiagnosticIDs::Note);
+  }
+}
+
+void UseAfterMoveCheck::registerMatchers(MatchFinder *Finder) {
+  if (!getLangOpts().CPlusPlus11)
+    return;
+
+  auto CallMoveMatcher =
+      callExpr(callee(functionDecl(hasName("::std::move"))), argumentCountIs(1),
+               hasArgument(0, declRefExpr().bind("arg")),
+               anyOf(hasAncestor(lambdaExpr().bind("containing-lambda")),
+                     hasAncestor(functionDecl().bind("containing-func"))),
+               unless(inDecltypeOrTemplateArg()))
+          .bind("call-move");
+
+  Finder->addMatcher(
+      // To find the Stmt that we assume performs the actual move, we look for
+      // the direct ancestor of the std::move() that isn't one of the node
+      // types ignored by ignoringParenImpCasts().
+      stmt(forEach(expr(ignoringParenImpCasts(CallMoveMatcher))),
+           // Don't allow an InitListExpr to be the moving call. An InitListExpr
+           // has both a syntactic and a semantic form, and the parent-child
+           // relationships are different between the two. This could cause an
+           // InitListExpr to be analyzed as the moving call in addition to the
+           // Expr that we actually want, resulting in two diagnostics with
+           // different code locations for the same move.
+           unless(initListExpr()),
+           unless(expr(ignoringParenImpCasts(equalsBoundNode("call-move")))))
+          .bind("moving-call"),
+      this);
+}
+
+void UseAfterMoveCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *ContainingLambda =
+      Result.Nodes.getNodeAs<LambdaExpr>("containing-lambda");
+  const auto *ContainingFunc =
+      Result.Nodes.getNodeAs<FunctionDecl>("containing-func");
+  const auto *CallMove = Result.Nodes.getNodeAs<CallExpr>("call-move");
+  const auto *MovingCall = Result.Nodes.getNodeAs<Expr>("moving-call");
+  const auto *Arg = Result.Nodes.getNodeAs<DeclRefExpr>("arg");
+
+  if (!MovingCall || !MovingCall->getExprLoc().isValid())
+    MovingCall = CallMove;
+
+  Stmt *FunctionBody = nullptr;
+  if (ContainingLambda)
+    FunctionBody = ContainingLambda->getBody();
+  else if (ContainingFunc)
+    FunctionBody = ContainingFunc->getBody();
+  else
+    return;
+
+  // Ignore the std::move if the variable that was passed to it isn't a local
+  // variable.
+  if (!Arg->getDecl()->getDeclContext()->isFunctionOrMethod())
+    return;
+
+  UseAfterMoveFinder finder(Result.Context);
+  UseAfterMove Use;
+  if (finder.find(FunctionBody, MovingCall, Arg->getDecl(), &Use))
+    emitDiagnostic(MovingCall, Arg, Use, this, Result.Context);
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/misc/UseAfterMoveCheck.h b/clang-tidy/misc/UseAfterMoveCheck.h
new file mode 100644 (file)
index 0000000..2f6be5b
--- /dev/null
@@ -0,0 +1,36 @@
+//===--- UseAfterMoveCheck.h - clang-tidy ---------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_USEAFTERMOVECHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_USEAFTERMOVECHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// The check warns if an object is used after it has been moved, without an
+/// intervening reinitialization.
+///
+/// For details, see the user-facing documentation:
+/// http://clang.llvm.org/extra/clang-tidy/checks/misc-use-after-move.html
+class UseAfterMoveCheck : public ClangTidyCheck {
+public:
+  UseAfterMoveCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_USEAFTERMOVECHECK_H
diff --git a/clang-tidy/misc/VirtualNearMissCheck.cpp b/clang-tidy/misc/VirtualNearMissCheck.cpp
new file mode 100644 (file)
index 0000000..0435406
--- /dev/null
@@ -0,0 +1,274 @@
+//===--- VirtualNearMissCheck.cpp - clang-tidy-----------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "VirtualNearMissCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/CXXInheritance.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+AST_MATCHER(CXXMethodDecl, isStatic) { return Node.isStatic(); }
+
+AST_MATCHER(CXXMethodDecl, isOverloadedOperator) {
+  return Node.isOverloadedOperator();
+}
+
+/// Finds out if the given method overrides some method.
+static bool isOverrideMethod(const CXXMethodDecl *MD) {
+  return MD->size_overridden_methods() > 0 || MD->hasAttr<OverrideAttr>();
+}
+
+/// Checks whether the return types are covariant, according to
+/// C++[class.virtual]p7.
+///
+/// Similar with clang::Sema::CheckOverridingFunctionReturnType.
+/// \returns true if the return types of BaseMD and DerivedMD are covariant.
+static bool checkOverridingFunctionReturnType(const ASTContext *Context,
+                                              const CXXMethodDecl *BaseMD,
+                                              const CXXMethodDecl *DerivedMD) {
+  QualType BaseReturnTy = BaseMD->getType()
+                              ->getAs<FunctionType>()
+                              ->getReturnType()
+                              .getCanonicalType();
+  QualType DerivedReturnTy = DerivedMD->getType()
+                                 ->getAs<FunctionType>()
+                                 ->getReturnType()
+                                 .getCanonicalType();
+
+  if (DerivedReturnTy->isDependentType() || BaseReturnTy->isDependentType())
+    return false;
+
+  // Check if return types are identical.
+  if (Context->hasSameType(DerivedReturnTy, BaseReturnTy))
+    return true;
+
+  /// Check if the return types are covariant.
+
+  // Both types must be pointers or references to classes.
+  if (!(BaseReturnTy->isPointerType() && DerivedReturnTy->isPointerType()) &&
+      !(BaseReturnTy->isReferenceType() && DerivedReturnTy->isReferenceType()))
+    return false;
+
+  /// BTy is the class type in return type of BaseMD. For example,
+  ///    B* Base::md()
+  /// While BRD is the declaration of B.
+  QualType DTy = DerivedReturnTy->getPointeeType().getCanonicalType();
+  QualType BTy = BaseReturnTy->getPointeeType().getCanonicalType();
+
+  const CXXRecordDecl *DRD = DTy->getAsCXXRecordDecl();
+  const CXXRecordDecl *BRD = BTy->getAsCXXRecordDecl();
+  if (DRD == nullptr || BRD == nullptr)
+    return false;
+
+  if (!DRD->hasDefinition() || !BRD->hasDefinition())
+    return false;
+
+  if (DRD == BRD)
+    return true;
+
+  if (!Context->hasSameUnqualifiedType(DTy, BTy)) {
+    // Begin checking whether the conversion from D to B is valid.
+    CXXBasePaths Paths(/*FindAmbiguities=*/true, /*RecordPaths=*/true,
+                       /*DetectVirtual=*/false);
+
+    // Check whether D is derived from B, and fill in a CXXBasePaths object.
+    if (!DRD->isDerivedFrom(BRD, Paths))
+      return false;
+
+    // Check ambiguity.
+    if (Paths.isAmbiguous(Context->getCanonicalType(BTy).getUnqualifiedType()))
+      return false;
+
+    // Check accessibility.
+    // FIXME: We currently only support checking if B is accessible base class
+    // of D, or D is the same class which DerivedMD is in.
+    bool IsItself =
+        DRD->getCanonicalDecl() == DerivedMD->getParent()->getCanonicalDecl();
+    bool HasPublicAccess = false;
+    for (const auto &Path : Paths) {
+      if (Path.Access == AS_public)
+        HasPublicAccess = true;
+    }
+    if (!HasPublicAccess && !IsItself)
+      return false;
+    // End checking conversion from D to B.
+  }
+
+  // Both pointers or references should have the same cv-qualification.
+  if (DerivedReturnTy.getLocalCVRQualifiers() !=
+      BaseReturnTy.getLocalCVRQualifiers())
+    return false;
+
+  // The class type D should have the same cv-qualification as or less
+  // cv-qualification than the class type B.
+  if (DTy.isMoreQualifiedThan(BTy))
+    return false;
+
+  return true;
+}
+
+/// \returns decayed type for arrays and functions.
+static QualType getDecayedType(QualType Type) {
+  if (const auto *Decayed = Type->getAs<DecayedType>())
+    return Decayed->getDecayedType();
+  return Type;
+}
+
+/// \returns true if the param types are the same.
+static bool checkParamTypes(const CXXMethodDecl *BaseMD,
+                            const CXXMethodDecl *DerivedMD) {
+  unsigned NumParamA = BaseMD->getNumParams();
+  unsigned NumParamB = DerivedMD->getNumParams();
+  if (NumParamA != NumParamB)
+    return false;
+
+  for (unsigned I = 0; I < NumParamA; I++) {
+    if (getDecayedType(BaseMD->getParamDecl(I)->getType().getCanonicalType()) !=
+        getDecayedType(
+            DerivedMD->getParamDecl(I)->getType().getCanonicalType()))
+      return false;
+  }
+  return true;
+}
+
+/// \returns true if derived method can override base method except for the
+/// name.
+static bool checkOverrideWithoutName(const ASTContext *Context,
+                                     const CXXMethodDecl *BaseMD,
+                                     const CXXMethodDecl *DerivedMD) {
+  if (BaseMD->isStatic() != DerivedMD->isStatic())
+    return false;
+
+  if (BaseMD->getType() == DerivedMD->getType())
+    return true;
+
+  // Now the function types are not identical. Then check if the return types
+  // are covariant and if the param types are the same.
+  if (!checkOverridingFunctionReturnType(Context, BaseMD, DerivedMD))
+    return false;
+  return checkParamTypes(BaseMD, DerivedMD);
+}
+
+/// Check whether BaseMD overrides DerivedMD.
+///
+/// Prerequisite: the class which BaseMD is in should be a base class of that
+/// DerivedMD is in.
+static bool checkOverrideByDerivedMethod(const CXXMethodDecl *BaseMD,
+                                         const CXXMethodDecl *DerivedMD) {
+  for (CXXMethodDecl::method_iterator I = DerivedMD->begin_overridden_methods(),
+                                      E = DerivedMD->end_overridden_methods();
+       I != E; ++I) {
+    const CXXMethodDecl *OverriddenMD = *I;
+    if (BaseMD->getCanonicalDecl() == OverriddenMD->getCanonicalDecl())
+      return true;
+  }
+
+  return false;
+}
+
+bool VirtualNearMissCheck::isPossibleToBeOverridden(
+    const CXXMethodDecl *BaseMD) {
+  auto Iter = PossibleMap.find(BaseMD);
+  if (Iter != PossibleMap.end())
+    return Iter->second;
+
+  bool IsPossible = !BaseMD->isImplicit() && !isa<CXXConstructorDecl>(BaseMD) &&
+                    !isa<CXXDestructorDecl>(BaseMD) && BaseMD->isVirtual() &&
+                    !BaseMD->isOverloadedOperator() &&
+                    !isa<CXXConversionDecl>(BaseMD);
+  PossibleMap[BaseMD] = IsPossible;
+  return IsPossible;
+}
+
+bool VirtualNearMissCheck::isOverriddenByDerivedClass(
+    const CXXMethodDecl *BaseMD, const CXXRecordDecl *DerivedRD) {
+  auto Key = std::make_pair(BaseMD, DerivedRD);
+  auto Iter = OverriddenMap.find(Key);
+  if (Iter != OverriddenMap.end())
+    return Iter->second;
+
+  bool IsOverridden = false;
+  for (const CXXMethodDecl *DerivedMD : DerivedRD->methods()) {
+    if (!isOverrideMethod(DerivedMD))
+      continue;
+
+    if (checkOverrideByDerivedMethod(BaseMD, DerivedMD)) {
+      IsOverridden = true;
+      break;
+    }
+  }
+  OverriddenMap[Key] = IsOverridden;
+  return IsOverridden;
+}
+
+void VirtualNearMissCheck::registerMatchers(MatchFinder *Finder) {
+  if (!getLangOpts().CPlusPlus)
+    return;
+
+  Finder->addMatcher(
+      cxxMethodDecl(
+          unless(anyOf(isOverride(), isImplicit(), cxxConstructorDecl(),
+                       cxxDestructorDecl(), cxxConversionDecl(), isStatic(),
+                       isOverloadedOperator())))
+          .bind("method"),
+      this);
+}
+
+void VirtualNearMissCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *DerivedMD = Result.Nodes.getNodeAs<CXXMethodDecl>("method");
+  assert(DerivedMD);
+
+  const ASTContext *Context = Result.Context;
+
+  const auto *DerivedRD = DerivedMD->getParent()->getDefinition();
+  assert(DerivedRD);
+
+  for (const auto &BaseSpec : DerivedRD->bases()) {
+    if (const auto *BaseRD = BaseSpec.getType()->getAsCXXRecordDecl()) {
+      for (const auto *BaseMD : BaseRD->methods()) {
+        if (!isPossibleToBeOverridden(BaseMD))
+          continue;
+
+        if (isOverriddenByDerivedClass(BaseMD, DerivedRD))
+          continue;
+
+        unsigned EditDistance =
+            BaseMD->getName().edit_distance(DerivedMD->getName());
+        if (EditDistance > 0 && EditDistance <= EditDistanceThreshold) {
+          if (checkOverrideWithoutName(Context, BaseMD, DerivedMD)) {
+            // A "virtual near miss" is found.
+            auto Range = CharSourceRange::getTokenRange(
+                SourceRange(DerivedMD->getLocation()));
+
+            bool ApplyFix = !BaseMD->isTemplateInstantiation() &&
+                            !DerivedMD->isTemplateInstantiation();
+            auto Diag =
+                diag(DerivedMD->getLocStart(),
+                     "method '%0' has a similar name and the same signature as "
+                     "virtual method '%1'; did you mean to override it?")
+                << DerivedMD->getQualifiedNameAsString()
+                << BaseMD->getQualifiedNameAsString();
+            if (ApplyFix)
+              Diag << FixItHint::CreateReplacement(Range, BaseMD->getName());
+          }
+        }
+      }
+    }
+  }
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/misc/VirtualNearMissCheck.h b/clang-tidy/misc/VirtualNearMissCheck.h
new file mode 100644 (file)
index 0000000..4e108f2
--- /dev/null
@@ -0,0 +1,65 @@
+//===--- VirtualNearMissCheck.h - clang-tidy---------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_VIRTUAL_NEAR_MISS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_VIRTUAL_NEAR_MISS_H
+
+#include "../ClangTidy.h"
+#include <map>
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// \brief Checks for near miss of virtual methods.
+///
+/// For a method in a derived class, this check looks for virtual method with a
+/// very similar name and an identical signature defined in a base class.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/misc-virtual-near-miss.html
+class VirtualNearMissCheck : public ClangTidyCheck {
+public:
+  VirtualNearMissCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+  /// Check if the given method is possible to be overridden by some other
+  /// method. Operators and destructors are excluded.
+  ///
+  /// Results are memoized in PossibleMap.
+  bool isPossibleToBeOverridden(const CXXMethodDecl *BaseMD);
+
+  /// Check if the given base method is overridden by some methods in the given
+  /// derived class.
+  ///
+  /// Results are memoized in OverriddenMap.
+  bool isOverriddenByDerivedClass(const CXXMethodDecl *BaseMD,
+                                  const CXXRecordDecl *DerivedRD);
+
+  /// Key: the unique ID of a method.
+  /// Value: whether the method is possible to be overridden.
+  std::map<const CXXMethodDecl *, bool> PossibleMap;
+
+  /// Key: <unique ID of base method, name of derived class>
+  /// Value: whether the base method is overridden by some method in the derived
+  /// class.
+  std::map<std::pair<const CXXMethodDecl *, const CXXRecordDecl *>, bool>
+      OverriddenMap;
+
+  const unsigned EditDistanceThreshold = 1;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_VIRTUAL_NEAR_MISS_H
diff --git a/clang-tidy/modernize/AvoidBindCheck.cpp b/clang-tidy/modernize/AvoidBindCheck.cpp
new file mode 100644 (file)
index 0000000..7cdbb66
--- /dev/null
@@ -0,0 +1,181 @@
+//===--- AvoidBindCheck.cpp - clang-tidy-----------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "AvoidBindCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Lex/Lexer.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/SmallSet.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/Regex.h"
+#include "llvm/Support/raw_ostream.h"
+#include <algorithm>
+#include <cstddef>
+#include <string>
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+namespace {
+
+enum BindArgumentKind { BK_Temporary, BK_Placeholder, BK_CallExpr, BK_Other };
+
+struct BindArgument {
+  StringRef Tokens;
+  BindArgumentKind Kind = BK_Other;
+  size_t PlaceHolderIndex = 0;
+};
+
+} // end namespace
+
+static SmallVector<BindArgument, 4>
+buildBindArguments(const MatchFinder::MatchResult &Result, const CallExpr *C) {
+  SmallVector<BindArgument, 4> BindArguments;
+  llvm::Regex MatchPlaceholder("^_([0-9]+)$");
+
+  // Start at index 1 as first argument to bind is the function name.
+  for (size_t I = 1, ArgCount = C->getNumArgs(); I < ArgCount; ++I) {
+    const Expr *E = C->getArg(I);
+    BindArgument B;
+    if (const auto *M = dyn_cast<MaterializeTemporaryExpr>(E)) {
+      const auto *TE = M->GetTemporaryExpr();
+      B.Kind = isa<CallExpr>(TE) ? BK_CallExpr : BK_Temporary;
+    }
+
+    B.Tokens = Lexer::getSourceText(
+        CharSourceRange::getTokenRange(E->getLocStart(), E->getLocEnd()),
+        *Result.SourceManager, Result.Context->getLangOpts());
+
+    SmallVector<StringRef, 2> Matches;
+    if (B.Kind == BK_Other && MatchPlaceholder.match(B.Tokens, &Matches)) {
+      B.Kind = BK_Placeholder;
+      B.PlaceHolderIndex = std::stoi(Matches[1]);
+    }
+    BindArguments.push_back(B);
+  }
+  return BindArguments;
+}
+
+static void addPlaceholderArgs(const ArrayRef<BindArgument> Args,
+                               llvm::raw_ostream &Stream) {
+  auto MaxPlaceholderIt =
+      std::max_element(Args.begin(), Args.end(),
+                       [](const BindArgument &B1, const BindArgument &B2) {
+                         return B1.PlaceHolderIndex < B2.PlaceHolderIndex;
+                       });
+
+  // Placeholders (if present) have index 1 or greater.
+  if (MaxPlaceholderIt == Args.end() || MaxPlaceholderIt->PlaceHolderIndex == 0)
+    return;
+
+  size_t PlaceholderCount = MaxPlaceholderIt->PlaceHolderIndex;
+  Stream << "(";
+  StringRef Delimiter = "";
+  for (size_t I = 1; I <= PlaceholderCount; ++I) {
+    Stream << Delimiter << "auto && arg" << I;
+    Delimiter = ", ";
+  }
+  Stream << ")";
+}
+
+static void addFunctionCallArgs(const ArrayRef<BindArgument> Args,
+                                llvm::raw_ostream &Stream) {
+  StringRef Delimiter = "";
+  for (const auto &B : Args) {
+    if (B.PlaceHolderIndex)
+      Stream << Delimiter << "arg" << B.PlaceHolderIndex;
+    else
+      Stream << Delimiter << B.Tokens;
+    Delimiter = ", ";
+  }
+}
+
+static bool isPlaceHolderIndexRepeated(const ArrayRef<BindArgument> Args) {
+  llvm::SmallSet<size_t, 4> PlaceHolderIndices;
+  for (const BindArgument &B : Args) {
+    if (B.PlaceHolderIndex) {
+      if (!PlaceHolderIndices.insert(B.PlaceHolderIndex).second)
+        return true;
+    }
+  }
+  return false;
+}
+
+void AvoidBindCheck::registerMatchers(MatchFinder *Finder) {
+  if (!getLangOpts().CPlusPlus14) // Need C++14 for generic lambdas.
+    return;
+
+  Finder->addMatcher(
+      callExpr(
+          callee(namedDecl(hasName("::std::bind"))),
+          hasArgument(0, declRefExpr(to(functionDecl().bind("f"))).bind("ref")))
+          .bind("bind"),
+      this);
+}
+
+void AvoidBindCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *MatchedDecl = Result.Nodes.getNodeAs<CallExpr>("bind");
+  auto Diag = diag(MatchedDecl->getLocStart(), "prefer a lambda to std::bind");
+
+  const auto Args = buildBindArguments(Result, MatchedDecl);
+
+  // Do not attempt to create fixits for nested call expressions.
+  // FIXME: Create lambda capture variables to capture output of calls.
+  // NOTE: Supporting nested std::bind will be more difficult due to placeholder
+  // sharing between outer and inner std:bind invocations.
+  if (llvm::any_of(Args,
+                   [](const BindArgument &B) { return B.Kind == BK_CallExpr; }))
+    return;
+
+  // Do not attempt to create fixits when placeholders are reused.
+  // Unused placeholders are supported by requiring C++14 generic lambdas.
+  // FIXME: Support this case by deducing the common type.
+  if (isPlaceHolderIndexRepeated(Args))
+    return;
+
+  const auto *F = Result.Nodes.getNodeAs<FunctionDecl>("f");
+
+  // std::bind can support argument count mismatch between its arguments and the
+  // bound function's arguments. Do not attempt to generate a fixit for such
+  // cases.
+  // FIXME: Support this case by creating unused lambda capture variables.
+  if (F->getNumParams() != Args.size())
+    return;
+
+  std::string Buffer;
+  llvm::raw_string_ostream Stream(Buffer);
+
+  bool HasCapturedArgument = llvm::any_of(
+      Args, [](const BindArgument &B) { return B.Kind == BK_Other; });
+  const auto *Ref = Result.Nodes.getNodeAs<DeclRefExpr>("ref");
+  Stream << "[" << (HasCapturedArgument ? "=" : "") << "]";
+  addPlaceholderArgs(Args, Stream);
+  Stream << " { return ";
+  Ref->printPretty(Stream, nullptr, Result.Context->getPrintingPolicy());
+  Stream << "(";
+  addFunctionCallArgs(Args, Stream);
+  Stream << "); };";
+
+  Diag << FixItHint::CreateReplacement(MatchedDecl->getSourceRange(),
+                                       Stream.str());
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/modernize/AvoidBindCheck.h b/clang-tidy/modernize/AvoidBindCheck.h
new file mode 100644 (file)
index 0000000..5ae0241
--- /dev/null
@@ -0,0 +1,36 @@
+//===--- AvoidBindCheck.h - clang-tidy---------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_AVOID_BIND_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_AVOID_BIND_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+/// Replace simple uses of std::bind with a lambda.
+///
+/// FIXME: Add support for function references and member function references.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/modernize-avoid-std-bind.html
+class AvoidBindCheck : public ClangTidyCheck {
+public:
+  AvoidBindCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_AVOID_BIND_H
diff --git a/clang-tidy/modernize/CMakeLists.txt b/clang-tidy/modernize/CMakeLists.txt
new file mode 100644 (file)
index 0000000..e344ada
--- /dev/null
@@ -0,0 +1,40 @@
+set(LLVM_LINK_COMPONENTS support)
+
+add_clang_library(clangTidyModernizeModule
+  AvoidBindCheck.cpp
+  DeprecatedHeadersCheck.cpp
+  LoopConvertCheck.cpp
+  LoopConvertUtils.cpp
+  MakeSmartPtrCheck.cpp
+  MakeSharedCheck.cpp
+  MakeUniqueCheck.cpp
+  ModernizeTidyModule.cpp
+  PassByValueCheck.cpp
+  RawStringLiteralCheck.cpp
+  RedundantVoidArgCheck.cpp
+  ReplaceAutoPtrCheck.cpp
+  ReplaceRandomShuffleCheck.cpp
+  ReturnBracedInitListCheck.cpp
+  ShrinkToFitCheck.cpp
+  UnaryStaticAssertCheck.cpp
+  UseAutoCheck.cpp
+  UseBoolLiteralsCheck.cpp
+  UseDefaultMemberInitCheck.cpp
+  UseEmplaceCheck.cpp
+  UseEqualsDefaultCheck.cpp
+  UseEqualsDeleteCheck.cpp
+  UseNoexceptCheck.cpp
+  UseNullptrCheck.cpp
+  UseOverrideCheck.cpp
+  UseTransparentFunctorsCheck.cpp
+  UseUsingCheck.cpp
+
+  LINK_LIBS
+  clangAST
+  clangASTMatchers
+  clangBasic
+  clangLex
+  clangTidy
+  clangTidyReadabilityModule
+  clangTidyUtils
+  )
diff --git a/clang-tidy/modernize/DeprecatedHeadersCheck.cpp b/clang-tidy/modernize/DeprecatedHeadersCheck.cpp
new file mode 100644 (file)
index 0000000..ea5943e
--- /dev/null
@@ -0,0 +1,123 @@
+//===--- DeprecatedHeadersCheck.cpp - clang-tidy---------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "DeprecatedHeadersCheck.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Lex/PPCallbacks.h"
+#include "clang/Lex/Preprocessor.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/StringSet.h"
+
+#include <vector>
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+namespace {
+class IncludeModernizePPCallbacks : public PPCallbacks {
+public:
+  explicit IncludeModernizePPCallbacks(ClangTidyCheck &Check,
+                                       LangOptions LangOpts);
+
+  void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
+                          StringRef FileName, bool IsAngled,
+                          CharSourceRange FilenameRange, const FileEntry *File,
+                          StringRef SearchPath, StringRef RelativePath,
+                          const Module *Imported) override;
+
+private:
+  ClangTidyCheck &Check;
+  LangOptions LangOpts;
+  llvm::StringMap<std::string> CStyledHeaderToCxx;
+  llvm::StringSet<> DeleteHeaders;
+};
+} // namespace
+
+void DeprecatedHeadersCheck::registerPPCallbacks(CompilerInstance &Compiler) {
+  if (this->getLangOpts().CPlusPlus) {
+    Compiler.getPreprocessor().addPPCallbacks(
+        ::llvm::make_unique<IncludeModernizePPCallbacks>(*this,
+                                                         this->getLangOpts()));
+  }
+}
+
+IncludeModernizePPCallbacks::IncludeModernizePPCallbacks(ClangTidyCheck &Check,
+                                                         LangOptions LangOpts)
+    : Check(Check), LangOpts(LangOpts) {
+  for (const auto &KeyValue :
+       std::vector<std::pair<llvm::StringRef, std::string>>(
+           {{"assert.h", "cassert"},
+            {"complex.h", "complex"},
+            {"ctype.h", "cctype"},
+            {"errno.h", "cerrno"},
+            {"float.h", "cfloat"},
+            {"limits.h", "climits"},
+            {"locale.h", "clocale"},
+            {"math.h", "cmath"},
+            {"setjmp.h", "csetjmp"},
+            {"signal.h", "csignal"},
+            {"stdarg.h", "cstdarg"},
+            {"stddef.h", "cstddef"},
+            {"stdio.h", "cstdio"},
+            {"stdlib.h", "cstdlib"},
+            {"string.h", "cstring"},
+            {"time.h", "ctime"},
+            {"wchar.h", "cwchar"},
+            {"wctype.h", "cwctype"}})) {
+    CStyledHeaderToCxx.insert(KeyValue);
+  }
+  // Add C++ 11 headers.
+  if (LangOpts.CPlusPlus11) {
+    for (const auto &KeyValue :
+         std::vector<std::pair<llvm::StringRef, std::string>>(
+             {{"fenv.h", "cfenv"},
+              {"stdint.h", "cstdint"},
+              {"inttypes.h", "cinttypes"},
+              {"tgmath.h", "ctgmath"},
+              {"uchar.h", "cuchar"}})) {
+      CStyledHeaderToCxx.insert(KeyValue);
+    }
+  }
+  for (const auto &Key :
+       std::vector<std::string>({"stdalign.h", "stdbool.h", "iso646.h"})) {
+    DeleteHeaders.insert(Key);
+  }
+}
+
+void IncludeModernizePPCallbacks::InclusionDirective(
+    SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName,
+    bool IsAngled, CharSourceRange FilenameRange, const FileEntry *File,
+    StringRef SearchPath, StringRef RelativePath, const Module *Imported) {
+  // FIXME: Take care of library symbols from the global namespace.
+  //
+  // Reasonable options for the check:
+  //
+  // 1. Insert std prefix for every such symbol occurrence.
+  // 2. Insert `using namespace std;` to the beginning of TU.
+  // 3. Do nothing and let the user deal with the migration himself.
+  if (CStyledHeaderToCxx.count(FileName) != 0) {
+    std::string Replacement =
+        (llvm::Twine("<") + CStyledHeaderToCxx[FileName] + ">").str();
+    Check.diag(FilenameRange.getBegin(), "inclusion of deprecated C++ header "
+                                         "'%0'; consider using '%1' instead")
+        << FileName << CStyledHeaderToCxx[FileName]
+        << FixItHint::CreateReplacement(FilenameRange.getAsRange(),
+                                        Replacement);
+  } else if (DeleteHeaders.count(FileName) != 0) {
+    Check.diag(FilenameRange.getBegin(),
+               "including '%0' has no effect in C++; consider removing it")
+        << FileName << FixItHint::CreateRemoval(
+                           SourceRange(HashLoc, FilenameRange.getEnd()));
+  }
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/modernize/DeprecatedHeadersCheck.h b/clang-tidy/modernize/DeprecatedHeadersCheck.h
new file mode 100644 (file)
index 0000000..ee7254e
--- /dev/null
@@ -0,0 +1,47 @@
+//===--- DeprecatedHeadersCheck.h - clang-tidy-------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_C_HEADERS_TO_CXX_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_C_HEADERS_TO_CXX_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+/// This check replaces deprecated C library headers with their C++ STL
+/// alternatives.
+///
+/// Before:
+/// ~~~{.cpp}
+/// #include <header.h>
+/// ~~~
+///
+/// After:
+/// ~~~{.cpp}
+/// #include <cheader>
+/// ~~~
+///
+/// Example: ``<stdio.h> => <cstdio>``
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/modernize-deprecated-headers.html
+class DeprecatedHeadersCheck : public ClangTidyCheck {
+public:
+  DeprecatedHeadersCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerPPCallbacks(CompilerInstance &Compiler) override;
+};
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_C_HEADERS_TO_CXX_H
diff --git a/clang-tidy/modernize/LoopConvertCheck.cpp b/clang-tidy/modernize/LoopConvertCheck.cpp
new file mode 100644 (file)
index 0000000..ac0bceb
--- /dev/null
@@ -0,0 +1,918 @@
+//===--- LoopConvertCheck.cpp - clang-tidy---------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "LoopConvertCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Lex/Lexer.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringSwitch.h"
+#include "llvm/Support/Casting.h"
+#include <cassert>
+#include <cstring>
+#include <utility>
+
+using namespace clang::ast_matchers;
+using namespace llvm;
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+static const char LoopNameArray[] = "forLoopArray";
+static const char LoopNameIterator[] = "forLoopIterator";
+static const char LoopNamePseudoArray[] = "forLoopPseudoArray";
+static const char ConditionBoundName[] = "conditionBound";
+static const char ConditionVarName[] = "conditionVar";
+static const char IncrementVarName[] = "incrementVar";
+static const char InitVarName[] = "initVar";
+static const char BeginCallName[] = "beginCall";
+static const char EndCallName[] = "endCall";
+static const char ConditionEndVarName[] = "conditionEndVar";
+static const char EndVarName[] = "endVar";
+static const char DerefByValueResultName[] = "derefByValueResult";
+static const char DerefByRefResultName[] = "derefByRefResult";
+
+// shared matchers
+static const TypeMatcher AnyType = anything();
+
+static const StatementMatcher IntegerComparisonMatcher =
+    expr(ignoringParenImpCasts(
+        declRefExpr(to(varDecl(hasType(isInteger())).bind(ConditionVarName)))));
+
+static const DeclarationMatcher InitToZeroMatcher =
+    varDecl(hasInitializer(ignoringParenImpCasts(integerLiteral(equals(0)))))
+        .bind(InitVarName);
+
+static const StatementMatcher IncrementVarMatcher =
+    declRefExpr(to(varDecl(hasType(isInteger())).bind(IncrementVarName)));
+
+/// \brief The matcher for loops over arrays.
+///
+/// In this general example, assuming 'j' and 'k' are of integral type:
+/// \code
+///   for (int i = 0; j < 3 + 2; ++k) { ... }
+/// \endcode
+/// The following string identifiers are bound to these parts of the AST:
+///   ConditionVarName: 'j' (as a VarDecl)
+///   ConditionBoundName: '3 + 2' (as an Expr)
+///   InitVarName: 'i' (as a VarDecl)
+///   IncrementVarName: 'k' (as a VarDecl)
+///   LoopName: The entire for loop (as a ForStmt)
+///
+/// Client code will need to make sure that:
+///   - The three index variables identified by the matcher are the same
+///     VarDecl.
+///   - The index variable is only used as an array index.
+///   - All arrays indexed by the loop are the same.
+StatementMatcher makeArrayLoopMatcher() {
+  StatementMatcher ArrayBoundMatcher =
+      expr(hasType(isInteger())).bind(ConditionBoundName);
+
+  return forStmt(
+             unless(isInTemplateInstantiation()),
+             hasLoopInit(declStmt(hasSingleDecl(InitToZeroMatcher))),
+             hasCondition(anyOf(
+                 binaryOperator(hasOperatorName("<"),
+                                hasLHS(IntegerComparisonMatcher),
+                                hasRHS(ArrayBoundMatcher)),
+                 binaryOperator(hasOperatorName(">"), hasLHS(ArrayBoundMatcher),
+                                hasRHS(IntegerComparisonMatcher)))),
+             hasIncrement(unaryOperator(hasOperatorName("++"),
+                                        hasUnaryOperand(IncrementVarMatcher))))
+      .bind(LoopNameArray);
+}
+
+/// \brief The matcher used for iterator-based for loops.
+///
+/// This matcher is more flexible than array-based loops. It will match
+/// catch loops of the following textual forms (regardless of whether the
+/// iterator type is actually a pointer type or a class type):
+///
+/// Assuming f, g, and h are of type containerType::iterator,
+/// \code
+///   for (containerType::iterator it = container.begin(),
+///        e = createIterator(); f != g; ++h) { ... }
+///   for (containerType::iterator it = container.begin();
+///        f != anotherContainer.end(); ++h) { ... }
+/// \endcode
+/// The following string identifiers are bound to the parts of the AST:
+///   InitVarName: 'it' (as a VarDecl)
+///   ConditionVarName: 'f' (as a VarDecl)
+///   LoopName: The entire for loop (as a ForStmt)
+///   In the first example only:
+///     EndVarName: 'e' (as a VarDecl)
+///     ConditionEndVarName: 'g' (as a VarDecl)
+///   In the second example only:
+///     EndCallName: 'container.end()' (as a CXXMemberCallExpr)
+///
+/// Client code will need to make sure that:
+///   - The iterator variables 'it', 'f', and 'h' are the same.
+///   - The two containers on which 'begin' and 'end' are called are the same.
+///   - If the end iterator variable 'g' is defined, it is the same as 'f'.
+StatementMatcher makeIteratorLoopMatcher() {
+  StatementMatcher BeginCallMatcher =
+      cxxMemberCallExpr(
+          argumentCountIs(0),
+          callee(cxxMethodDecl(anyOf(hasName("begin"), hasName("cbegin")))))
+          .bind(BeginCallName);
+
+  DeclarationMatcher InitDeclMatcher =
+      varDecl(hasInitializer(anyOf(ignoringParenImpCasts(BeginCallMatcher),
+                                   materializeTemporaryExpr(
+                                       ignoringParenImpCasts(BeginCallMatcher)),
+                                   hasDescendant(BeginCallMatcher))))
+          .bind(InitVarName);
+
+  DeclarationMatcher EndDeclMatcher =
+      varDecl(hasInitializer(anything())).bind(EndVarName);
+
+  StatementMatcher EndCallMatcher = cxxMemberCallExpr(
+      argumentCountIs(0),
+      callee(cxxMethodDecl(anyOf(hasName("end"), hasName("cend")))));
+
+  StatementMatcher IteratorBoundMatcher =
+      expr(anyOf(ignoringParenImpCasts(
+                     declRefExpr(to(varDecl().bind(ConditionEndVarName)))),
+                 ignoringParenImpCasts(expr(EndCallMatcher).bind(EndCallName)),
+                 materializeTemporaryExpr(ignoringParenImpCasts(
+                     expr(EndCallMatcher).bind(EndCallName)))));
+
+  StatementMatcher IteratorComparisonMatcher = expr(
+      ignoringParenImpCasts(declRefExpr(to(varDecl().bind(ConditionVarName)))));
+
+  auto OverloadedNEQMatcher = ignoringImplicit(
+      cxxOperatorCallExpr(hasOverloadedOperatorName("!="), argumentCountIs(2),
+                          hasArgument(0, IteratorComparisonMatcher),
+                          hasArgument(1, IteratorBoundMatcher)));
+
+  // This matcher tests that a declaration is a CXXRecordDecl that has an
+  // overloaded operator*(). If the operator*() returns by value instead of by
+  // reference then the return type is tagged with DerefByValueResultName.
+  internal::Matcher<VarDecl> TestDerefReturnsByValue =
+      hasType(cxxRecordDecl(hasMethod(allOf(
+          hasOverloadedOperatorName("*"),
+          anyOf(
+              // Tag the return type if it's by value.
+              returns(qualType(unless(hasCanonicalType(referenceType())))
+                          .bind(DerefByValueResultName)),
+              returns(
+                  // Skip loops where the iterator's operator* returns an
+                  // rvalue reference. This is just weird.
+                  qualType(unless(hasCanonicalType(rValueReferenceType())))
+                      .bind(DerefByRefResultName)))))));
+
+  return forStmt(
+             unless(isInTemplateInstantiation()),
+             hasLoopInit(anyOf(declStmt(declCountIs(2),
+                                        containsDeclaration(0, InitDeclMatcher),
+                                        containsDeclaration(1, EndDeclMatcher)),
+                               declStmt(hasSingleDecl(InitDeclMatcher)))),
+             hasCondition(
+                 anyOf(binaryOperator(hasOperatorName("!="),
+                                      hasLHS(IteratorComparisonMatcher),
+                                      hasRHS(IteratorBoundMatcher)),
+                       binaryOperator(hasOperatorName("!="),
+                                      hasLHS(IteratorBoundMatcher),
+                                      hasRHS(IteratorComparisonMatcher)),
+                       OverloadedNEQMatcher)),
+             hasIncrement(anyOf(
+                 unaryOperator(hasOperatorName("++"),
+                               hasUnaryOperand(declRefExpr(
+                                   to(varDecl(hasType(pointsTo(AnyType)))
+                                          .bind(IncrementVarName))))),
+                 cxxOperatorCallExpr(
+                     hasOverloadedOperatorName("++"),
+                     hasArgument(
+                         0, declRefExpr(to(varDecl(TestDerefReturnsByValue)
+                                               .bind(IncrementVarName))))))))
+      .bind(LoopNameIterator);
+}
+
+/// \brief The matcher used for array-like containers (pseudoarrays).
+///
+/// This matcher is more flexible than array-based loops. It will match
+/// loops of the following textual forms (regardless of whether the
+/// iterator type is actually a pointer type or a class type):
+///
+/// Assuming f, g, and h are of type containerType::iterator,
+/// \code
+///   for (int i = 0, j = container.size(); f < g; ++h) { ... }
+///   for (int i = 0; f < container.size(); ++h) { ... }
+/// \endcode
+/// The following string identifiers are bound to the parts of the AST:
+///   InitVarName: 'i' (as a VarDecl)
+///   ConditionVarName: 'f' (as a VarDecl)
+///   LoopName: The entire for loop (as a ForStmt)
+///   In the first example only:
+///     EndVarName: 'j' (as a VarDecl)
+///     ConditionEndVarName: 'g' (as a VarDecl)
+///   In the second example only:
+///     EndCallName: 'container.size()' (as a CXXMemberCallExpr)
+///
+/// Client code will need to make sure that:
+///   - The index variables 'i', 'f', and 'h' are the same.
+///   - The containers on which 'size()' is called is the container indexed.
+///   - The index variable is only used in overloaded operator[] or
+///     container.at().
+///   - If the end iterator variable 'g' is defined, it is the same as 'j'.
+///   - The container's iterators would not be invalidated during the loop.
+StatementMatcher makePseudoArrayLoopMatcher() {
+  // Test that the incoming type has a record declaration that has methods
+  // called 'begin' and 'end'. If the incoming type is const, then make sure
+  // these methods are also marked const.
+  //
+  // FIXME: To be completely thorough this matcher should also ensure the
+  // return type of begin/end is an iterator that dereferences to the same as
+  // what operator[] or at() returns. Such a test isn't likely to fail except
+  // for pathological cases.
+  //
+  // FIXME: Also, a record doesn't necessarily need begin() and end(). Free
+  // functions called begin() and end() taking the container as an argument
+  // are also allowed.
+  TypeMatcher RecordWithBeginEnd = qualType(anyOf(
+      qualType(isConstQualified(),
+               hasDeclaration(cxxRecordDecl(
+                   hasMethod(cxxMethodDecl(hasName("begin"), isConst())),
+                   hasMethod(cxxMethodDecl(hasName("end"),
+                                           isConst())))) // hasDeclaration
+               ),                                        // qualType
+      qualType(
+          unless(isConstQualified()),
+          hasDeclaration(cxxRecordDecl(hasMethod(hasName("begin")),
+                                       hasMethod(hasName("end"))))) // qualType
+      ));
+
+  StatementMatcher SizeCallMatcher = cxxMemberCallExpr(
+      argumentCountIs(0),
+      callee(cxxMethodDecl(anyOf(hasName("size"), hasName("length")))),
+      on(anyOf(hasType(pointsTo(RecordWithBeginEnd)),
+               hasType(RecordWithBeginEnd))));
+
+  StatementMatcher EndInitMatcher =
+      expr(anyOf(ignoringParenImpCasts(expr(SizeCallMatcher).bind(EndCallName)),
+                 explicitCastExpr(hasSourceExpression(ignoringParenImpCasts(
+                     expr(SizeCallMatcher).bind(EndCallName))))));
+
+  DeclarationMatcher EndDeclMatcher =
+      varDecl(hasInitializer(EndInitMatcher)).bind(EndVarName);
+
+  StatementMatcher IndexBoundMatcher =
+      expr(anyOf(ignoringParenImpCasts(declRefExpr(to(
+                     varDecl(hasType(isInteger())).bind(ConditionEndVarName)))),
+                 EndInitMatcher));
+
+  return forStmt(
+             unless(isInTemplateInstantiation()),
+             hasLoopInit(
+                 anyOf(declStmt(declCountIs(2),
+                                containsDeclaration(0, InitToZeroMatcher),
+                                containsDeclaration(1, EndDeclMatcher)),
+                       declStmt(hasSingleDecl(InitToZeroMatcher)))),
+             hasCondition(anyOf(
+                 binaryOperator(hasOperatorName("<"),
+                                hasLHS(IntegerComparisonMatcher),
+                                hasRHS(IndexBoundMatcher)),
+                 binaryOperator(hasOperatorName(">"), hasLHS(IndexBoundMatcher),
+                                hasRHS(IntegerComparisonMatcher)))),
+             hasIncrement(unaryOperator(hasOperatorName("++"),
+                                        hasUnaryOperand(IncrementVarMatcher))))
+      .bind(LoopNamePseudoArray);
+}
+
+/// \brief Determine whether Init appears to be an initializing an iterator.
+///
+/// If it is, returns the object whose begin() or end() method is called, and
+/// the output parameter isArrow is set to indicate whether the initialization
+/// is called via . or ->.
+static const Expr *getContainerFromBeginEndCall(const Expr *Init, bool IsBegin,
+                                                bool *IsArrow) {
+  // FIXME: Maybe allow declaration/initialization outside of the for loop.
+  const auto *TheCall =
+      dyn_cast_or_null<CXXMemberCallExpr>(digThroughConstructors(Init));
+  if (!TheCall || TheCall->getNumArgs() != 0)
+    return nullptr;
+
+  const auto *Member = dyn_cast<MemberExpr>(TheCall->getCallee());
+  if (!Member)
+    return nullptr;
+  StringRef Name = Member->getMemberDecl()->getName();
+  StringRef TargetName = IsBegin ? "begin" : "end";
+  StringRef ConstTargetName = IsBegin ? "cbegin" : "cend";
+  if (Name != TargetName && Name != ConstTargetName)
+    return nullptr;
+
+  const Expr *SourceExpr = Member->getBase();
+  if (!SourceExpr)
+    return nullptr;
+
+  *IsArrow = Member->isArrow();
+  return SourceExpr;
+}
+
+/// \brief Determines the container whose begin() and end() functions are called
+/// for an iterator-based loop.
+///
+/// BeginExpr must be a member call to a function named "begin()", and EndExpr
+/// must be a member.
+static const Expr *findContainer(ASTContext *Context, const Expr *BeginExpr,
+                                 const Expr *EndExpr,
+                                 bool *ContainerNeedsDereference) {
+  // Now that we know the loop variable and test expression, make sure they are
+  // valid.
+  bool BeginIsArrow = false;
+  bool EndIsArrow = false;
+  const Expr *BeginContainerExpr =
+      getContainerFromBeginEndCall(BeginExpr, /*IsBegin=*/true, &BeginIsArrow);
+  if (!BeginContainerExpr)
+    return nullptr;
+
+  const Expr *EndContainerExpr =
+      getContainerFromBeginEndCall(EndExpr, /*IsBegin=*/false, &EndIsArrow);
+  // Disallow loops that try evil things like this (note the dot and arrow):
+  //  for (IteratorType It = Obj.begin(), E = Obj->end(); It != E; ++It) { }
+  if (!EndContainerExpr || BeginIsArrow != EndIsArrow ||
+      !areSameExpr(Context, EndContainerExpr, BeginContainerExpr))
+    return nullptr;
+
+  *ContainerNeedsDereference = BeginIsArrow;
+  return BeginContainerExpr;
+}
+
+/// \brief Obtain the original source code text from a SourceRange.
+static StringRef getStringFromRange(SourceManager &SourceMgr,
+                                    const LangOptions &LangOpts,
+                                    SourceRange Range) {
+  if (SourceMgr.getFileID(Range.getBegin()) !=
+      SourceMgr.getFileID(Range.getEnd())) {
+    return StringRef(); // Empty string.
+  }
+
+  return Lexer::getSourceText(CharSourceRange(Range, true), SourceMgr,
+                              LangOpts);
+}
+
+/// \brief If the given expression is actually a DeclRefExpr or a MemberExpr,
+/// find and return the underlying ValueDecl; otherwise, return NULL.
+static const ValueDecl *getReferencedVariable(const Expr *E) {
+  if (const DeclRefExpr *DRE = getDeclRef(E))
+    return dyn_cast<VarDecl>(DRE->getDecl());
+  if (const auto *Mem = dyn_cast<MemberExpr>(E->IgnoreParenImpCasts()))
+    return dyn_cast<FieldDecl>(Mem->getMemberDecl());
+  return nullptr;
+}
+
+/// \brief Returns true when the given expression is a member expression
+/// whose base is `this` (implicitly or not).
+static bool isDirectMemberExpr(const Expr *E) {
+  if (const auto *Member = dyn_cast<MemberExpr>(E->IgnoreParenImpCasts()))
+    return isa<CXXThisExpr>(Member->getBase()->IgnoreParenImpCasts());
+  return false;
+}
+
+/// \brief Given an expression that represents an usage of an element from the
+/// containter that we are iterating over, returns false when it can be
+/// guaranteed this element cannot be modified as a result of this usage.
+static bool canBeModified(ASTContext *Context, const Expr *E) {
+  if (E->getType().isConstQualified())
+    return false;
+  auto Parents = Context->getParents(*E);
+  if (Parents.size() != 1)
+    return true;
+  if (const auto *Cast = Parents[0].get<ImplicitCastExpr>()) {
+    if ((Cast->getCastKind() == CK_NoOp &&
+         Cast->getType() == E->getType().withConst()) ||
+        (Cast->getCastKind() == CK_LValueToRValue &&
+         !Cast->getType().isNull() && Cast->getType()->isFundamentalType()))
+      return false;
+  }
+  // FIXME: Make this function more generic.
+  return true;
+}
+
+/// \brief Returns true when it can be guaranteed that the elements of the
+/// container are not being modified.
+static bool usagesAreConst(ASTContext *Context, const UsageResult &Usages) {
+  for (const Usage &U : Usages) {
+    // Lambda captures are just redeclarations (VarDecl) of the same variable,
+    // not expressions. If we want to know if a variable that is captured by
+    // reference can be modified in an usage inside the lambda's body, we need
+    // to find the expression corresponding to that particular usage, later in
+    // this loop.
+    if (U.Kind != Usage::UK_CaptureByCopy && U.Kind != Usage::UK_CaptureByRef &&
+        canBeModified(Context, U.Expression))
+      return false;
+  }
+  return true;
+}
+
+/// \brief Returns true if the elements of the container are never accessed
+/// by reference.
+static bool usagesReturnRValues(const UsageResult &Usages) {
+  for (const auto &U : Usages) {
+    if (U.Expression && !U.Expression->isRValue())
+      return false;
+  }
+  return true;
+}
+
+/// \brief Returns true if the container is const-qualified.
+static bool containerIsConst(const Expr *ContainerExpr, bool Dereference) {
+  if (const auto *VDec = getReferencedVariable(ContainerExpr)) {
+    QualType CType = VDec->getType();
+    if (Dereference) {
+      if (!CType->isPointerType())
+        return false;
+      CType = CType->getPointeeType();
+    }
+    // If VDec is a reference to a container, Dereference is false,
+    // but we still need to check the const-ness of the underlying container
+    // type.
+    CType = CType.getNonReferenceType();
+    return CType.isConstQualified();
+  }
+  return false;
+}
+
+LoopConvertCheck::RangeDescriptor::RangeDescriptor()
+    : ContainerNeedsDereference(false), DerefByConstRef(false),
+      DerefByValue(false) {}
+
+LoopConvertCheck::LoopConvertCheck(StringRef Name, ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context), TUInfo(new TUTrackingInfo),
+      MaxCopySize(std::stoull(Options.get("MaxCopySize", "16"))),
+      MinConfidence(StringSwitch<Confidence::Level>(
+                        Options.get("MinConfidence", "reasonable"))
+                        .Case("safe", Confidence::CL_Safe)
+                        .Case("risky", Confidence::CL_Risky)
+                        .Default(Confidence::CL_Reasonable)),
+      NamingStyle(StringSwitch<VariableNamer::NamingStyle>(
+                      Options.get("NamingStyle", "CamelCase"))
+                      .Case("camelBack", VariableNamer::NS_CamelBack)
+                      .Case("lower_case", VariableNamer::NS_LowerCase)
+                      .Case("UPPER_CASE", VariableNamer::NS_UpperCase)
+                      .Default(VariableNamer::NS_CamelCase)) {}
+
+void LoopConvertCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "MaxCopySize", std::to_string(MaxCopySize));
+  SmallVector<std::string, 3> Confs{"risky", "reasonable", "safe"};
+  Options.store(Opts, "MinConfidence", Confs[static_cast<int>(MinConfidence)]);
+
+  SmallVector<std::string, 4> Styles{"camelBack", "CamelCase", "lower_case",
+                                     "UPPER_CASE"};
+  Options.store(Opts, "NamingStyle", Styles[static_cast<int>(NamingStyle)]);
+}
+
+void LoopConvertCheck::registerMatchers(MatchFinder *Finder) {
+  // Only register the matchers for C++. Because this checker is used for
+  // modernization, it is reasonable to run it on any C++ standard with the
+  // assumption the user is trying to modernize their codebase.
+  if (!getLangOpts().CPlusPlus)
+    return;
+
+  Finder->addMatcher(makeArrayLoopMatcher(), this);
+  Finder->addMatcher(makeIteratorLoopMatcher(), this);
+  Finder->addMatcher(makePseudoArrayLoopMatcher(), this);
+}
+
+/// \brief Given the range of a single declaration, such as:
+/// \code
+///   unsigned &ThisIsADeclarationThatCanSpanSeveralLinesOfCode =
+///       InitializationValues[I];
+///   next_instruction;
+/// \endcode
+/// Finds the range that has to be erased to remove this declaration without
+/// leaving empty lines, by extending the range until the beginning of the
+/// next instruction.
+///
+/// We need to delete a potential newline after the deleted alias, as
+/// clang-format will leave empty lines untouched. For all other formatting we
+/// rely on clang-format to fix it.
+void LoopConvertCheck::getAliasRange(SourceManager &SM, SourceRange &Range) {
+  bool Invalid = false;
+  const char *TextAfter =
+      SM.getCharacterData(Range.getEnd().getLocWithOffset(1), &Invalid);
+  if (Invalid)
+    return;
+  unsigned Offset = std::strspn(TextAfter, " \t\r\n");
+  Range =
+      SourceRange(Range.getBegin(), Range.getEnd().getLocWithOffset(Offset));
+}
+
+/// \brief Computes the changes needed to convert a given for loop, and
+/// applies them.
+void LoopConvertCheck::doConversion(
+    ASTContext *Context, const VarDecl *IndexVar,
+    const ValueDecl *MaybeContainer, const UsageResult &Usages,
+    const DeclStmt *AliasDecl, bool AliasUseRequired, bool AliasFromForInit,
+    const ForStmt *Loop, RangeDescriptor Descriptor) {
+  auto Diag = diag(Loop->getForLoc(), "use range-based for loop instead");
+
+  std::string VarName;
+  bool VarNameFromAlias = (Usages.size() == 1) && AliasDecl;
+  bool AliasVarIsRef = false;
+  bool CanCopy = true;
+
+  if (VarNameFromAlias) {
+    const auto *AliasVar = cast<VarDecl>(AliasDecl->getSingleDecl());
+    VarName = AliasVar->getName().str();
+
+    // Use the type of the alias if it's not the same
+    QualType AliasVarType = AliasVar->getType();
+    assert(!AliasVarType.isNull() && "Type in VarDecl is null");
+    if (AliasVarType->isReferenceType()) {
+      AliasVarType = AliasVarType.getNonReferenceType();
+      AliasVarIsRef = true;
+    }
+    if (Descriptor.ElemType.isNull() ||
+        !Context->hasSameUnqualifiedType(AliasVarType, Descriptor.ElemType))
+      Descriptor.ElemType = AliasVarType;
+
+    // We keep along the entire DeclStmt to keep the correct range here.
+    SourceRange ReplaceRange = AliasDecl->getSourceRange();
+
+    std::string ReplacementText;
+    if (AliasUseRequired) {
+      ReplacementText = VarName;
+    } else if (AliasFromForInit) {
+      // FIXME: Clang includes the location of the ';' but only for DeclStmt's
+      // in a for loop's init clause. Need to put this ';' back while removing
+      // the declaration of the alias variable. This is probably a bug.
+      ReplacementText = ";";
+    } else {
+      // Avoid leaving empty lines or trailing whitespaces.
+      getAliasRange(Context->getSourceManager(), ReplaceRange);
+    }
+
+    Diag << FixItHint::CreateReplacement(
+        CharSourceRange::getTokenRange(ReplaceRange), ReplacementText);
+    // No further replacements are made to the loop, since the iterator or index
+    // was used exactly once - in the initialization of AliasVar.
+  } else {
+    VariableNamer Namer(&TUInfo->getGeneratedDecls(),
+                        &TUInfo->getParentFinder().getStmtToParentStmtMap(),
+                        Loop, IndexVar, MaybeContainer, Context, NamingStyle);
+    VarName = Namer.createIndexName();
+    // First, replace all usages of the array subscript expression with our new
+    // variable.
+    for (const auto &Usage : Usages) {
+      std::string ReplaceText;
+      SourceRange Range = Usage.Range;
+      if (Usage.Expression) {
+        // If this is an access to a member through the arrow operator, after
+        // the replacement it must be accessed through the '.' operator.
+        ReplaceText = Usage.Kind == Usage::UK_MemberThroughArrow ? VarName + "."
+                                                                 : VarName;
+        auto Parents = Context->getParents(*Usage.Expression);
+        if (Parents.size() == 1) {
+          if (const auto *Paren = Parents[0].get<ParenExpr>()) {
+            // Usage.Expression will be replaced with the new index variable,
+            // and parenthesis around a simple DeclRefExpr can always be
+            // removed.
+            Range = Paren->getSourceRange();
+          } else if (const auto *UOP = Parents[0].get<UnaryOperator>()) {
+            // If we are taking the address of the loop variable, then we must
+            // not use a copy, as it would mean taking the address of the loop's
+            // local index instead.
+            // FIXME: This won't catch cases where the address is taken outside
+            // of the loop's body (for instance, in a function that got the
+            // loop's index as a const reference parameter), or where we take
+            // the address of a member (like "&Arr[i].A.B.C").
+            if (UOP->getOpcode() == UO_AddrOf)
+              CanCopy = false;
+          }
+        }
+      } else {
+        // The Usage expression is only null in case of lambda captures (which
+        // are VarDecl). If the index is captured by value, add '&' to capture
+        // by reference instead.
+        ReplaceText =
+            Usage.Kind == Usage::UK_CaptureByCopy ? "&" + VarName : VarName;
+      }
+      TUInfo->getReplacedVars().insert(std::make_pair(Loop, IndexVar));
+      Diag << FixItHint::CreateReplacement(
+          CharSourceRange::getTokenRange(Range), ReplaceText);
+    }
+  }
+
+  // Now, we need to construct the new range expression.
+  SourceRange ParenRange(Loop->getLParenLoc(), Loop->getRParenLoc());
+
+  QualType Type = Context->getAutoDeductType();
+  if (!Descriptor.ElemType.isNull() && Descriptor.ElemType->isFundamentalType())
+    Type = Descriptor.ElemType.getUnqualifiedType();
+
+  // If the new variable name is from the aliased variable, then the reference
+  // type for the new variable should only be used if the aliased variable was
+  // declared as a reference.
+  bool IsCheapToCopy =
+      !Descriptor.ElemType.isNull() &&
+      Descriptor.ElemType.isTriviallyCopyableType(*Context) &&
+      // TypeInfo::Width is in bits.
+      Context->getTypeInfo(Descriptor.ElemType).Width <= 8 * MaxCopySize;
+  bool UseCopy = CanCopy && ((VarNameFromAlias && !AliasVarIsRef) ||
+                             (Descriptor.DerefByConstRef && IsCheapToCopy));
+
+  if (!UseCopy) {
+    if (Descriptor.DerefByConstRef) {
+      Type = Context->getLValueReferenceType(Context->getConstType(Type));
+    } else if (Descriptor.DerefByValue) {
+      if (!IsCheapToCopy)
+        Type = Context->getRValueReferenceType(Type);
+    } else {
+      Type = Context->getLValueReferenceType(Type);
+    }
+  }
+
+  StringRef MaybeDereference = Descriptor.ContainerNeedsDereference ? "*" : "";
+  std::string TypeString = Type.getAsString(getLangOpts());
+  std::string Range = ("(" + TypeString + " " + VarName + " : " +
+                       MaybeDereference + Descriptor.ContainerString + ")")
+                          .str();
+  Diag << FixItHint::CreateReplacement(
+      CharSourceRange::getTokenRange(ParenRange), Range);
+  TUInfo->getGeneratedDecls().insert(make_pair(Loop, VarName));
+}
+
+/// \brief Returns a string which refers to the container iterated over.
+StringRef LoopConvertCheck::getContainerString(ASTContext *Context,
+                                               const ForStmt *Loop,
+                                               const Expr *ContainerExpr) {
+  StringRef ContainerString;
+  if (isa<CXXThisExpr>(ContainerExpr->IgnoreParenImpCasts())) {
+    ContainerString = "this";
+  } else {
+    ContainerString =
+        getStringFromRange(Context->getSourceManager(), Context->getLangOpts(),
+                           ContainerExpr->getSourceRange());
+  }
+
+  return ContainerString;
+}
+
+/// \brief Determines what kind of 'auto' must be used after converting a for
+/// loop that iterates over an array or pseudoarray.
+void LoopConvertCheck::getArrayLoopQualifiers(ASTContext *Context,
+                                              const BoundNodes &Nodes,
+                                              const Expr *ContainerExpr,
+                                              const UsageResult &Usages,
+                                              RangeDescriptor &Descriptor) {
+  // On arrays and pseudoarrays, we must figure out the qualifiers from the
+  // usages.
+  if (usagesAreConst(Context, Usages) ||
+      containerIsConst(ContainerExpr, Descriptor.ContainerNeedsDereference)) {
+    Descriptor.DerefByConstRef = true;
+  }
+  if (usagesReturnRValues(Usages)) {
+    // If the index usages (dereference, subscript, at, ...) return rvalues,
+    // then we should not use a reference, because we need to keep the code
+    // correct if it mutates the returned objects.
+    Descriptor.DerefByValue = true;
+  }
+  // Try to find the type of the elements on the container, to check if
+  // they are trivially copyable.
+  for (const Usage &U : Usages) {
+    if (!U.Expression || U.Expression->getType().isNull())
+      continue;
+    QualType Type = U.Expression->getType().getCanonicalType();
+    if (U.Kind == Usage::UK_MemberThroughArrow) {
+      if (!Type->isPointerType()) {
+        continue;
+      }
+      Type = Type->getPointeeType();
+    }
+    Descriptor.ElemType = Type;
+  }
+}
+
+/// \brief Determines what kind of 'auto' must be used after converting an
+/// iterator based for loop.
+void LoopConvertCheck::getIteratorLoopQualifiers(ASTContext *Context,
+                                                 const BoundNodes &Nodes,
+                                                 RangeDescriptor &Descriptor) {
+  // The matchers for iterator loops provide bound nodes to obtain this
+  // information.
+  const auto *InitVar = Nodes.getNodeAs<VarDecl>(InitVarName);
+  QualType CanonicalInitVarType = InitVar->getType().getCanonicalType();
+  const auto *DerefByValueType =
+      Nodes.getNodeAs<QualType>(DerefByValueResultName);
+  Descriptor.DerefByValue = DerefByValueType;
+
+  if (Descriptor.DerefByValue) {
+    // If the dereference operator returns by value then test for the
+    // canonical const qualification of the init variable type.
+    Descriptor.DerefByConstRef = CanonicalInitVarType.isConstQualified();
+    Descriptor.ElemType = *DerefByValueType;
+  } else {
+    if (const auto *DerefType =
+            Nodes.getNodeAs<QualType>(DerefByRefResultName)) {
+      // A node will only be bound with DerefByRefResultName if we're dealing
+      // with a user-defined iterator type. Test the const qualification of
+      // the reference type.
+      auto ValueType = DerefType->getNonReferenceType();
+
+      Descriptor.DerefByConstRef = ValueType.isConstQualified();
+      Descriptor.ElemType = ValueType;
+    } else {
+      // By nature of the matcher this case is triggered only for built-in
+      // iterator types (i.e. pointers).
+      assert(isa<PointerType>(CanonicalInitVarType) &&
+             "Non-class iterator type is not a pointer type");
+
+      // We test for const qualification of the pointed-at type.
+      Descriptor.DerefByConstRef =
+          CanonicalInitVarType->getPointeeType().isConstQualified();
+      Descriptor.ElemType = CanonicalInitVarType->getPointeeType();
+    }
+  }
+}
+
+/// \brief Determines the parameters needed to build the range replacement.
+void LoopConvertCheck::determineRangeDescriptor(
+    ASTContext *Context, const BoundNodes &Nodes, const ForStmt *Loop,
+    LoopFixerKind FixerKind, const Expr *ContainerExpr,
+    const UsageResult &Usages, RangeDescriptor &Descriptor) {
+  Descriptor.ContainerString = getContainerString(Context, Loop, ContainerExpr);
+
+  if (FixerKind == LFK_Iterator)
+    getIteratorLoopQualifiers(Context, Nodes, Descriptor);
+  else
+    getArrayLoopQualifiers(Context, Nodes, ContainerExpr, Usages, Descriptor);
+}
+
+/// \brief Check some of the conditions that must be met for the loop to be
+/// convertible.
+bool LoopConvertCheck::isConvertible(ASTContext *Context,
+                                     const ast_matchers::BoundNodes &Nodes,
+                                     const ForStmt *Loop,
+                                     LoopFixerKind FixerKind) {
+  // If we already modified the range of this for loop, don't do any further
+  // updates on this iteration.
+  if (TUInfo->getReplacedVars().count(Loop))
+    return false;
+
+  // Check that we have exactly one index variable and at most one end variable.
+  const auto *LoopVar = Nodes.getNodeAs<VarDecl>(IncrementVarName);
+  const auto *CondVar = Nodes.getNodeAs<VarDecl>(ConditionVarName);
+  const auto *InitVar = Nodes.getNodeAs<VarDecl>(InitVarName);
+  if (!areSameVariable(LoopVar, CondVar) || !areSameVariable(LoopVar, InitVar))
+    return false;
+  const auto *EndVar = Nodes.getNodeAs<VarDecl>(EndVarName);
+  const auto *ConditionEndVar = Nodes.getNodeAs<VarDecl>(ConditionEndVarName);
+  if (EndVar && !areSameVariable(EndVar, ConditionEndVar))
+    return false;
+
+  // FIXME: Try to put most of this logic inside a matcher.
+  if (FixerKind == LFK_Iterator) {
+    QualType InitVarType = InitVar->getType();
+    QualType CanonicalInitVarType = InitVarType.getCanonicalType();
+
+    const auto *BeginCall = Nodes.getNodeAs<CXXMemberCallExpr>(BeginCallName);
+    assert(BeginCall && "Bad Callback. No begin call expression");
+    QualType CanonicalBeginType =
+        BeginCall->getMethodDecl()->getReturnType().getCanonicalType();
+    if (CanonicalBeginType->isPointerType() &&
+        CanonicalInitVarType->isPointerType()) {
+      // If the initializer and the variable are both pointers check if the
+      // un-qualified pointee types match, otherwise we don't use auto.
+      if (!Context->hasSameUnqualifiedType(
+              CanonicalBeginType->getPointeeType(),
+              CanonicalInitVarType->getPointeeType()))
+        return false;
+    } else if (!Context->hasSameType(CanonicalInitVarType,
+                                     CanonicalBeginType)) {
+      // Check for qualified types to avoid conversions from non-const to const
+      // iterator types.
+      return false;
+    }
+  } else if (FixerKind == LFK_PseudoArray) {
+    // This call is required to obtain the container.
+    const auto *EndCall = Nodes.getNodeAs<CXXMemberCallExpr>(EndCallName);
+    if (!EndCall || !dyn_cast<MemberExpr>(EndCall->getCallee()))
+      return false;
+  }
+  return true;
+}
+
+void LoopConvertCheck::check(const MatchFinder::MatchResult &Result) {
+  const BoundNodes &Nodes = Result.Nodes;
+  Confidence ConfidenceLevel(Confidence::CL_Safe);
+  ASTContext *Context = Result.Context;
+
+  const ForStmt *Loop;
+  LoopFixerKind FixerKind;
+  RangeDescriptor Descriptor;
+
+  if ((Loop = Nodes.getNodeAs<ForStmt>(LoopNameArray))) {
+    FixerKind = LFK_Array;
+  } else if ((Loop = Nodes.getNodeAs<ForStmt>(LoopNameIterator))) {
+    FixerKind = LFK_Iterator;
+  } else {
+    Loop = Nodes.getNodeAs<ForStmt>(LoopNamePseudoArray);
+    assert(Loop && "Bad Callback. No for statement");
+    FixerKind = LFK_PseudoArray;
+  }
+
+  if (!isConvertible(Context, Nodes, Loop, FixerKind))
+    return;
+
+  const auto *LoopVar = Nodes.getNodeAs<VarDecl>(IncrementVarName);
+  const auto *EndVar = Nodes.getNodeAs<VarDecl>(EndVarName);
+
+  // If the loop calls end()/size() after each iteration, lower our confidence
+  // level.
+  if (FixerKind != LFK_Array && !EndVar)
+    ConfidenceLevel.lowerTo(Confidence::CL_Reasonable);
+
+  // If the end comparison isn't a variable, we can try to work with the
+  // expression the loop variable is being tested against instead.
+  const auto *EndCall = Nodes.getNodeAs<CXXMemberCallExpr>(EndCallName);
+  const auto *BoundExpr = Nodes.getNodeAs<Expr>(ConditionBoundName);
+
+  // Find container expression of iterators and pseudoarrays, and determine if
+  // this expression needs to be dereferenced to obtain the container.
+  // With array loops, the container is often discovered during the
+  // ForLoopIndexUseVisitor traversal.
+  const Expr *ContainerExpr = nullptr;
+  if (FixerKind == LFK_Iterator) {
+    ContainerExpr = findContainer(Context, LoopVar->getInit(),
+                                  EndVar ? EndVar->getInit() : EndCall,
+                                  &Descriptor.ContainerNeedsDereference);
+  } else if (FixerKind == LFK_PseudoArray) {
+    ContainerExpr = EndCall->getImplicitObjectArgument();
+    Descriptor.ContainerNeedsDereference =
+        dyn_cast<MemberExpr>(EndCall->getCallee())->isArrow();
+  }
+
+  // We must know the container or an array length bound.
+  if (!ContainerExpr && !BoundExpr)
+    return;
+
+  ForLoopIndexUseVisitor Finder(Context, LoopVar, EndVar, ContainerExpr,
+                                BoundExpr,
+                                Descriptor.ContainerNeedsDereference);
+
+  // Find expressions and variables on which the container depends.
+  if (ContainerExpr) {
+    ComponentFinderASTVisitor ComponentFinder;
+    ComponentFinder.findExprComponents(ContainerExpr->IgnoreParenImpCasts());
+    Finder.addComponents(ComponentFinder.getComponents());
+  }
+
+  // Find usages of the loop index. If they are not used in a convertible way,
+  // stop here.
+  if (!Finder.findAndVerifyUsages(Loop->getBody()))
+    return;
+  ConfidenceLevel.lowerTo(Finder.getConfidenceLevel());
+
+  // Obtain the container expression, if we don't have it yet.
+  if (FixerKind == LFK_Array) {
+    ContainerExpr = Finder.getContainerIndexed()->IgnoreParenImpCasts();
+
+    // Very few loops are over expressions that generate arrays rather than
+    // array variables. Consider loops over arrays that aren't just represented
+    // by a variable to be risky conversions.
+    if (!getReferencedVariable(ContainerExpr) &&
+        !isDirectMemberExpr(ContainerExpr))
+      ConfidenceLevel.lowerTo(Confidence::CL_Risky);
+  }
+
+  // Find out which qualifiers we have to use in the loop range.
+  const UsageResult &Usages = Finder.getUsages();
+  determineRangeDescriptor(Context, Nodes, Loop, FixerKind, ContainerExpr,
+                           Usages, Descriptor);
+
+  // Ensure that we do not try to move an expression dependent on a local
+  // variable declared inside the loop outside of it.
+  // FIXME: Determine when the external dependency isn't an expression converted
+  // by another loop.
+  TUInfo->getParentFinder().gatherAncestors(Context->getTranslationUnitDecl());
+  DependencyFinderASTVisitor DependencyFinder(
+      &TUInfo->getParentFinder().getStmtToParentStmtMap(),
+      &TUInfo->getParentFinder().getDeclToParentStmtMap(),
+      &TUInfo->getReplacedVars(), Loop);
+
+  if (DependencyFinder.dependsOnInsideVariable(ContainerExpr) ||
+      Descriptor.ContainerString.empty() || Usages.empty() ||
+      ConfidenceLevel.getLevel() < MinConfidence)
+    return;
+
+  doConversion(Context, LoopVar, getReferencedVariable(ContainerExpr), Usages,
+               Finder.getAliasDecl(), Finder.aliasUseRequired(),
+               Finder.aliasFromForInit(), Loop, Descriptor);
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/modernize/LoopConvertCheck.h b/clang-tidy/modernize/LoopConvertCheck.h
new file mode 100644 (file)
index 0000000..75ab25a
--- /dev/null
@@ -0,0 +1,78 @@
+//===--- LoopConvertCheck.h - clang-tidy-------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_LOOP_CONVERT_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_LOOP_CONVERT_H
+
+#include "../ClangTidy.h"
+#include "LoopConvertUtils.h"
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+class LoopConvertCheck : public ClangTidyCheck {
+public:
+  LoopConvertCheck(StringRef Name, ClangTidyContext *Context);
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+  struct RangeDescriptor {
+    RangeDescriptor();
+    bool ContainerNeedsDereference;
+    bool DerefByConstRef;
+    bool DerefByValue;
+    std::string ContainerString;
+    QualType ElemType;
+  };
+
+  void getAliasRange(SourceManager &SM, SourceRange &DeclRange);
+
+  void doConversion(ASTContext *Context, const VarDecl *IndexVar,
+                    const ValueDecl *MaybeContainer, const UsageResult &Usages,
+                    const DeclStmt *AliasDecl, bool AliasUseRequired,
+                    bool AliasFromForInit, const ForStmt *Loop,
+                    RangeDescriptor Descriptor);
+
+  StringRef getContainerString(ASTContext *Context, const ForStmt *Loop,
+                               const Expr *ContainerExpr);
+
+  void getArrayLoopQualifiers(ASTContext *Context,
+                              const ast_matchers::BoundNodes &Nodes,
+                              const Expr *ContainerExpr,
+                              const UsageResult &Usages,
+                              RangeDescriptor &Descriptor);
+
+  void getIteratorLoopQualifiers(ASTContext *Context,
+                                 const ast_matchers::BoundNodes &Nodes,
+                                 RangeDescriptor &Descriptor);
+
+  void determineRangeDescriptor(ASTContext *Context,
+                                const ast_matchers::BoundNodes &Nodes,
+                                const ForStmt *Loop, LoopFixerKind FixerKind,
+                                const Expr *ContainerExpr,
+                                const UsageResult &Usages,
+                                RangeDescriptor &Descriptor);
+
+  bool isConvertible(ASTContext *Context, const ast_matchers::BoundNodes &Nodes,
+                     const ForStmt *Loop, LoopFixerKind FixerKind);
+
+  std::unique_ptr<TUTrackingInfo> TUInfo;
+  const unsigned long long MaxCopySize;
+  const Confidence::Level MinConfidence;
+  const VariableNamer::NamingStyle NamingStyle;
+};
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_LOOP_CONVERT_H
diff --git a/clang-tidy/modernize/LoopConvertUtils.cpp b/clang-tidy/modernize/LoopConvertUtils.cpp
new file mode 100644 (file)
index 0000000..f65f7a1
--- /dev/null
@@ -0,0 +1,909 @@
+//===--- LoopConvertUtils.cpp - clang-tidy --------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "LoopConvertUtils.h"
+#include "clang/Basic/IdentifierTable.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/Basic/Lambda.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/TokenKinds.h"
+#include "clang/Lex/Lexer.h"
+#include "llvm/ADT/APSInt.h"
+#include "llvm/ADT/FoldingSet.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Casting.h"
+#include <algorithm>
+#include <cassert>
+#include <cstddef>
+#include <string>
+#include <utility>
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+/// \brief Tracks a stack of parent statements during traversal.
+///
+/// All this really does is inject push_back() before running
+/// RecursiveASTVisitor::TraverseStmt() and pop_back() afterwards. The Stmt atop
+/// the stack is the parent of the current statement (NULL for the topmost
+/// statement).
+bool StmtAncestorASTVisitor::TraverseStmt(Stmt *Statement) {
+  StmtAncestors.insert(std::make_pair(Statement, StmtStack.back()));
+  StmtStack.push_back(Statement);
+  RecursiveASTVisitor<StmtAncestorASTVisitor>::TraverseStmt(Statement);
+  StmtStack.pop_back();
+  return true;
+}
+
+/// \brief Keep track of the DeclStmt associated with each VarDecl.
+///
+/// Combined with StmtAncestors, this provides roughly the same information as
+/// Scope, as we can map a VarDecl to its DeclStmt, then walk up the parent tree
+/// using StmtAncestors.
+bool StmtAncestorASTVisitor::VisitDeclStmt(DeclStmt *Decls) {
+  for (const auto *decl : Decls->decls()) {
+    if (const auto *V = dyn_cast<VarDecl>(decl))
+      DeclParents.insert(std::make_pair(V, Decls));
+  }
+  return true;
+}
+
+/// \brief record the DeclRefExpr as part of the parent expression.
+bool ComponentFinderASTVisitor::VisitDeclRefExpr(DeclRefExpr *E) {
+  Components.push_back(E);
+  return true;
+}
+
+/// \brief record the MemberExpr as part of the parent expression.
+bool ComponentFinderASTVisitor::VisitMemberExpr(MemberExpr *Member) {
+  Components.push_back(Member);
+  return true;
+}
+
+/// \brief Forward any DeclRefExprs to a check on the referenced variable
+/// declaration.
+bool DependencyFinderASTVisitor::VisitDeclRefExpr(DeclRefExpr *DeclRef) {
+  if (auto *V = dyn_cast_or_null<VarDecl>(DeclRef->getDecl()))
+    return VisitVarDecl(V);
+  return true;
+}
+
+/// \brief Determine if any this variable is declared inside the ContainingStmt.
+bool DependencyFinderASTVisitor::VisitVarDecl(VarDecl *V) {
+  const Stmt *Curr = DeclParents->lookup(V);
+  // First, see if the variable was declared within an inner scope of the loop.
+  while (Curr != nullptr) {
+    if (Curr == ContainingStmt) {
+      DependsOnInsideVariable = true;
+      return false;
+    }
+    Curr = StmtParents->lookup(Curr);
+  }
+
+  // Next, check if the variable was removed from existence by an earlier
+  // iteration.
+  for (const auto &I : *ReplacedVars) {
+    if (I.second == V) {
+      DependsOnInsideVariable = true;
+      return false;
+    }
+  }
+  return true;
+}
+
+/// \brief If we already created a variable for TheLoop, check to make sure
+/// that the name was not already taken.
+bool DeclFinderASTVisitor::VisitForStmt(ForStmt *TheLoop) {
+  StmtGeneratedVarNameMap::const_iterator I = GeneratedDecls->find(TheLoop);
+  if (I != GeneratedDecls->end() && I->second == Name) {
+    Found = true;
+    return false;
+  }
+  return true;
+}
+
+/// \brief If any named declaration within the AST subtree has the same name,
+/// then consider Name already taken.
+bool DeclFinderASTVisitor::VisitNamedDecl(NamedDecl *D) {
+  const IdentifierInfo *Ident = D->getIdentifier();
+  if (Ident && Ident->getName() == Name) {
+    Found = true;
+    return false;
+  }
+  return true;
+}
+
+/// \brief Forward any declaration references to the actual check on the
+/// referenced declaration.
+bool DeclFinderASTVisitor::VisitDeclRefExpr(DeclRefExpr *DeclRef) {
+  if (auto *D = dyn_cast<NamedDecl>(DeclRef->getDecl()))
+    return VisitNamedDecl(D);
+  return true;
+}
+
+/// \brief If the new variable name conflicts with any type used in the loop,
+/// then we mark that variable name as taken.
+bool DeclFinderASTVisitor::VisitTypeLoc(TypeLoc TL) {
+  QualType QType = TL.getType();
+
+  // Check if our name conflicts with a type, to handle for typedefs.
+  if (QType.getAsString() == Name) {
+    Found = true;
+    return false;
+  }
+  // Check for base type conflicts. For example, when a struct is being
+  // referenced in the body of the loop, the above getAsString() will return the
+  // whole type (ex. "struct s"), but will be caught here.
+  if (const IdentifierInfo *Ident = QType.getBaseTypeIdentifier()) {
+    if (Ident->getName() == Name) {
+      Found = true;
+      return false;
+    }
+  }
+  return true;
+}
+
+/// \brief Look through conversion/copy constructors to find the explicit
+/// initialization expression, returning it is found.
+///
+/// The main idea is that given
+///   vector<int> v;
+/// we consider either of these initializations
+///   vector<int>::iterator it = v.begin();
+///   vector<int>::iterator it(v.begin());
+/// and retrieve `v.begin()` as the expression used to initialize `it` but do
+/// not include
+///   vector<int>::iterator it;
+///   vector<int>::iterator it(v.begin(), 0); // if this constructor existed
+/// as being initialized from `v.begin()`
+const Expr *digThroughConstructors(const Expr *E) {
+  if (!E)
+    return nullptr;
+  E = E->IgnoreImplicit();
+  if (const auto *ConstructExpr = dyn_cast<CXXConstructExpr>(E)) {
+    // The initial constructor must take exactly one parameter, but base class
+    // and deferred constructors can take more.
+    if (ConstructExpr->getNumArgs() != 1 ||
+        ConstructExpr->getConstructionKind() != CXXConstructExpr::CK_Complete)
+      return nullptr;
+    E = ConstructExpr->getArg(0);
+    if (const auto *Temp = dyn_cast<MaterializeTemporaryExpr>(E))
+      E = Temp->GetTemporaryExpr();
+    return digThroughConstructors(E);
+  }
+  return E;
+}
+
+/// \brief Returns true when two Exprs are equivalent.
+bool areSameExpr(ASTContext *Context, const Expr *First, const Expr *Second) {
+  if (!First || !Second)
+    return false;
+
+  llvm::FoldingSetNodeID FirstID, SecondID;
+  First->Profile(FirstID, *Context, true);
+  Second->Profile(SecondID, *Context, true);
+  return FirstID == SecondID;
+}
+
+/// \brief Returns the DeclRefExpr represented by E, or NULL if there isn't one.
+const DeclRefExpr *getDeclRef(const Expr *E) {
+  return dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts());
+}
+
+/// \brief Returns true when two ValueDecls are the same variable.
+bool areSameVariable(const ValueDecl *First, const ValueDecl *Second) {
+  return First && Second &&
+         First->getCanonicalDecl() == Second->getCanonicalDecl();
+}
+
+/// \brief Determines if an expression is a declaration reference to a
+/// particular variable.
+static bool exprReferencesVariable(const ValueDecl *Target, const Expr *E) {
+  if (!Target || !E)
+    return false;
+  const DeclRefExpr *Decl = getDeclRef(E);
+  return Decl && areSameVariable(Target, Decl->getDecl());
+}
+
+/// \brief If the expression is a dereference or call to operator*(), return the
+/// operand. Otherwise, return NULL.
+static const Expr *getDereferenceOperand(const Expr *E) {
+  if (const auto *Uop = dyn_cast<UnaryOperator>(E))
+    return Uop->getOpcode() == UO_Deref ? Uop->getSubExpr() : nullptr;
+
+  if (const auto *OpCall = dyn_cast<CXXOperatorCallExpr>(E)) {
+    return OpCall->getOperator() == OO_Star && OpCall->getNumArgs() == 1
+               ? OpCall->getArg(0)
+               : nullptr;
+  }
+
+  return nullptr;
+}
+
+/// \brief Returns true when the Container contains an Expr equivalent to E.
+template <typename ContainerT>
+static bool containsExpr(ASTContext *Context, const ContainerT *Container,
+                         const Expr *E) {
+  llvm::FoldingSetNodeID ID;
+  E->Profile(ID, *Context, true);
+  for (const auto &I : *Container) {
+    if (ID == I.second)
+      return true;
+  }
+  return false;
+}
+
+/// \brief Returns true when the index expression is a declaration reference to
+/// IndexVar.
+///
+/// If the index variable is `index`, this function returns true on
+///    arrayExpression[index];
+///    containerExpression[index];
+/// but not
+///    containerExpression[notIndex];
+static bool isIndexInSubscriptExpr(const Expr *IndexExpr,
+                                   const VarDecl *IndexVar) {
+  const DeclRefExpr *Idx = getDeclRef(IndexExpr);
+  return Idx && Idx->getType()->isIntegerType() &&
+         areSameVariable(IndexVar, Idx->getDecl());
+}
+
+/// \brief Returns true when the index expression is a declaration reference to
+/// IndexVar, Obj is the same expression as SourceExpr after all parens and
+/// implicit casts are stripped off.
+///
+/// If PermitDeref is true, IndexExpression may
+/// be a dereference (overloaded or builtin operator*).
+///
+/// This function is intended for array-like containers, as it makes sure that
+/// both the container and the index match.
+/// If the loop has index variable `index` and iterates over `container`, then
+/// isIndexInSubscriptExpr returns true for
+/// \code
+///   container[index]
+///   container.at(index)
+///   container->at(index)
+/// \endcode
+/// but not for
+/// \code
+///   container[notIndex]
+///   notContainer[index]
+/// \endcode
+/// If PermitDeref is true, then isIndexInSubscriptExpr additionally returns
+/// true on these expressions:
+/// \code
+///   (*container)[index]
+///   (*container).at(index)
+/// \endcode
+static bool isIndexInSubscriptExpr(ASTContext *Context, const Expr *IndexExpr,
+                                   const VarDecl *IndexVar, const Expr *Obj,
+                                   const Expr *SourceExpr, bool PermitDeref) {
+  if (!SourceExpr || !Obj || !isIndexInSubscriptExpr(IndexExpr, IndexVar))
+    return false;
+
+  if (areSameExpr(Context, SourceExpr->IgnoreParenImpCasts(),
+                  Obj->IgnoreParenImpCasts()))
+    return true;
+
+  if (const Expr *InnerObj = getDereferenceOperand(Obj->IgnoreParenImpCasts()))
+    if (PermitDeref && areSameExpr(Context, SourceExpr->IgnoreParenImpCasts(),
+                                   InnerObj->IgnoreParenImpCasts()))
+      return true;
+
+  return false;
+}
+
+/// \brief Returns true when Opcall is a call a one-parameter dereference of
+/// IndexVar.
+///
+/// For example, if the index variable is `index`, returns true for
+///   *index
+/// but not
+///   index
+///   *notIndex
+static bool isDereferenceOfOpCall(const CXXOperatorCallExpr *OpCall,
+                                  const VarDecl *IndexVar) {
+  return OpCall->getOperator() == OO_Star && OpCall->getNumArgs() == 1 &&
+         exprReferencesVariable(IndexVar, OpCall->getArg(0));
+}
+
+/// \brief Returns true when Uop is a dereference of IndexVar.
+///
+/// For example, if the index variable is `index`, returns true for
+///   *index
+/// but not
+///   index
+///   *notIndex
+static bool isDereferenceOfUop(const UnaryOperator *Uop,
+                               const VarDecl *IndexVar) {
+  return Uop->getOpcode() == UO_Deref &&
+         exprReferencesVariable(IndexVar, Uop->getSubExpr());
+}
+
+/// \brief Determines whether the given Decl defines a variable initialized to
+/// the loop object.
+///
+/// This is intended to find cases such as
+/// \code
+///   for (int i = 0; i < arraySize(arr); ++i) {
+///     T t = arr[i];
+///     // use t, do not use i
+///   }
+/// \endcode
+/// and
+/// \code
+///   for (iterator i = container.begin(), e = container.end(); i != e; ++i) {
+///     T t = *i;
+///     // use t, do not use i
+///   }
+/// \endcode
+static bool isAliasDecl(ASTContext *Context, const Decl *TheDecl,
+                        const VarDecl *IndexVar) {
+  const auto *VDecl = dyn_cast<VarDecl>(TheDecl);
+  if (!VDecl)
+    return false;
+  if (!VDecl->hasInit())
+    return false;
+
+  bool OnlyCasts = true;
+  const Expr *Init = VDecl->getInit()->IgnoreParenImpCasts();
+  if (Init && isa<CXXConstructExpr>(Init)) {
+    Init = digThroughConstructors(Init);
+    OnlyCasts = false;
+  }
+  if (!Init)
+    return false;
+
+  // Check that the declared type is the same as (or a reference to) the
+  // container type.
+  if (!OnlyCasts) {
+    QualType InitType = Init->getType();
+    QualType DeclarationType = VDecl->getType();
+    if (!DeclarationType.isNull() && DeclarationType->isReferenceType())
+      DeclarationType = DeclarationType.getNonReferenceType();
+
+    if (InitType.isNull() || DeclarationType.isNull() ||
+        !Context->hasSameUnqualifiedType(DeclarationType, InitType))
+      return false;
+  }
+
+  switch (Init->getStmtClass()) {
+  case Stmt::ArraySubscriptExprClass: {
+    const auto *E = cast<ArraySubscriptExpr>(Init);
+    // We don't really care which array is used here. We check to make sure
+    // it was the correct one later, since the AST will traverse it next.
+    return isIndexInSubscriptExpr(E->getIdx(), IndexVar);
+  }
+
+  case Stmt::UnaryOperatorClass:
+    return isDereferenceOfUop(cast<UnaryOperator>(Init), IndexVar);
+
+  case Stmt::CXXOperatorCallExprClass: {
+    const auto *OpCall = cast<CXXOperatorCallExpr>(Init);
+    if (OpCall->getOperator() == OO_Star)
+      return isDereferenceOfOpCall(OpCall, IndexVar);
+    if (OpCall->getOperator() == OO_Subscript) {
+      assert(OpCall->getNumArgs() == 2);
+      return isIndexInSubscriptExpr(OpCall->getArg(1), IndexVar);
+    }
+    break;
+  }
+
+  case Stmt::CXXMemberCallExprClass: {
+    const auto *MemCall = cast<CXXMemberCallExpr>(Init);
+    // This check is needed because getMethodDecl can return nullptr if the
+    // callee is a member function pointer.
+    const auto *MDecl = MemCall->getMethodDecl();
+    if (MDecl && !isa<CXXConversionDecl>(MDecl) &&
+        MDecl->getNameAsString() == "at" && MemCall->getNumArgs() == 1) {
+      return isIndexInSubscriptExpr(MemCall->getArg(0), IndexVar);
+    }
+    return false;
+  }
+
+  default:
+    break;
+  }
+  return false;
+}
+
+/// \brief Determines whether the bound of a for loop condition expression is
+/// the same as the statically computable size of ArrayType.
+///
+/// Given
+/// \code
+///   const int N = 5;
+///   int arr[N];
+/// \endcode
+/// This is intended to permit
+/// \code
+///   for (int i = 0; i < N; ++i) {  /* use arr[i] */ }
+///   for (int i = 0; i < arraysize(arr); ++i) { /* use arr[i] */ }
+/// \endcode
+static bool arrayMatchesBoundExpr(ASTContext *Context,
+                                  const QualType &ArrayType,
+                                  const Expr *ConditionExpr) {
+  if (!ConditionExpr || ConditionExpr->isValueDependent())
+    return false;
+  const ConstantArrayType *ConstType =
+      Context->getAsConstantArrayType(ArrayType);
+  if (!ConstType)
+    return false;
+  llvm::APSInt ConditionSize;
+  if (!ConditionExpr->isIntegerConstantExpr(ConditionSize, *Context))
+    return false;
+  llvm::APSInt ArraySize(ConstType->getSize());
+  return llvm::APSInt::isSameValue(ConditionSize, ArraySize);
+}
+
+ForLoopIndexUseVisitor::ForLoopIndexUseVisitor(ASTContext *Context,
+                                               const VarDecl *IndexVar,
+                                               const VarDecl *EndVar,
+                                               const Expr *ContainerExpr,
+                                               const Expr *ArrayBoundExpr,
+                                               bool ContainerNeedsDereference)
+    : Context(Context), IndexVar(IndexVar), EndVar(EndVar),
+      ContainerExpr(ContainerExpr), ArrayBoundExpr(ArrayBoundExpr),
+      ContainerNeedsDereference(ContainerNeedsDereference),
+      OnlyUsedAsIndex(true), AliasDecl(nullptr),
+      ConfidenceLevel(Confidence::CL_Safe), NextStmtParent(nullptr),
+      CurrStmtParent(nullptr), ReplaceWithAliasUse(false),
+      AliasFromForInit(false) {
+  if (ContainerExpr)
+    addComponent(ContainerExpr);
+}
+
+bool ForLoopIndexUseVisitor::findAndVerifyUsages(const Stmt *Body) {
+  TraverseStmt(const_cast<Stmt *>(Body));
+  return OnlyUsedAsIndex && ContainerExpr;
+}
+
+void ForLoopIndexUseVisitor::addComponents(const ComponentVector &Components) {
+  // FIXME: add sort(on ID)+unique to avoid extra work.
+  for (const auto &I : Components)
+    addComponent(I);
+}
+
+void ForLoopIndexUseVisitor::addComponent(const Expr *E) {
+  llvm::FoldingSetNodeID ID;
+  const Expr *Node = E->IgnoreParenImpCasts();
+  Node->Profile(ID, *Context, true);
+  DependentExprs.push_back(std::make_pair(Node, ID));
+}
+
+void ForLoopIndexUseVisitor::addUsage(const Usage &U) {
+  SourceLocation Begin = U.Range.getBegin();
+  if (Begin.isMacroID())
+    Begin = Context->getSourceManager().getSpellingLoc(Begin);
+
+  if (UsageLocations.insert(Begin).second)
+    Usages.push_back(U);
+}
+
+/// \brief If the unary operator is a dereference of IndexVar, include it
+/// as a valid usage and prune the traversal.
+///
+/// For example, if container.begin() and container.end() both return pointers
+/// to int, this makes sure that the initialization for `k` is not counted as an
+/// unconvertible use of the iterator `i`.
+/// \code
+///   for (int *i = container.begin(), *e = container.end(); i != e; ++i) {
+///     int k = *i + 2;
+///   }
+/// \endcode
+bool ForLoopIndexUseVisitor::TraverseUnaryDeref(UnaryOperator *Uop) {
+  // If we dereference an iterator that's actually a pointer, count the
+  // occurrence.
+  if (isDereferenceOfUop(Uop, IndexVar)) {
+    addUsage(Usage(Uop));
+    return true;
+  }
+
+  return VisitorBase::TraverseUnaryOperator(Uop);
+}
+
+/// \brief If the member expression is operator-> (overloaded or not) on
+/// IndexVar, include it as a valid usage and prune the traversal.
+///
+/// For example, given
+/// \code
+///   struct Foo { int bar(); int x; };
+///   vector<Foo> v;
+/// \endcode
+/// the following uses will be considered convertible:
+/// \code
+///   for (vector<Foo>::iterator i = v.begin(), e = v.end(); i != e; ++i) {
+///     int b = i->bar();
+///     int k = i->x + 1;
+///   }
+/// \endcode
+/// though
+/// \code
+///   for (vector<Foo>::iterator i = v.begin(), e = v.end(); i != e; ++i) {
+///     int k = i.insert(1);
+///   }
+///   for (vector<Foo>::iterator i = v.begin(), e = v.end(); i != e; ++i) {
+///     int b = e->bar();
+///   }
+/// \endcode
+/// will not.
+bool ForLoopIndexUseVisitor::TraverseMemberExpr(MemberExpr *Member) {
+  const Expr *Base = Member->getBase();
+  const DeclRefExpr *Obj = getDeclRef(Base);
+  const Expr *ResultExpr = Member;
+  QualType ExprType;
+  if (const auto *Call =
+          dyn_cast<CXXOperatorCallExpr>(Base->IgnoreParenImpCasts())) {
+    // If operator->() is a MemberExpr containing a CXXOperatorCallExpr, then
+    // the MemberExpr does not have the expression we want. We therefore catch
+    // that instance here.
+    // For example, if vector<Foo>::iterator defines operator->(), then the
+    // example `i->bar()` at the top of this function is a CXXMemberCallExpr
+    // referring to `i->` as the member function called. We want just `i`, so
+    // we take the argument to operator->() as the base object.
+    if (Call->getOperator() == OO_Arrow) {
+      assert(Call->getNumArgs() == 1 &&
+             "Operator-> takes more than one argument");
+      Obj = getDeclRef(Call->getArg(0));
+      ResultExpr = Obj;
+      ExprType = Call->getCallReturnType(*Context);
+    }
+  }
+
+  if (Obj && exprReferencesVariable(IndexVar, Obj)) {
+    // Member calls on the iterator with '.' are not allowed.
+    if (!Member->isArrow()) {
+      OnlyUsedAsIndex = false;
+      return true;
+    }
+
+    if (ExprType.isNull())
+      ExprType = Obj->getType();
+
+    if (!ExprType->isPointerType())
+      return false;
+
+    // FIXME: This works around not having the location of the arrow operator.
+    // Consider adding OperatorLoc to MemberExpr?
+    SourceLocation ArrowLoc = Lexer::getLocForEndOfToken(
+        Base->getExprLoc(), 0, Context->getSourceManager(),
+        Context->getLangOpts());
+    // If something complicated is happening (i.e. the next token isn't an
+    // arrow), give up on making this work.
+    if (ArrowLoc.isValid()) {
+      addUsage(Usage(ResultExpr, Usage::UK_MemberThroughArrow,
+                     SourceRange(Base->getExprLoc(), ArrowLoc)));
+      return true;
+    }
+  }
+  return VisitorBase::TraverseMemberExpr(Member);
+}
+
+/// \brief If a member function call is the at() accessor on the container with
+/// IndexVar as the single argument, include it as a valid usage and prune
+/// the traversal.
+///
+/// Member calls on other objects will not be permitted.
+/// Calls on the iterator object are not permitted, unless done through
+/// operator->(). The one exception is allowing vector::at() for pseudoarrays.
+bool ForLoopIndexUseVisitor::TraverseCXXMemberCallExpr(
+    CXXMemberCallExpr *MemberCall) {
+  auto *Member =
+      dyn_cast<MemberExpr>(MemberCall->getCallee()->IgnoreParenImpCasts());
+  if (!Member)
+    return VisitorBase::TraverseCXXMemberCallExpr(MemberCall);
+
+  // We specifically allow an accessor named "at" to let STL in, though
+  // this is restricted to pseudo-arrays by requiring a single, integer
+  // argument.
+  const IdentifierInfo *Ident = Member->getMemberDecl()->getIdentifier();
+  if (Ident && Ident->isStr("at") && MemberCall->getNumArgs() == 1) {
+    if (isIndexInSubscriptExpr(Context, MemberCall->getArg(0), IndexVar,
+                               Member->getBase(), ContainerExpr,
+                               ContainerNeedsDereference)) {
+      addUsage(Usage(MemberCall));
+      return true;
+    }
+  }
+
+  if (containsExpr(Context, &DependentExprs, Member->getBase()))
+    ConfidenceLevel.lowerTo(Confidence::CL_Risky);
+
+  return VisitorBase::TraverseCXXMemberCallExpr(MemberCall);
+}
+
+/// \brief If an overloaded operator call is a dereference of IndexVar or
+/// a subscript of the container with IndexVar as the single argument,
+/// include it as a valid usage and prune the traversal.
+///
+/// For example, given
+/// \code
+///   struct Foo { int bar(); int x; };
+///   vector<Foo> v;
+///   void f(Foo);
+/// \endcode
+/// the following uses will be considered convertible:
+/// \code
+///   for (vector<Foo>::iterator i = v.begin(), e = v.end(); i != e; ++i) {
+///     f(*i);
+///   }
+///   for (int i = 0; i < v.size(); ++i) {
+///      int i = v[i] + 1;
+///   }
+/// \endcode
+bool ForLoopIndexUseVisitor::TraverseCXXOperatorCallExpr(
+    CXXOperatorCallExpr *OpCall) {
+  switch (OpCall->getOperator()) {
+  case OO_Star:
+    if (isDereferenceOfOpCall(OpCall, IndexVar)) {
+      addUsage(Usage(OpCall));
+      return true;
+    }
+    break;
+
+  case OO_Subscript:
+    if (OpCall->getNumArgs() != 2)
+      break;
+    if (isIndexInSubscriptExpr(Context, OpCall->getArg(1), IndexVar,
+                               OpCall->getArg(0), ContainerExpr,
+                               ContainerNeedsDereference)) {
+      addUsage(Usage(OpCall));
+      return true;
+    }
+    break;
+
+  default:
+    break;
+  }
+  return VisitorBase::TraverseCXXOperatorCallExpr(OpCall);
+}
+
+/// \brief If we encounter an array with IndexVar as the index of an
+/// ArraySubsriptExpression, note it as a consistent usage and prune the
+/// AST traversal.
+///
+/// For example, given
+/// \code
+///   const int N = 5;
+///   int arr[N];
+/// \endcode
+/// This is intended to permit
+/// \code
+///   for (int i = 0; i < N; ++i) {  /* use arr[i] */ }
+/// \endcode
+/// but not
+/// \code
+///   for (int i = 0; i < N; ++i) {  /* use notArr[i] */ }
+/// \endcode
+/// and further checking needs to be done later to ensure that exactly one array
+/// is referenced.
+bool ForLoopIndexUseVisitor::TraverseArraySubscriptExpr(ArraySubscriptExpr *E) {
+  Expr *Arr = E->getBase();
+  if (!isIndexInSubscriptExpr(E->getIdx(), IndexVar))
+    return VisitorBase::TraverseArraySubscriptExpr(E);
+
+  if ((ContainerExpr &&
+       !areSameExpr(Context, Arr->IgnoreParenImpCasts(),
+                    ContainerExpr->IgnoreParenImpCasts())) ||
+      !arrayMatchesBoundExpr(Context, Arr->IgnoreImpCasts()->getType(),
+                             ArrayBoundExpr)) {
+    // If we have already discovered the array being indexed and this isn't it
+    // or this array doesn't match, mark this loop as unconvertible.
+    OnlyUsedAsIndex = false;
+    return VisitorBase::TraverseArraySubscriptExpr(E);
+  }
+
+  if (!ContainerExpr)
+    ContainerExpr = Arr;
+
+  addUsage(Usage(E));
+  return true;
+}
+
+/// \brief If we encounter a reference to IndexVar in an unpruned branch of the
+/// traversal, mark this loop as unconvertible.
+///
+/// This implements the whitelist for convertible loops: any usages of IndexVar
+/// not explicitly considered convertible by this traversal will be caught by
+/// this function.
+///
+/// Additionally, if the container expression is more complex than just a
+/// DeclRefExpr, and some part of it is appears elsewhere in the loop, lower
+/// our confidence in the transformation.
+///
+/// For example, these are not permitted:
+/// \code
+///   for (int i = 0; i < N; ++i) {  printf("arr[%d] = %d", i, arr[i]); }
+///   for (vector<int>::iterator i = container.begin(), e = container.end();
+///        i != e; ++i)
+///     i.insert(0);
+///   for (vector<int>::iterator i = container.begin(), e = container.end();
+///        i != e; ++i)
+///     if (i + 1 != e)
+///       printf("%d", *i);
+/// \endcode
+///
+/// And these will raise the risk level:
+/// \code
+///    int arr[10][20];
+///    int l = 5;
+///    for (int j = 0; j < 20; ++j)
+///      int k = arr[l][j] + l; // using l outside arr[l] is considered risky
+///    for (int i = 0; i < obj.getVector().size(); ++i)
+///      obj.foo(10); // using `obj` is considered risky
+/// \endcode
+bool ForLoopIndexUseVisitor::VisitDeclRefExpr(DeclRefExpr *E) {
+  const ValueDecl *TheDecl = E->getDecl();
+  if (areSameVariable(IndexVar, TheDecl) ||
+      exprReferencesVariable(IndexVar, E) || areSameVariable(EndVar, TheDecl) ||
+      exprReferencesVariable(EndVar, E))
+    OnlyUsedAsIndex = false;
+  if (containsExpr(Context, &DependentExprs, E))
+    ConfidenceLevel.lowerTo(Confidence::CL_Risky);
+  return true;
+}
+
+/// \brief If the loop index is captured by a lambda, replace this capture
+/// by the range-for loop variable.
+///
+/// For example:
+/// \code
+///   for (int i = 0; i < N; ++i) {
+///     auto f = [v, i](int k) {
+///       printf("%d\n", v[i] + k);
+///     };
+///     f(v[i]);
+///   }
+/// \endcode
+///
+/// Will be replaced by:
+/// \code
+///   for (auto & elem : v) {
+///     auto f = [v, elem](int k) {
+///       printf("%d\n", elem + k);
+///     };
+///     f(elem);
+///   }
+/// \endcode
+bool ForLoopIndexUseVisitor::TraverseLambdaCapture(LambdaExpr *LE,
+                                                   const LambdaCapture *C,
+                                                   Expr *Init) {
+  if (C->capturesVariable()) {
+    const VarDecl *VDecl = C->getCapturedVar();
+    if (areSameVariable(IndexVar, cast<ValueDecl>(VDecl))) {
+      // FIXME: if the index is captured, it will count as an usage and the
+      // alias (if any) won't work, because it is only used in case of having
+      // exactly one usage.
+      addUsage(Usage(nullptr,
+                     C->getCaptureKind() == LCK_ByCopy ? Usage::UK_CaptureByCopy
+                                                       : Usage::UK_CaptureByRef,
+                     C->getLocation()));
+    }
+  }
+  return VisitorBase::TraverseLambdaCapture(LE, C, Init);
+}
+
+/// \brief If we find that another variable is created just to refer to the loop
+/// element, note it for reuse as the loop variable.
+///
+/// See the comments for isAliasDecl.
+bool ForLoopIndexUseVisitor::VisitDeclStmt(DeclStmt *S) {
+  if (!AliasDecl && S->isSingleDecl() &&
+      isAliasDecl(Context, S->getSingleDecl(), IndexVar)) {
+    AliasDecl = S;
+    if (CurrStmtParent) {
+      if (isa<IfStmt>(CurrStmtParent) || isa<WhileStmt>(CurrStmtParent) ||
+          isa<SwitchStmt>(CurrStmtParent))
+        ReplaceWithAliasUse = true;
+      else if (isa<ForStmt>(CurrStmtParent)) {
+        if (cast<ForStmt>(CurrStmtParent)->getConditionVariableDeclStmt() == S)
+          ReplaceWithAliasUse = true;
+        else
+          // It's assumed S came the for loop's init clause.
+          AliasFromForInit = true;
+      }
+    }
+  }
+
+  return true;
+}
+
+bool ForLoopIndexUseVisitor::TraverseStmt(Stmt *S) {
+  // If this is an initialization expression for a lambda capture, prune the
+  // traversal so that we don't end up diagnosing the contained DeclRefExpr as
+  // inconsistent usage. No need to record the usage here -- this is done in
+  // TraverseLambdaCapture().
+  if (const auto *LE = dyn_cast_or_null<LambdaExpr>(NextStmtParent)) {
+    // Any child of a LambdaExpr that isn't the body is an initialization
+    // expression.
+    if (S != LE->getBody()) {
+      return true;
+    }
+  }
+
+  // All this pointer swapping is a mechanism for tracking immediate parentage
+  // of Stmts.
+  const Stmt *OldNextParent = NextStmtParent;
+  CurrStmtParent = NextStmtParent;
+  NextStmtParent = S;
+  bool Result = VisitorBase::TraverseStmt(S);
+  NextStmtParent = OldNextParent;
+  return Result;
+}
+
+std::string VariableNamer::createIndexName() {
+  // FIXME: Add in naming conventions to handle:
+  //  - How to handle conflicts.
+  //  - An interactive process for naming.
+  std::string IteratorName;
+  StringRef ContainerName;
+  if (TheContainer)
+    ContainerName = TheContainer->getName();
+
+  size_t Len = ContainerName.size();
+  if (Len > 1 && ContainerName.endswith(Style == NS_UpperCase ? "S" : "s")) {
+    IteratorName = ContainerName.substr(0, Len - 1);
+    // E.g.: (auto thing : things)
+    if (!declarationExists(IteratorName) || IteratorName == OldIndex->getName())
+      return IteratorName;
+  }
+
+  if (Len > 2 && ContainerName.endswith(Style == NS_UpperCase ? "S_" : "s_")) {
+    IteratorName = ContainerName.substr(0, Len - 2);
+    // E.g.: (auto thing : things_)
+    if (!declarationExists(IteratorName) || IteratorName == OldIndex->getName())
+      return IteratorName;
+  }
+
+  return OldIndex->getName();
+}
+
+/// \brief Determines whether or not the the name \a Symbol conflicts with
+/// language keywords or defined macros. Also checks if the name exists in
+/// LoopContext, any of its parent contexts, or any of its child statements.
+///
+/// We also check to see if the same identifier was generated by this loop
+/// converter in a loop nested within SourceStmt.
+bool VariableNamer::declarationExists(StringRef Symbol) {
+  assert(Context != nullptr && "Expected an ASTContext");
+  IdentifierInfo &Ident = Context->Idents.get(Symbol);
+
+  // Check if the symbol is not an identifier (ie. is a keyword or alias).
+  if (!isAnyIdentifier(Ident.getTokenID()))
+    return true;
+
+  // Check for conflicting macro definitions.
+  if (Ident.hasMacroDefinition())
+    return true;
+
+  // Determine if the symbol was generated in a parent context.
+  for (const Stmt *S = SourceStmt; S != nullptr; S = ReverseAST->lookup(S)) {
+    StmtGeneratedVarNameMap::const_iterator I = GeneratedDecls->find(S);
+    if (I != GeneratedDecls->end() && I->second == Symbol)
+      return true;
+  }
+
+  // FIXME: Rather than detecting conflicts at their usages, we should check the
+  // parent context.
+  // For some reason, lookup() always returns the pair (NULL, NULL) because its
+  // StoredDeclsMap is not initialized (i.e. LookupPtr.getInt() is false inside
+  // of DeclContext::lookup()). Why is this?
+
+  // Finally, determine if the symbol was used in the loop or a child context.
+  DeclFinderASTVisitor DeclFinder(Symbol, GeneratedDecls);
+  return DeclFinder.findUsages(SourceStmt);
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/modernize/LoopConvertUtils.h b/clang-tidy/modernize/LoopConvertUtils.h
new file mode 100644 (file)
index 0000000..bee77d9
--- /dev/null
@@ -0,0 +1,467 @@
+//===--- LoopConvertUtils.h - clang-tidy ------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_LOOP_CONVERT_UTILS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_LOOP_CONVERT_UTILS_H
+
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Basic/SourceLocation.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SmallSet.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+#include <algorithm>
+#include <memory>
+#include <string>
+#include <utility>
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+enum LoopFixerKind { LFK_Array, LFK_Iterator, LFK_PseudoArray };
+
+/// A map used to walk the AST in reverse: maps child Stmt to parent Stmt.
+typedef llvm::DenseMap<const clang::Stmt *, const clang::Stmt *> StmtParentMap;
+
+/// A map used to walk the AST in reverse:
+///  maps VarDecl to the to parent DeclStmt.
+typedef llvm::DenseMap<const clang::VarDecl *, const clang::DeclStmt *>
+    DeclParentMap;
+
+/// A map used to track which variables have been removed by a refactoring pass.
+/// It maps the parent ForStmt to the removed index variable's VarDecl.
+typedef llvm::DenseMap<const clang::ForStmt *, const clang::VarDecl *>
+    ReplacedVarsMap;
+
+/// A map used to remember the variable names generated in a Stmt
+typedef llvm::DenseMap<const clang::Stmt *, std::string>
+    StmtGeneratedVarNameMap;
+
+/// A vector used to store the AST subtrees of an Expr.
+typedef llvm::SmallVector<const clang::Expr *, 16> ComponentVector;
+
+/// \brief Class used build the reverse AST properties needed to detect
+/// name conflicts and free variables.
+class StmtAncestorASTVisitor
+    : public clang::RecursiveASTVisitor<StmtAncestorASTVisitor> {
+public:
+  StmtAncestorASTVisitor() { StmtStack.push_back(nullptr); }
+
+  /// \brief Run the analysis on the TranslationUnitDecl.
+  ///
+  /// In case we're running this analysis multiple times, don't repeat the work.
+  void gatherAncestors(const clang::TranslationUnitDecl *T) {
+    if (StmtAncestors.empty())
+      TraverseDecl(const_cast<clang::TranslationUnitDecl *>(T));
+  }
+
+  /// Accessor for StmtAncestors.
+  const StmtParentMap &getStmtToParentStmtMap() { return StmtAncestors; }
+
+  /// Accessor for DeclParents.
+  const DeclParentMap &getDeclToParentStmtMap() { return DeclParents; }
+
+  friend class clang::RecursiveASTVisitor<StmtAncestorASTVisitor>;
+
+private:
+  StmtParentMap StmtAncestors;
+  DeclParentMap DeclParents;
+  llvm::SmallVector<const clang::Stmt *, 16> StmtStack;
+
+  bool TraverseStmt(clang::Stmt *Statement);
+  bool VisitDeclStmt(clang::DeclStmt *Statement);
+};
+
+/// Class used to find the variables and member expressions on which an
+/// arbitrary expression depends.
+class ComponentFinderASTVisitor
+    : public clang::RecursiveASTVisitor<ComponentFinderASTVisitor> {
+public:
+  ComponentFinderASTVisitor() = default;
+
+  /// Find the components of an expression and place them in a ComponentVector.
+  void findExprComponents(const clang::Expr *SourceExpr) {
+    TraverseStmt(const_cast<clang::Expr *>(SourceExpr));
+  }
+
+  /// Accessor for Components.
+  const ComponentVector &getComponents() { return Components; }
+
+  friend class clang::RecursiveASTVisitor<ComponentFinderASTVisitor>;
+
+private:
+  ComponentVector Components;
+
+  bool VisitDeclRefExpr(clang::DeclRefExpr *E);
+  bool VisitMemberExpr(clang::MemberExpr *Member);
+};
+
+/// Class used to determine if an expression is dependent on a variable declared
+/// inside of the loop where it would be used.
+class DependencyFinderASTVisitor
+    : public clang::RecursiveASTVisitor<DependencyFinderASTVisitor> {
+public:
+  DependencyFinderASTVisitor(const StmtParentMap *StmtParents,
+                             const DeclParentMap *DeclParents,
+                             const ReplacedVarsMap *ReplacedVars,
+                             const clang::Stmt *ContainingStmt)
+      : StmtParents(StmtParents), DeclParents(DeclParents),
+        ContainingStmt(ContainingStmt), ReplacedVars(ReplacedVars) {}
+
+  /// \brief Run the analysis on Body, and return true iff the expression
+  /// depends on some variable declared within ContainingStmt.
+  ///
+  /// This is intended to protect against hoisting the container expression
+  /// outside of an inner context if part of that expression is declared in that
+  /// inner context.
+  ///
+  /// For example,
+  /// \code
+  ///   const int N = 10, M = 20;
+  ///   int arr[N][M];
+  ///   int getRow();
+  ///
+  ///   for (int i = 0; i < M; ++i) {
+  ///     int k = getRow();
+  ///     printf("%d:", arr[k][i]);
+  ///   }
+  /// \endcode
+  /// At first glance, this loop looks like it could be changed to
+  /// \code
+  ///   for (int elem : arr[k]) {
+  ///     int k = getIndex();
+  ///     printf("%d:", elem);
+  ///   }
+  /// \endcode
+  /// But this is malformed, since `k` is used before it is defined!
+  ///
+  /// In order to avoid this, this class looks at the container expression
+  /// `arr[k]` and decides whether or not it contains a sub-expression declared
+  /// within the the loop body.
+  bool dependsOnInsideVariable(const clang::Stmt *Body) {
+    DependsOnInsideVariable = false;
+    TraverseStmt(const_cast<clang::Stmt *>(Body));
+    return DependsOnInsideVariable;
+  }
+
+  friend class clang::RecursiveASTVisitor<DependencyFinderASTVisitor>;
+
+private:
+  const StmtParentMap *StmtParents;
+  const DeclParentMap *DeclParents;
+  const clang::Stmt *ContainingStmt;
+  const ReplacedVarsMap *ReplacedVars;
+  bool DependsOnInsideVariable;
+
+  bool VisitVarDecl(clang::VarDecl *V);
+  bool VisitDeclRefExpr(clang::DeclRefExpr *D);
+};
+
+/// Class used to determine if any declarations used in a Stmt would conflict
+/// with a particular identifier. This search includes the names that don't
+/// actually appear in the AST (i.e. created by a refactoring tool) by including
+/// a map from Stmts to generated names associated with those stmts.
+class DeclFinderASTVisitor
+    : public clang::RecursiveASTVisitor<DeclFinderASTVisitor> {
+public:
+  DeclFinderASTVisitor(const std::string &Name,
+                       const StmtGeneratedVarNameMap *GeneratedDecls)
+      : Name(Name), GeneratedDecls(GeneratedDecls), Found(false) {}
+
+  /// Attempts to find any usages of variables name Name in Body, returning
+  /// true when it is used in Body. This includes the generated loop variables
+  /// of ForStmts which have already been transformed.
+  bool findUsages(const clang::Stmt *Body) {
+    Found = false;
+    TraverseStmt(const_cast<clang::Stmt *>(Body));
+    return Found;
+  }
+
+  friend class clang::RecursiveASTVisitor<DeclFinderASTVisitor>;
+
+private:
+  std::string Name;
+  /// GeneratedDecls keeps track of ForStmts which have been transformed,
+  /// mapping each modified ForStmt to the variable generated in the loop.
+  const StmtGeneratedVarNameMap *GeneratedDecls;
+  bool Found;
+
+  bool VisitForStmt(clang::ForStmt *F);
+  bool VisitNamedDecl(clang::NamedDecl *D);
+  bool VisitDeclRefExpr(clang::DeclRefExpr *D);
+  bool VisitTypeLoc(clang::TypeLoc TL);
+};
+
+/// \brief The information needed to describe a valid convertible usage
+/// of an array index or iterator.
+struct Usage {
+  enum UsageKind {
+    // Regular usages of the loop index (the ones not specified below). Some
+    // examples:
+    // \code
+    //   int X = 8 * Arr[i];
+    //               ^~~~~~
+    //   f(param1, param2, *It);
+    //                     ^~~
+    //   if (Vec[i].SomeBool) {}
+    //       ^~~~~~
+    // \endcode
+    UK_Default,
+    // Indicates whether this is an access to a member through the arrow
+    // operator on pointers or iterators.
+    UK_MemberThroughArrow,
+    // If the variable is being captured by a lambda, indicates whether the
+    // capture was done by value or by reference.
+    UK_CaptureByCopy,
+    UK_CaptureByRef
+  };
+  // The expression that is going to be converted. Null in case of lambda
+  // captures.
+  const Expr *Expression;
+
+  UsageKind Kind;
+
+  // Range that covers this usage.
+  SourceRange Range;
+
+  explicit Usage(const Expr *E)
+      : Expression(E), Kind(UK_Default), Range(Expression->getSourceRange()) {}
+  Usage(const Expr *E, UsageKind Kind, SourceRange Range)
+      : Expression(E), Kind(Kind), Range(std::move(Range)) {}
+};
+
+/// \brief A class to encapsulate lowering of the tool's confidence level.
+class Confidence {
+public:
+  enum Level {
+    // Transformations that are likely to change semantics.
+    CL_Risky,
+
+    // Transformations that might change semantics.
+    CL_Reasonable,
+
+    // Transformations that will not change semantics.
+    CL_Safe
+  };
+  /// \brief Initialize confidence level.
+  explicit Confidence(Confidence::Level Level) : CurrentLevel(Level) {}
+
+  /// \brief Lower the internal confidence level to Level, but do not raise it.
+  void lowerTo(Confidence::Level Level) {
+    CurrentLevel = std::min(Level, CurrentLevel);
+  }
+
+  /// \brief Return the internal confidence level.
+  Level getLevel() const { return CurrentLevel; }
+
+private:
+  Level CurrentLevel;
+};
+
+// The main computational result of ForLoopIndexVisitor.
+typedef llvm::SmallVector<Usage, 8> UsageResult;
+
+// General functions used by ForLoopIndexUseVisitor and LoopConvertCheck.
+const Expr *digThroughConstructors(const Expr *E);
+bool areSameExpr(ASTContext *Context, const Expr *First, const Expr *Second);
+const DeclRefExpr *getDeclRef(const Expr *E);
+bool areSameVariable(const ValueDecl *First, const ValueDecl *Second);
+
+/// \brief Discover usages of expressions consisting of index or iterator
+/// access.
+///
+/// Given an index variable, recursively crawls a for loop to discover if the
+/// index variable is used in a way consistent with range-based for loop access.
+class ForLoopIndexUseVisitor
+    : public RecursiveASTVisitor<ForLoopIndexUseVisitor> {
+public:
+  ForLoopIndexUseVisitor(ASTContext *Context, const VarDecl *IndexVar,
+                         const VarDecl *EndVar, const Expr *ContainerExpr,
+                         const Expr *ArrayBoundExpr,
+                         bool ContainerNeedsDereference);
+
+  /// \brief Finds all uses of IndexVar in Body, placing all usages in Usages,
+  /// and returns true if IndexVar was only used in a way consistent with a
+  /// range-based for loop.
+  ///
+  /// The general strategy is to reject any DeclRefExprs referencing IndexVar,
+  /// with the exception of certain acceptable patterns.
+  /// For arrays, the DeclRefExpr for IndexVar must appear as the index of an
+  /// ArraySubscriptExpression. Iterator-based loops may dereference
+  /// IndexVar or call methods through operator-> (builtin or overloaded).
+  /// Array-like containers may use IndexVar as a parameter to the at() member
+  /// function and in overloaded operator[].
+  bool findAndVerifyUsages(const Stmt *Body);
+
+  /// \brief Add a set of components that we should consider relevant to the
+  /// container.
+  void addComponents(const ComponentVector &Components);
+
+  /// \brief Accessor for Usages.
+  const UsageResult &getUsages() const { return Usages; }
+
+  /// \brief Adds the Usage if it was not added before.
+  void addUsage(const Usage &U);
+
+  /// \brief Get the container indexed by IndexVar, if any.
+  const Expr *getContainerIndexed() const { return ContainerExpr; }
+
+  /// \brief Returns the statement declaring the variable created as an alias
+  /// for the loop element, if any.
+  const DeclStmt *getAliasDecl() const { return AliasDecl; }
+
+  /// \brief Accessor for ConfidenceLevel.
+  Confidence::Level getConfidenceLevel() const {
+    return ConfidenceLevel.getLevel();
+  }
+
+  /// \brief Indicates if the alias declaration was in a place where it cannot
+  /// simply be removed but rather replaced with a use of the alias variable.
+  /// For example, variables declared in the condition of an if, switch, or for
+  /// stmt.
+  bool aliasUseRequired() const { return ReplaceWithAliasUse; }
+
+  /// \brief Indicates if the alias declaration came from the init clause of a
+  /// nested for loop. SourceRanges provided by Clang for DeclStmts in this
+  /// case need to be adjusted.
+  bool aliasFromForInit() const { return AliasFromForInit; }
+
+private:
+  /// Typedef used in CRTP functions.
+  typedef RecursiveASTVisitor<ForLoopIndexUseVisitor> VisitorBase;
+  friend class RecursiveASTVisitor<ForLoopIndexUseVisitor>;
+
+  /// Overriden methods for RecursiveASTVisitor's traversal.
+  bool TraverseArraySubscriptExpr(ArraySubscriptExpr *E);
+  bool TraverseCXXMemberCallExpr(CXXMemberCallExpr *MemberCall);
+  bool TraverseCXXOperatorCallExpr(CXXOperatorCallExpr *OpCall);
+  bool TraverseLambdaCapture(LambdaExpr *LE, const LambdaCapture *C,
+                             Expr *Init);
+  bool TraverseMemberExpr(MemberExpr *Member);
+  bool TraverseUnaryDeref(UnaryOperator *Uop);
+  bool VisitDeclRefExpr(DeclRefExpr *E);
+  bool VisitDeclStmt(DeclStmt *S);
+  bool TraverseStmt(Stmt *S);
+
+  /// \brief Add an expression to the list of expressions on which the container
+  /// expression depends.
+  void addComponent(const Expr *E);
+
+  // Input member variables:
+  ASTContext *Context;
+  /// The index variable's VarDecl.
+  const VarDecl *IndexVar;
+  /// The loop's 'end' variable, which cannot be mentioned at all.
+  const VarDecl *EndVar;
+  /// The Expr which refers to the container.
+  const Expr *ContainerExpr;
+  /// The Expr which refers to the terminating condition for array-based loops.
+  const Expr *ArrayBoundExpr;
+  bool ContainerNeedsDereference;
+
+  // Output member variables:
+  /// A container which holds all usages of IndexVar as the index of
+  /// ArraySubscriptExpressions.
+  UsageResult Usages;
+  llvm::SmallSet<SourceLocation, 8> UsageLocations;
+  bool OnlyUsedAsIndex;
+  /// The DeclStmt for an alias to the container element.
+  const DeclStmt *AliasDecl;
+  Confidence ConfidenceLevel;
+  /// \brief A list of expressions on which ContainerExpr depends.
+  ///
+  /// If any of these expressions are encountered outside of an acceptable usage
+  /// of the loop element, lower our confidence level.
+  llvm::SmallVector<std::pair<const Expr *, llvm::FoldingSetNodeID>, 16>
+      DependentExprs;
+
+  /// The parent-in-waiting. Will become the real parent once we traverse down
+  /// one level in the AST.
+  const Stmt *NextStmtParent;
+  /// The actual parent of a node when Visit*() calls are made. Only the
+  /// parentage of DeclStmt's to possible iteration/selection statements is of
+  /// importance.
+  const Stmt *CurrStmtParent;
+
+  /// \see aliasUseRequired().
+  bool ReplaceWithAliasUse;
+  /// \see aliasFromForInit().
+  bool AliasFromForInit;
+};
+
+struct TUTrackingInfo {
+  /// \brief Reset and initialize per-TU tracking information.
+  ///
+  /// Must be called before using container accessors.
+  TUTrackingInfo() : ParentFinder(new StmtAncestorASTVisitor) {}
+
+  StmtAncestorASTVisitor &getParentFinder() { return *ParentFinder; }
+  StmtGeneratedVarNameMap &getGeneratedDecls() { return GeneratedDecls; }
+  ReplacedVarsMap &getReplacedVars() { return ReplacedVars; }
+
+private:
+  std::unique_ptr<StmtAncestorASTVisitor> ParentFinder;
+  StmtGeneratedVarNameMap GeneratedDecls;
+  ReplacedVarsMap ReplacedVars;
+};
+
+/// \brief Create names for generated variables within a particular statement.
+///
+/// VariableNamer uses a DeclContext as a reference point, checking for any
+/// conflicting declarations higher up in the context or within SourceStmt.
+/// It creates a variable name using hints from a source container and the old
+/// index, if they exist.
+class VariableNamer {
+public:
+  // Supported naming styles.
+  enum NamingStyle {
+    NS_CamelBack,
+    NS_CamelCase,
+    NS_LowerCase,
+    NS_UpperCase,
+  };
+
+  VariableNamer(StmtGeneratedVarNameMap *GeneratedDecls,
+                const StmtParentMap *ReverseAST, const clang::Stmt *SourceStmt,
+                const clang::VarDecl *OldIndex,
+                const clang::ValueDecl *TheContainer,
+                const clang::ASTContext *Context, NamingStyle Style)
+      : GeneratedDecls(GeneratedDecls), ReverseAST(ReverseAST),
+        SourceStmt(SourceStmt), OldIndex(OldIndex), TheContainer(TheContainer),
+        Context(Context), Style(Style) {}
+
+  /// \brief Generate a new index name.
+  ///
+  /// Generates the name to be used for an inserted iterator. It relies on
+  /// declarationExists() to determine that there are no naming conflicts, and
+  /// tries to use some hints from the container name and the old index name.
+  std::string createIndexName();
+
+private:
+  StmtGeneratedVarNameMap *GeneratedDecls;
+  const StmtParentMap *ReverseAST;
+  const clang::Stmt *SourceStmt;
+  const clang::VarDecl *OldIndex;
+  const clang::ValueDecl *TheContainer;
+  const clang::ASTContext *Context;
+  const NamingStyle Style;
+
+  // Determine whether or not a declaration that would conflict with Symbol
+  // exists in an outer context or in any statement contained in SourceStmt.
+  bool declarationExists(llvm::StringRef Symbol);
+};
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_LOOP_CONVERT_UTILS_H
diff --git a/clang-tidy/modernize/MakeSharedCheck.cpp b/clang-tidy/modernize/MakeSharedCheck.cpp
new file mode 100644 (file)
index 0000000..0495dca
--- /dev/null
@@ -0,0 +1,31 @@
+//===--- MakeSharedCheck.cpp - clang-tidy----------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "MakeSharedCheck.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+MakeSharedCheck::MakeSharedCheck(StringRef Name, ClangTidyContext *Context)
+    : MakeSmartPtrCheck(Name, Context, "std::make_shared") {}
+
+MakeSharedCheck::SmartPtrTypeMatcher
+MakeSharedCheck::getSmartPointerTypeMatcher() const {
+  return qualType(hasDeclaration(classTemplateSpecializationDecl(
+      hasName("::std::shared_ptr"), templateArgumentCountIs(1),
+      hasTemplateArgument(
+          0, templateArgument(refersToType(qualType().bind(PointerType)))))));
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/modernize/MakeSharedCheck.h b/clang-tidy/modernize/MakeSharedCheck.h
new file mode 100644 (file)
index 0000000..cf01446
--- /dev/null
@@ -0,0 +1,43 @@
+//===--- MakeSharedCheck.h - clang-tidy--------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_MAKE_SHARED_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_MAKE_SHARED_H
+
+#include "MakeSmartPtrCheck.h"
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+/// Replace the pattern:
+/// \code
+///   std::shared_ptr<type>(new type(args...))
+/// \endcode
+///
+/// With the safer version:
+/// \code
+///   std::make_shared<type>(args...)
+/// \endcode
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/modernize-make-shared.html
+class MakeSharedCheck : public MakeSmartPtrCheck {
+public:
+  MakeSharedCheck(StringRef Name, ClangTidyContext *Context);
+
+protected:
+  SmartPtrTypeMatcher getSmartPointerTypeMatcher() const override;
+};
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_MAKE_SHARED_H
diff --git a/clang-tidy/modernize/MakeSmartPtrCheck.cpp b/clang-tidy/modernize/MakeSmartPtrCheck.cpp
new file mode 100644 (file)
index 0000000..fd2b74c
--- /dev/null
@@ -0,0 +1,286 @@
+//===--- MakeSmartPtrCheck.cpp - clang-tidy--------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "MakeSharedCheck.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Lex/Preprocessor.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+namespace {
+
+constexpr char StdMemoryHeader[] = "memory";
+
+std::string GetNewExprName(const CXXNewExpr *NewExpr,
+                           const SourceManager &SM,
+                           const LangOptions &Lang) {
+  StringRef WrittenName = Lexer::getSourceText(
+      CharSourceRange::getTokenRange(
+          NewExpr->getAllocatedTypeSourceInfo()->getTypeLoc().getSourceRange()),
+      SM, Lang);
+  if (NewExpr->isArray()) {
+    return WrittenName.str() + "[]";
+  }
+  return WrittenName.str();
+}
+
+} // namespace
+
+const char MakeSmartPtrCheck::PointerType[] = "pointerType";
+const char MakeSmartPtrCheck::ConstructorCall[] = "constructorCall";
+const char MakeSmartPtrCheck::ResetCall[] = "resetCall";
+const char MakeSmartPtrCheck::NewExpression[] = "newExpression";
+
+MakeSmartPtrCheck::MakeSmartPtrCheck(StringRef Name, ClangTidyContext *Context,
+                                     StringRef MakeSmartPtrFunctionName)
+    : ClangTidyCheck(Name, Context),
+      IncludeStyle(utils::IncludeSorter::parseIncludeStyle(
+          Options.get("IncludeStyle", "llvm"))),
+      MakeSmartPtrFunctionHeader(
+          Options.get("MakeSmartPtrFunctionHeader", StdMemoryHeader)),
+      MakeSmartPtrFunctionName(
+          Options.get("MakeSmartPtrFunction", MakeSmartPtrFunctionName)) {}
+
+void MakeSmartPtrCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "IncludeStyle", IncludeStyle);
+  Options.store(Opts, "MakeSmartPtrFunctionHeader", MakeSmartPtrFunctionHeader);
+  Options.store(Opts, "MakeSmartPtrFunction", MakeSmartPtrFunctionName);
+}
+
+void MakeSmartPtrCheck::registerPPCallbacks(CompilerInstance &Compiler) {
+  if (getLangOpts().CPlusPlus11) {
+    Inserter.reset(new utils::IncludeInserter(
+        Compiler.getSourceManager(), Compiler.getLangOpts(), IncludeStyle));
+    Compiler.getPreprocessor().addPPCallbacks(Inserter->CreatePPCallbacks());
+  }
+}
+
+void MakeSmartPtrCheck::registerMatchers(ast_matchers::MatchFinder *Finder) {
+  if (!getLangOpts().CPlusPlus11)
+    return;
+
+  // Calling make_smart_ptr from within a member function of a type with a
+  // private or protected constructor would be ill-formed.
+  auto CanCallCtor = unless(has(ignoringImpCasts(
+      cxxConstructExpr(hasDeclaration(decl(unless(isPublic())))))));
+
+  Finder->addMatcher(
+      cxxBindTemporaryExpr(has(ignoringParenImpCasts(
+          cxxConstructExpr(
+              hasType(getSmartPointerTypeMatcher()), argumentCountIs(1),
+              hasArgument(0,
+                          cxxNewExpr(hasType(pointsTo(qualType(hasCanonicalType(
+                                         equalsBoundNode(PointerType))))),
+                                     CanCallCtor)
+                              .bind(NewExpression)))
+              .bind(ConstructorCall)))),
+      this);
+
+  Finder->addMatcher(
+      cxxMemberCallExpr(
+          thisPointerType(getSmartPointerTypeMatcher()),
+          callee(cxxMethodDecl(hasName("reset"))),
+          hasArgument(0, cxxNewExpr(CanCallCtor).bind(NewExpression)))
+          .bind(ResetCall),
+      this);
+}
+
+void MakeSmartPtrCheck::check(const MatchFinder::MatchResult &Result) {
+  // 'smart_ptr' refers to 'std::shared_ptr' or 'std::unique_ptr' or other
+  // pointer, 'make_smart_ptr' refers to 'std::make_shared' or
+  // 'std::make_unique' or other function that creates smart_ptr.
+
+  SourceManager &SM = *Result.SourceManager;
+  const auto *Construct =
+      Result.Nodes.getNodeAs<CXXConstructExpr>(ConstructorCall);
+  const auto *Reset = Result.Nodes.getNodeAs<CXXMemberCallExpr>(ResetCall);
+  const auto *Type = Result.Nodes.getNodeAs<QualType>(PointerType);
+  const auto *New = Result.Nodes.getNodeAs<CXXNewExpr>(NewExpression);
+
+  if (New->getNumPlacementArgs() != 0)
+    return;
+
+  if (Construct)
+    checkConstruct(SM, Construct, Type, New);
+  else if (Reset)
+    checkReset(SM, Reset, New);
+}
+
+void MakeSmartPtrCheck::checkConstruct(SourceManager &SM,
+                                       const CXXConstructExpr *Construct,
+                                       const QualType *Type,
+                                       const CXXNewExpr *New) {
+  SourceLocation ConstructCallStart = Construct->getExprLoc();
+
+  bool Invalid = false;
+  StringRef ExprStr = Lexer::getSourceText(
+      CharSourceRange::getCharRange(
+          ConstructCallStart, Construct->getParenOrBraceRange().getBegin()),
+      SM, getLangOpts(), &Invalid);
+  if (Invalid)
+    return;
+
+  auto Diag = diag(ConstructCallStart, "use %0 instead")
+              << MakeSmartPtrFunctionName;
+
+  // Find the location of the template's left angle.
+  size_t LAngle = ExprStr.find("<");
+  SourceLocation ConstructCallEnd;
+  if (LAngle == StringRef::npos) {
+    // If the template argument is missing (because it is part of the alias)
+    // we have to add it back.
+    ConstructCallEnd = ConstructCallStart.getLocWithOffset(ExprStr.size());
+    Diag << FixItHint::CreateInsertion(
+        ConstructCallEnd,
+        "<" + GetNewExprName(New, SM, getLangOpts()) + ">");
+  } else {
+    ConstructCallEnd = ConstructCallStart.getLocWithOffset(LAngle);
+  }
+
+  Diag << FixItHint::CreateReplacement(
+      CharSourceRange::getCharRange(ConstructCallStart, ConstructCallEnd),
+      MakeSmartPtrFunctionName);
+
+  // If the smart_ptr is built with brace enclosed direct initialization, use
+  // parenthesis instead.
+  if (Construct->isListInitialization()) {
+    SourceRange BraceRange = Construct->getParenOrBraceRange();
+    Diag << FixItHint::CreateReplacement(
+        CharSourceRange::getCharRange(
+            BraceRange.getBegin(), BraceRange.getBegin().getLocWithOffset(1)),
+        "(");
+    Diag << FixItHint::CreateReplacement(
+        CharSourceRange::getCharRange(BraceRange.getEnd(),
+                                      BraceRange.getEnd().getLocWithOffset(1)),
+        ")");
+  }
+
+  replaceNew(Diag, New, SM);
+  insertHeader(Diag, SM.getFileID(ConstructCallStart));
+}
+
+void MakeSmartPtrCheck::checkReset(SourceManager &SM,
+                                   const CXXMemberCallExpr *Reset,
+                                   const CXXNewExpr *New) {
+  const auto *Expr = cast<MemberExpr>(Reset->getCallee());
+  SourceLocation OperatorLoc = Expr->getOperatorLoc();
+  SourceLocation ResetCallStart = Reset->getExprLoc();
+  SourceLocation ExprStart = Expr->getLocStart();
+  SourceLocation ExprEnd =
+      Lexer::getLocForEndOfToken(Expr->getLocEnd(), 0, SM, getLangOpts());
+
+  auto Diag = diag(ResetCallStart, "use %0 instead")
+              << MakeSmartPtrFunctionName;
+
+  Diag << FixItHint::CreateReplacement(
+      CharSourceRange::getCharRange(OperatorLoc, ExprEnd),
+      (llvm::Twine(" = ") + MakeSmartPtrFunctionName + "<" +
+       GetNewExprName(New, SM, getLangOpts()) + ">")
+          .str());
+
+  if (Expr->isArrow())
+    Diag << FixItHint::CreateInsertion(ExprStart, "*");
+
+  replaceNew(Diag, New, SM);
+  insertHeader(Diag, SM.getFileID(OperatorLoc));
+}
+
+void MakeSmartPtrCheck::replaceNew(DiagnosticBuilder &Diag,
+                                   const CXXNewExpr *New,
+                                   SourceManager& SM) {
+  SourceLocation NewStart = New->getSourceRange().getBegin();
+  SourceLocation NewEnd = New->getSourceRange().getEnd();
+
+  std::string ArraySizeExpr;
+  if (const auto* ArraySize = New->getArraySize()) {
+    ArraySizeExpr = Lexer::getSourceText(CharSourceRange::getTokenRange(
+                                             ArraySize->getSourceRange()),
+                                         SM, getLangOpts())
+                        .str();
+  }
+
+  switch (New->getInitializationStyle()) {
+  case CXXNewExpr::NoInit: {
+    if (ArraySizeExpr.empty()) {
+      Diag << FixItHint::CreateRemoval(SourceRange(NewStart, NewEnd));
+    } else {
+      // New array expression without written initializer:
+      //   smart_ptr<Foo[]>(new Foo[5]);
+      Diag << FixItHint::CreateReplacement(SourceRange(NewStart, NewEnd),
+                                           ArraySizeExpr);
+    }
+    break;
+  }
+  case CXXNewExpr::CallInit: {
+    if (ArraySizeExpr.empty()) {
+      SourceRange InitRange = New->getDirectInitRange();
+      Diag << FixItHint::CreateRemoval(
+          SourceRange(NewStart, InitRange.getBegin()));
+      Diag << FixItHint::CreateRemoval(SourceRange(InitRange.getEnd(), NewEnd));
+    }
+    else {
+      // New array expression with default/value initialization:
+      //   smart_ptr<Foo[]>(new int[5]());
+      //   smart_ptr<Foo[]>(new Foo[5]());
+      Diag << FixItHint::CreateReplacement(SourceRange(NewStart, NewEnd),
+                                           ArraySizeExpr);
+    }
+    break;
+  }
+  case CXXNewExpr::ListInit: {
+    // Range of the substring that we do not want to remove.
+    SourceRange InitRange;
+    if (const auto *NewConstruct = New->getConstructExpr()) {
+      // Direct initialization with initialization list.
+      //   struct S { S(int x) {} };
+      //   smart_ptr<S>(new S{5});
+      // The arguments in the initialization list are going to be forwarded to
+      // the constructor, so this has to be replaced with:
+      //   struct S { S(int x) {} };
+      //   std::make_smart_ptr<S>(5);
+      InitRange = SourceRange(
+          NewConstruct->getParenOrBraceRange().getBegin().getLocWithOffset(1),
+          NewConstruct->getParenOrBraceRange().getEnd().getLocWithOffset(-1));
+    } else {
+      // Aggregate initialization.
+      //   smart_ptr<Pair>(new Pair{first, second});
+      // Has to be replaced with:
+      //   smart_ptr<Pair>(Pair{first, second});
+      InitRange = SourceRange(
+          New->getAllocatedTypeSourceInfo()->getTypeLoc().getLocStart(),
+          New->getInitializer()->getSourceRange().getEnd());
+    }
+    Diag << FixItHint::CreateRemoval(
+        CharSourceRange::getCharRange(NewStart, InitRange.getBegin()));
+    Diag << FixItHint::CreateRemoval(
+        SourceRange(InitRange.getEnd().getLocWithOffset(1), NewEnd));
+    break;
+  }
+  }
+}
+
+void MakeSmartPtrCheck::insertHeader(DiagnosticBuilder &Diag, FileID FD) {
+  if (MakeSmartPtrFunctionHeader.empty()) {
+    return;
+  }
+  if (auto IncludeFixit = Inserter->CreateIncludeInsertion(
+          FD, MakeSmartPtrFunctionHeader,
+          /*IsAngled=*/MakeSmartPtrFunctionHeader == StdMemoryHeader)) {
+    Diag << *IncludeFixit;
+  }
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/modernize/MakeSmartPtrCheck.h b/clang-tidy/modernize/MakeSmartPtrCheck.h
new file mode 100644 (file)
index 0000000..f01550e
--- /dev/null
@@ -0,0 +1,68 @@
+//===--- MakeSmartPtrCheck.h - clang-tidy------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_MAKE_SMART_PTR_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_MAKE_SMART_PTR_H
+
+#include "../ClangTidy.h"
+#include "../utils/IncludeInserter.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchersInternal.h"
+#include "llvm/ADT/StringRef.h"
+#include <string>
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+/// Base class for MakeSharedCheck and MakeUniqueCheck.
+class MakeSmartPtrCheck : public ClangTidyCheck {
+public:
+  MakeSmartPtrCheck(StringRef Name, ClangTidyContext *Context,
+                    StringRef MakeSmartPtrFunctionName);
+  void registerMatchers(ast_matchers::MatchFinder *Finder) final;
+  void registerPPCallbacks(clang::CompilerInstance &Compiler) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) final;
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+
+protected:
+  using SmartPtrTypeMatcher = ast_matchers::internal::BindableMatcher<QualType>;
+
+  /// Returns matcher that match with different smart pointer types.
+  ///
+  /// Requires to bind pointer type (qualType) with PointerType string declared
+  /// in this class.
+  virtual SmartPtrTypeMatcher getSmartPointerTypeMatcher() const = 0;
+
+  static const char PointerType[];
+  static const char ConstructorCall[];
+  static const char ResetCall[];
+  static const char NewExpression[];
+
+private:
+  std::unique_ptr<utils::IncludeInserter> Inserter;
+  const utils::IncludeSorter::IncludeStyle IncludeStyle;
+  const std::string MakeSmartPtrFunctionHeader;
+  const std::string MakeSmartPtrFunctionName;
+
+  void checkConstruct(SourceManager &SM, const CXXConstructExpr *Construct,
+                      const QualType *Type, const CXXNewExpr *New);
+  void checkReset(SourceManager &SM, const CXXMemberCallExpr *Member,
+                  const CXXNewExpr *New);
+
+  void replaceNew(DiagnosticBuilder &Diag, const CXXNewExpr *New,
+                  SourceManager &SM);
+  void insertHeader(DiagnosticBuilder &Diag, FileID FD);
+};
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_MAKE_SMART_PTR_H
diff --git a/clang-tidy/modernize/MakeUniqueCheck.cpp b/clang-tidy/modernize/MakeUniqueCheck.cpp
new file mode 100644 (file)
index 0000000..fea452f
--- /dev/null
@@ -0,0 +1,40 @@
+//===--- MakeUniqueCheck.cpp - clang-tidy----------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "MakeUniqueCheck.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+MakeUniqueCheck::MakeUniqueCheck(StringRef Name,
+                                 clang::tidy::ClangTidyContext *Context)
+    : MakeSmartPtrCheck(Name, Context, "std::make_unique") {}
+
+MakeUniqueCheck::SmartPtrTypeMatcher
+MakeUniqueCheck::getSmartPointerTypeMatcher() const {
+  return qualType(hasDeclaration(classTemplateSpecializationDecl(
+      hasName("::std::unique_ptr"), templateArgumentCountIs(2),
+      hasTemplateArgument(
+          0, templateArgument(refersToType(qualType().bind(PointerType)))),
+      hasTemplateArgument(
+          1,
+          templateArgument(refersToType(
+              qualType(hasDeclaration(classTemplateSpecializationDecl(
+                  hasName("::std::default_delete"), templateArgumentCountIs(1),
+                  hasTemplateArgument(
+                      0, templateArgument(refersToType(
+                             qualType(equalsBoundNode(PointerType))))))))))))));
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/modernize/MakeUniqueCheck.h b/clang-tidy/modernize/MakeUniqueCheck.h
new file mode 100644 (file)
index 0000000..15fbd55
--- /dev/null
@@ -0,0 +1,40 @@
+//===--- MakeUniqueCheck.h - clang-tidy--------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_MAKE_UNIQUE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_MAKE_UNIQUE_H
+
+#include "MakeSmartPtrCheck.h"
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+/// Replace the pattern:
+/// \code
+///   std::unique_ptr<type>(new type(args...))
+/// \endcode
+///
+/// With the C++14 version:
+/// \code
+///   std::make_unique<type>(args...)
+/// \endcode
+class MakeUniqueCheck : public MakeSmartPtrCheck {
+public:
+  MakeUniqueCheck(StringRef Name, ClangTidyContext *Context);
+
+protected:
+  SmartPtrTypeMatcher getSmartPointerTypeMatcher() const override;
+};
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_MAKE_UNIQUE_H
diff --git a/clang-tidy/modernize/ModernizeTidyModule.cpp b/clang-tidy/modernize/ModernizeTidyModule.cpp
new file mode 100644 (file)
index 0000000..a2f05d6
--- /dev/null
@@ -0,0 +1,113 @@
+//===--- ModernizeTidyModule.cpp - clang-tidy -----------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "../ClangTidy.h"
+#include "../ClangTidyModule.h"
+#include "../ClangTidyModuleRegistry.h"
+#include "AvoidBindCheck.h"
+#include "DeprecatedHeadersCheck.h"
+#include "LoopConvertCheck.h"
+#include "MakeSharedCheck.h"
+#include "MakeUniqueCheck.h"
+#include "PassByValueCheck.h"
+#include "RawStringLiteralCheck.h"
+#include "RedundantVoidArgCheck.h"
+#include "ReplaceAutoPtrCheck.h"
+#include "ReplaceRandomShuffleCheck.h"
+#include "ReturnBracedInitListCheck.h"
+#include "ShrinkToFitCheck.h"
+#include "UnaryStaticAssertCheck.h"
+#include "UseAutoCheck.h"
+#include "UseBoolLiteralsCheck.h"
+#include "UseDefaultMemberInitCheck.h"
+#include "UseEmplaceCheck.h"
+#include "UseEqualsDefaultCheck.h"
+#include "UseEqualsDeleteCheck.h"
+#include "UseNoexceptCheck.h"
+#include "UseNullptrCheck.h"
+#include "UseOverrideCheck.h"
+#include "UseTransparentFunctorsCheck.h"
+#include "UseUsingCheck.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+class ModernizeModule : public ClangTidyModule {
+public:
+  void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
+    CheckFactories.registerCheck<AvoidBindCheck>("modernize-avoid-bind");
+    CheckFactories.registerCheck<DeprecatedHeadersCheck>(
+        "modernize-deprecated-headers");
+    CheckFactories.registerCheck<LoopConvertCheck>("modernize-loop-convert");
+    CheckFactories.registerCheck<MakeSharedCheck>("modernize-make-shared");
+    CheckFactories.registerCheck<MakeUniqueCheck>("modernize-make-unique");
+    CheckFactories.registerCheck<PassByValueCheck>("modernize-pass-by-value");
+    CheckFactories.registerCheck<RawStringLiteralCheck>(
+        "modernize-raw-string-literal");
+    CheckFactories.registerCheck<RedundantVoidArgCheck>(
+        "modernize-redundant-void-arg");
+    CheckFactories.registerCheck<ReplaceAutoPtrCheck>(
+        "modernize-replace-auto-ptr");
+    CheckFactories.registerCheck<ReplaceRandomShuffleCheck>(
+        "modernize-replace-random-shuffle");
+    CheckFactories.registerCheck<ReturnBracedInitListCheck>(
+        "modernize-return-braced-init-list");
+    CheckFactories.registerCheck<ShrinkToFitCheck>("modernize-shrink-to-fit");
+    CheckFactories.registerCheck<UnaryStaticAssertCheck>(
+        "modernize-unary-static-assert");
+    CheckFactories.registerCheck<UseAutoCheck>("modernize-use-auto");
+    CheckFactories.registerCheck<UseBoolLiteralsCheck>(
+        "modernize-use-bool-literals");
+    CheckFactories.registerCheck<UseDefaultMemberInitCheck>(
+        "modernize-use-default-member-init");
+    CheckFactories.registerCheck<UseEmplaceCheck>("modernize-use-emplace");
+    CheckFactories.registerCheck<UseEqualsDefaultCheck>("modernize-use-equals-default");
+    CheckFactories.registerCheck<UseEqualsDeleteCheck>(
+        "modernize-use-equals-delete");
+    CheckFactories.registerCheck<UseNoexceptCheck>("modernize-use-noexcept");
+    CheckFactories.registerCheck<UseNullptrCheck>("modernize-use-nullptr");
+    CheckFactories.registerCheck<UseOverrideCheck>("modernize-use-override");
+    CheckFactories.registerCheck<UseTransparentFunctorsCheck>(
+        "modernize-use-transparent-functors");
+    CheckFactories.registerCheck<UseUsingCheck>("modernize-use-using");
+  }
+
+  ClangTidyOptions getModuleOptions() override {
+    ClangTidyOptions Options;
+    auto &Opts = Options.CheckOptions;
+    // For types whose size in bytes is above this threshold, we prefer taking a
+    // const-reference than making a copy.
+    Opts["modernize-loop-convert.MaxCopySize"] = "16";
+
+    Opts["modernize-loop-convert.MinConfidence"] = "reasonable";
+    Opts["modernize-loop-convert.NamingStyle"] = "CamelCase";
+    Opts["modernize-pass-by-value.IncludeStyle"] = "llvm";    // Also: "google".
+    Opts["modernize-replace-auto-ptr.IncludeStyle"] = "llvm"; // Also: "google".
+
+    // Comma-separated list of macros that behave like NULL.
+    Opts["modernize-use-nullptr.NullMacros"] = "NULL";
+    return Options;
+  }
+};
+
+// Register the ModernizeTidyModule using this statically initialized variable.
+static ClangTidyModuleRegistry::Add<ModernizeModule> X("modernize-module",
+                                                       "Add modernize checks.");
+
+} // namespace modernize
+
+// This anchor is used to force the linker to link in the generated object file
+// and thus register the ModernizeModule.
+volatile int ModernizeModuleAnchorSource = 0;
+
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/modernize/PassByValueCheck.cpp b/clang-tidy/modernize/PassByValueCheck.cpp
new file mode 100644 (file)
index 0000000..2d462db
--- /dev/null
@@ -0,0 +1,232 @@
+//===--- PassByValueCheck.cpp - clang-tidy---------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "PassByValueCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Lex/Preprocessor.h"
+
+using namespace clang::ast_matchers;
+using namespace llvm;
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+/// \brief Matches move-constructible classes.
+///
+/// Given
+/// \code
+///   // POD types are trivially move constructible.
+///   struct Foo { int a; };
+///
+///   struct Bar {
+///     Bar(Bar &&) = deleted;
+///     int a;
+///   };
+/// \endcode
+/// recordDecl(isMoveConstructible())
+///   matches "Foo".
+AST_MATCHER(CXXRecordDecl, isMoveConstructible) {
+  for (const CXXConstructorDecl *Ctor : Node.ctors()) {
+    if (Ctor->isMoveConstructor() && !Ctor->isDeleted())
+      return true;
+  }
+  return false;
+}
+
+static TypeMatcher constRefType() {
+  return lValueReferenceType(pointee(isConstQualified()));
+}
+
+static TypeMatcher nonConstValueType() {
+  return qualType(unless(anyOf(referenceType(), isConstQualified())));
+}
+
+/// \brief Whether or not \p ParamDecl is used exactly one time in \p Ctor.
+///
+/// Checks both in the init-list and the body of the constructor.
+static bool paramReferredExactlyOnce(const CXXConstructorDecl *Ctor,
+                                     const ParmVarDecl *ParamDecl) {
+  /// \brief \c clang::RecursiveASTVisitor that checks that the given
+  /// \c ParmVarDecl is used exactly one time.
+  ///
+  /// \see ExactlyOneUsageVisitor::hasExactlyOneUsageIn()
+  class ExactlyOneUsageVisitor
+      : public RecursiveASTVisitor<ExactlyOneUsageVisitor> {
+    friend class RecursiveASTVisitor<ExactlyOneUsageVisitor>;
+
+  public:
+    ExactlyOneUsageVisitor(const ParmVarDecl *ParamDecl)
+        : ParamDecl(ParamDecl) {}
+
+    /// \brief Whether or not the parameter variable is referred only once in
+    /// the
+    /// given constructor.
+    bool hasExactlyOneUsageIn(const CXXConstructorDecl *Ctor) {
+      Count = 0;
+      TraverseDecl(const_cast<CXXConstructorDecl *>(Ctor));
+      return Count == 1;
+    }
+
+  private:
+    /// \brief Counts the number of references to a variable.
+    ///
+    /// Stops the AST traversal if more than one usage is found.
+    bool VisitDeclRefExpr(DeclRefExpr *D) {
+      if (const ParmVarDecl *To = dyn_cast<ParmVarDecl>(D->getDecl())) {
+        if (To == ParamDecl) {
+          ++Count;
+          if (Count > 1) {
+            // No need to look further, used more than once.
+            return false;
+          }
+        }
+      }
+      return true;
+    }
+
+    const ParmVarDecl *ParamDecl;
+    unsigned Count;
+  };
+
+  return ExactlyOneUsageVisitor(ParamDecl).hasExactlyOneUsageIn(Ctor);
+}
+
+/// \brief Find all references to \p ParamDecl across all of the
+/// redeclarations of \p Ctor.
+static SmallVector<const ParmVarDecl *, 2>
+collectParamDecls(const CXXConstructorDecl *Ctor,
+                  const ParmVarDecl *ParamDecl) {
+  SmallVector<const ParmVarDecl *, 2> Results;
+  unsigned ParamIdx = ParamDecl->getFunctionScopeIndex();
+
+  for (const FunctionDecl *Redecl : Ctor->redecls())
+    Results.push_back(Redecl->getParamDecl(ParamIdx));
+  return Results;
+}
+
+PassByValueCheck::PassByValueCheck(StringRef Name, ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      IncludeStyle(utils::IncludeSorter::parseIncludeStyle(
+          Options.get("IncludeStyle", "llvm"))),
+      ValuesOnly(Options.get("ValuesOnly", 0) != 0) {}
+
+void PassByValueCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "IncludeStyle",
+                utils::IncludeSorter::toString(IncludeStyle));
+  Options.store(Opts, "ValuesOnly", ValuesOnly);
+}
+
+void PassByValueCheck::registerMatchers(MatchFinder *Finder) {
+  // Only register the matchers for C++; the functionality currently does not
+  // provide any benefit to other languages, despite being benign.
+  if (!getLangOpts().CPlusPlus)
+    return;
+
+  Finder->addMatcher(
+      cxxConstructorDecl(
+          forEachConstructorInitializer(
+              cxxCtorInitializer(
+                  unless(isBaseInitializer()),
+                  // Clang builds a CXXConstructExpr only when it knows which
+                  // constructor will be called. In dependent contexts a
+                  // ParenListExpr is generated instead of a CXXConstructExpr,
+                  // filtering out templates automatically for us.
+                  withInitializer(cxxConstructExpr(
+                      has(ignoringParenImpCasts(declRefExpr(to(
+                          parmVarDecl(
+                              hasType(qualType(
+                                  // Match only const-ref or a non-const value
+                                  // parameters. Rvalues and const-values
+                                  // shouldn't be modified.
+                                  ValuesOnly ? nonConstValueType()
+                                             : anyOf(constRefType(),
+                                                     nonConstValueType()))))
+                              .bind("Param"))))),
+                      hasDeclaration(cxxConstructorDecl(
+                          isCopyConstructor(), unless(isDeleted()),
+                          hasDeclContext(
+                              cxxRecordDecl(isMoveConstructible())))))))
+                  .bind("Initializer")))
+          .bind("Ctor"),
+      this);
+}
+
+void PassByValueCheck::registerPPCallbacks(CompilerInstance &Compiler) {
+  // Only register the preprocessor callbacks for C++; the functionality
+  // currently does not provide any benefit to other languages, despite being
+  // benign.
+  if (getLangOpts().CPlusPlus) {
+    Inserter.reset(new utils::IncludeInserter(
+        Compiler.getSourceManager(), Compiler.getLangOpts(), IncludeStyle));
+    Compiler.getPreprocessor().addPPCallbacks(Inserter->CreatePPCallbacks());
+  }
+}
+
+void PassByValueCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructorDecl>("Ctor");
+  const auto *ParamDecl = Result.Nodes.getNodeAs<ParmVarDecl>("Param");
+  const auto *Initializer =
+      Result.Nodes.getNodeAs<CXXCtorInitializer>("Initializer");
+  SourceManager &SM = *Result.SourceManager;
+
+  // If the parameter is used or anything other than the copy, do not apply
+  // the changes.
+  if (!paramReferredExactlyOnce(Ctor, ParamDecl))
+    return;
+
+  // If the parameter is trivial to copy, don't move it. Moving a trivivally
+  // copyable type will cause a problem with misc-move-const-arg
+  if (ParamDecl->getType().getNonReferenceType().isTriviallyCopyableType(
+          *Result.Context))
+    return;
+
+  auto Diag = diag(ParamDecl->getLocStart(), "pass by value and use std::move");
+
+  // Iterate over all declarations of the constructor.
+  for (const ParmVarDecl *ParmDecl : collectParamDecls(Ctor, ParamDecl)) {
+    auto ParamTL = ParmDecl->getTypeSourceInfo()->getTypeLoc();
+    auto RefTL = ParamTL.getAs<ReferenceTypeLoc>();
+
+    // Do not replace if it is already a value, skip.
+    if (RefTL.isNull())
+      continue;
+
+    TypeLoc ValueTL = RefTL.getPointeeLoc();
+    auto TypeRange = CharSourceRange::getTokenRange(ParmDecl->getLocStart(),
+                                                    ParamTL.getLocEnd());
+    std::string ValueStr = Lexer::getSourceText(CharSourceRange::getTokenRange(
+                                                    ValueTL.getSourceRange()),
+                                                SM, getLangOpts())
+                               .str();
+    ValueStr += ' ';
+    Diag << FixItHint::CreateReplacement(TypeRange, ValueStr);
+  }
+
+  // Use std::move in the initialization list.
+  Diag << FixItHint::CreateInsertion(Initializer->getRParenLoc(), ")")
+       << FixItHint::CreateInsertion(
+              Initializer->getLParenLoc().getLocWithOffset(1), "std::move(");
+
+  if (auto IncludeFixit = Inserter->CreateIncludeInsertion(
+          Result.SourceManager->getFileID(Initializer->getSourceLocation()),
+          "utility",
+          /*IsAngled=*/true)) {
+    Diag << *IncludeFixit;
+  }
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/modernize/PassByValueCheck.h b/clang-tidy/modernize/PassByValueCheck.h
new file mode 100644 (file)
index 0000000..37deb3f
--- /dev/null
@@ -0,0 +1,40 @@
+//===--- PassByValueCheck.h - clang-tidy-------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_PASS_BY_VALUE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_PASS_BY_VALUE_H
+
+#include "../ClangTidy.h"
+#include "../utils/IncludeInserter.h"
+
+#include <memory>
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+class PassByValueCheck : public ClangTidyCheck {
+public:
+  PassByValueCheck(StringRef Name, ClangTidyContext *Context);
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+  void registerPPCallbacks(clang::CompilerInstance &Compiler) override;
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+  std::unique_ptr<utils::IncludeInserter> Inserter;
+  const utils::IncludeSorter::IncludeStyle IncludeStyle;
+  const bool ValuesOnly;
+};
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_PASS_BY_VALUE_H
diff --git a/clang-tidy/modernize/RawStringLiteralCheck.cpp b/clang-tidy/modernize/RawStringLiteralCheck.cpp
new file mode 100644 (file)
index 0000000..f6eed70
--- /dev/null
@@ -0,0 +1,150 @@
+//===--- RawStringLiteralCheck.cpp - clang-tidy----------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "RawStringLiteralCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+namespace {
+
+bool containsEscapes(StringRef HayStack, StringRef Escapes) {
+  size_t BackSlash = HayStack.find('\\');
+  if (BackSlash == StringRef::npos)
+    return false;
+
+  while (BackSlash != StringRef::npos) {
+    if (Escapes.find(HayStack[BackSlash + 1]) == StringRef::npos)
+      return false;
+    BackSlash = HayStack.find('\\', BackSlash + 2);
+  }
+
+  return true;
+}
+
+bool isRawStringLiteral(StringRef Text) {
+  // Already a raw string literal if R comes before ".
+  const size_t QuotePos = Text.find('"');
+  assert(QuotePos != StringRef::npos);
+  return (QuotePos > 0) && (Text[QuotePos - 1] == 'R');
+}
+
+bool containsEscapedCharacters(const MatchFinder::MatchResult &Result,
+                               const StringLiteral *Literal) {
+  // FIXME: Handle L"", u8"", u"" and U"" literals.
+  if (!Literal->isAscii())
+    return false;
+
+  StringRef Bytes = Literal->getBytes();
+  // Non-printing characters disqualify this literal:
+  // \007 = \a bell
+  // \010 = \b backspace
+  // \011 = \t horizontal tab
+  // \012 = \n new line
+  // \013 = \v vertical tab
+  // \014 = \f form feed
+  // \015 = \r carriage return
+  // \177 = delete
+  if (Bytes.find_first_of(StringRef("\000\001\002\003\004\005\006\a"
+                                    "\b\t\n\v\f\r\016\017"
+                                    "\020\021\022\023\024\025\026\027"
+                                    "\030\031\032\033\034\035\036\037"
+                                    "\177",
+                                    33)) != StringRef::npos)
+    return false;
+
+  CharSourceRange CharRange = Lexer::makeFileCharRange(
+      CharSourceRange::getTokenRange(Literal->getSourceRange()),
+      *Result.SourceManager, Result.Context->getLangOpts());
+  StringRef Text = Lexer::getSourceText(CharRange, *Result.SourceManager,
+                                        Result.Context->getLangOpts());
+  if (isRawStringLiteral(Text))
+    return false;
+
+  return containsEscapes(Text, R"('\"?x01)");
+}
+
+bool containsDelimiter(StringRef Bytes, const std::string &Delimiter) {
+  return Bytes.find(Delimiter.empty()
+                        ? std::string(R"lit()")lit")
+                        : (")" + Delimiter + R"(")")) != StringRef::npos;
+}
+
+std::string asRawStringLiteral(const StringLiteral *Literal,
+                               const std::string &DelimiterStem) {
+  const StringRef Bytes = Literal->getBytes();
+  std::string Delimiter;
+  for (int I = 0; containsDelimiter(Bytes, Delimiter); ++I) {
+    Delimiter = (I == 0) ? DelimiterStem : DelimiterStem + std::to_string(I);
+  }
+
+  if (Delimiter.empty())
+    return (R"(R"()" + Bytes + R"lit()")lit").str();
+
+  return (R"(R")" + Delimiter + "(" + Bytes + ")" + Delimiter + R"(")").str();
+}
+
+} // namespace
+
+RawStringLiteralCheck::RawStringLiteralCheck(StringRef Name,
+                                             ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      DelimiterStem(Options.get("DelimiterStem", "lit")),
+      ReplaceShorterLiterals(Options.get("ReplaceShorterLiterals", false)) {}
+
+void RawStringLiteralCheck::storeOptions(ClangTidyOptions::OptionMap &Options) {
+  ClangTidyCheck::storeOptions(Options);
+  this->Options.store(Options, "ReplaceShorterLiterals",
+                      ReplaceShorterLiterals);
+}
+
+void RawStringLiteralCheck::registerMatchers(MatchFinder *Finder) {
+  // Raw string literals require C++11 or later.
+  if (!getLangOpts().CPlusPlus11)
+    return;
+
+  Finder->addMatcher(
+      stringLiteral(unless(hasParent(predefinedExpr()))).bind("lit"), this);
+}
+
+void RawStringLiteralCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *Literal = Result.Nodes.getNodeAs<StringLiteral>("lit");
+  if (Literal->getLocStart().isMacroID())
+    return;
+
+  if (containsEscapedCharacters(Result, Literal)) {
+    std::string Replacement = asRawStringLiteral(Literal, DelimiterStem);
+    if (ReplaceShorterLiterals ||
+        Replacement.length() <=
+            Lexer::MeasureTokenLength(Literal->getLocStart(),
+                                      *Result.SourceManager, getLangOpts()))
+      replaceWithRawStringLiteral(Result, Literal, Replacement);
+  }
+}
+
+void RawStringLiteralCheck::replaceWithRawStringLiteral(
+    const MatchFinder::MatchResult &Result, const StringLiteral *Literal,
+    StringRef Replacement) {
+  CharSourceRange CharRange = Lexer::makeFileCharRange(
+      CharSourceRange::getTokenRange(Literal->getSourceRange()),
+      *Result.SourceManager, getLangOpts());
+  diag(Literal->getLocStart(),
+       "escaped string literal can be written as a raw string literal")
+      << FixItHint::CreateReplacement(CharRange, Replacement);
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/modernize/RawStringLiteralCheck.h b/clang-tidy/modernize/RawStringLiteralCheck.h
new file mode 100644 (file)
index 0000000..25c493f
--- /dev/null
@@ -0,0 +1,45 @@
+//===--- RawStringLiteralCheck.h - clang-tidy--------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_RAW_STRING_LITERAL_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_RAW_STRING_LITERAL_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+/// This check replaces string literals with escaped characters to
+/// raw string literals.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/modernize-raw-string-literal.html
+class RawStringLiteralCheck : public ClangTidyCheck {
+public:
+  RawStringLiteralCheck(StringRef Name, ClangTidyContext *Context);
+
+  void storeOptions(ClangTidyOptions::OptionMap &Options) override;
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+  void replaceWithRawStringLiteral(
+      const ast_matchers::MatchFinder::MatchResult &Result,
+      const StringLiteral *Literal, StringRef Replacement);
+
+  std::string DelimiterStem;
+  const bool ReplaceShorterLiterals;
+};
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_RAW_STRING_LITERAL_H
diff --git a/clang-tidy/modernize/RedundantVoidArgCheck.cpp b/clang-tidy/modernize/RedundantVoidArgCheck.cpp
new file mode 100644 (file)
index 0000000..6701fa4
--- /dev/null
@@ -0,0 +1,248 @@
+//===- RedundantVoidArgCheck.cpp - clang-tidy -----------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "RedundantVoidArgCheck.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+namespace {
+
+// Determine if the given QualType is a nullary function or pointer to same.
+bool protoTypeHasNoParms(QualType QT) {
+  if (auto PT = QT->getAs<PointerType>()) {
+    QT = PT->getPointeeType();
+  }
+  if (auto *MPT = QT->getAs<MemberPointerType>()) {
+    QT = MPT->getPointeeType();
+  }
+  if (auto FP = QT->getAs<FunctionProtoType>()) {
+    return FP->getNumParams() == 0;
+  }
+  return false;
+}
+
+const char FunctionId[] = "function";
+const char TypedefId[] = "typedef";
+const char FieldId[] = "field";
+const char VarId[] = "var";
+const char NamedCastId[] = "named-cast";
+const char CStyleCastId[] = "c-style-cast";
+const char ExplicitCastId[] = "explicit-cast";
+const char LambdaId[] = "lambda";
+
+} // namespace
+
+void RedundantVoidArgCheck::registerMatchers(MatchFinder *Finder) {
+  if (!getLangOpts().CPlusPlus)
+    return;
+
+  Finder->addMatcher(functionDecl(parameterCountIs(0), unless(isImplicit()),
+                                  unless(isExternC()))
+                         .bind(FunctionId),
+                     this);
+  Finder->addMatcher(typedefNameDecl().bind(TypedefId), this);
+  auto ParenFunctionType = parenType(innerType(functionType()));
+  auto PointerToFunctionType = pointee(ParenFunctionType);
+  auto FunctionOrMemberPointer =
+      anyOf(hasType(pointerType(PointerToFunctionType)),
+            hasType(memberPointerType(PointerToFunctionType)));
+  Finder->addMatcher(fieldDecl(FunctionOrMemberPointer).bind(FieldId), this);
+  Finder->addMatcher(varDecl(FunctionOrMemberPointer).bind(VarId), this);
+  auto CastDestinationIsFunction =
+      hasDestinationType(pointsTo(ParenFunctionType));
+  Finder->addMatcher(
+      cStyleCastExpr(CastDestinationIsFunction).bind(CStyleCastId), this);
+  Finder->addMatcher(
+      cxxStaticCastExpr(CastDestinationIsFunction).bind(NamedCastId), this);
+  Finder->addMatcher(
+      cxxReinterpretCastExpr(CastDestinationIsFunction).bind(NamedCastId),
+      this);
+  Finder->addMatcher(
+      cxxConstCastExpr(CastDestinationIsFunction).bind(NamedCastId), this);
+  Finder->addMatcher(lambdaExpr().bind(LambdaId), this);
+}
+
+void RedundantVoidArgCheck::check(const MatchFinder::MatchResult &Result) {
+  const BoundNodes &Nodes = Result.Nodes;
+  if (const auto *Function = Nodes.getNodeAs<FunctionDecl>(FunctionId)) {
+    processFunctionDecl(Result, Function);
+  } else if (const auto *TypedefName =
+                 Nodes.getNodeAs<TypedefNameDecl>(TypedefId)) {
+    processTypedefNameDecl(Result, TypedefName);
+  } else if (const auto *Member = Nodes.getNodeAs<FieldDecl>(FieldId)) {
+    processFieldDecl(Result, Member);
+  } else if (const auto *Var = Nodes.getNodeAs<VarDecl>(VarId)) {
+    processVarDecl(Result, Var);
+  } else if (const auto *NamedCast =
+                 Nodes.getNodeAs<CXXNamedCastExpr>(NamedCastId)) {
+    processNamedCastExpr(Result, NamedCast);
+  } else if (const auto *CStyleCast =
+                 Nodes.getNodeAs<CStyleCastExpr>(CStyleCastId)) {
+    processExplicitCastExpr(Result, CStyleCast);
+  } else if (const auto *ExplicitCast =
+                 Nodes.getNodeAs<ExplicitCastExpr>(ExplicitCastId)) {
+    processExplicitCastExpr(Result, ExplicitCast);
+  } else if (const auto *Lambda = Nodes.getNodeAs<LambdaExpr>(LambdaId)) {
+    processLambdaExpr(Result, Lambda);
+  }
+}
+
+void RedundantVoidArgCheck::processFunctionDecl(
+    const MatchFinder::MatchResult &Result, const FunctionDecl *Function) {
+  if (Function->isThisDeclarationADefinition()) {
+    const Stmt *Body = Function->getBody();
+    SourceLocation Start = Function->getLocStart();
+    SourceLocation End =
+        Body ? Body->getLocStart().getLocWithOffset(-1) : Function->getLocEnd();
+    removeVoidArgumentTokens(Result, SourceRange(Start, End),
+                             "function definition");
+  } else {
+    removeVoidArgumentTokens(Result, Function->getSourceRange(),
+                             "function declaration");
+  }
+}
+
+void RedundantVoidArgCheck::removeVoidArgumentTokens(
+    const ast_matchers::MatchFinder::MatchResult &Result, SourceRange Range,
+    StringRef GrammarLocation) {
+  CharSourceRange CharRange =
+      Lexer::makeFileCharRange(CharSourceRange::getTokenRange(Range),
+                               *Result.SourceManager, getLangOpts());
+
+  std::string DeclText =
+      Lexer::getSourceText(CharRange, *Result.SourceManager, getLangOpts())
+          .str();
+  Lexer PrototypeLexer(CharRange.getBegin(), getLangOpts(), DeclText.data(),
+                       DeclText.data(), DeclText.data() + DeclText.size());
+  enum TokenState {
+    NothingYet,
+    SawLeftParen,
+    SawVoid,
+  };
+  TokenState State = NothingYet;
+  Token VoidToken;
+  Token ProtoToken;
+  std::string Diagnostic =
+      ("redundant void argument list in " + GrammarLocation).str();
+
+  while (!PrototypeLexer.LexFromRawLexer(ProtoToken)) {
+    switch (State) {
+    case NothingYet:
+      if (ProtoToken.is(tok::TokenKind::l_paren)) {
+        State = SawLeftParen;
+      }
+      break;
+    case SawLeftParen:
+      if (ProtoToken.is(tok::TokenKind::raw_identifier) &&
+          ProtoToken.getRawIdentifier() == "void") {
+        State = SawVoid;
+        VoidToken = ProtoToken;
+      } else {
+        State = NothingYet;
+      }
+      break;
+    case SawVoid:
+      State = NothingYet;
+      if (ProtoToken.is(tok::TokenKind::r_paren)) {
+        removeVoidToken(VoidToken, Diagnostic);
+      } else if (ProtoToken.is(tok::TokenKind::l_paren)) {
+        State = SawLeftParen;
+      }
+      break;
+    }
+  }
+
+  if (State == SawVoid && ProtoToken.is(tok::TokenKind::r_paren)) {
+    removeVoidToken(VoidToken, Diagnostic);
+  }
+}
+
+void RedundantVoidArgCheck::removeVoidToken(Token VoidToken,
+                                            StringRef Diagnostic) {
+  SourceLocation VoidLoc(VoidToken.getLocation());
+  auto VoidRange =
+      CharSourceRange::getTokenRange(VoidLoc, VoidLoc.getLocWithOffset(3));
+  diag(VoidLoc, Diagnostic) << FixItHint::CreateRemoval(VoidRange);
+}
+
+void RedundantVoidArgCheck::processTypedefNameDecl(
+    const MatchFinder::MatchResult &Result,
+    const TypedefNameDecl *TypedefName) {
+  if (protoTypeHasNoParms(TypedefName->getUnderlyingType())) {
+    removeVoidArgumentTokens(Result, TypedefName->getSourceRange(),
+                             isa<TypedefDecl>(TypedefName) ? "typedef"
+                                                           : "type alias");
+  }
+}
+
+void RedundantVoidArgCheck::processFieldDecl(
+    const MatchFinder::MatchResult &Result, const FieldDecl *Member) {
+  if (protoTypeHasNoParms(Member->getType())) {
+    removeVoidArgumentTokens(Result, Member->getSourceRange(),
+                             "field declaration");
+  }
+}
+
+void RedundantVoidArgCheck::processVarDecl(
+    const MatchFinder::MatchResult &Result, const VarDecl *Var) {
+  if (protoTypeHasNoParms(Var->getType())) {
+    SourceLocation Begin = Var->getLocStart();
+    if (Var->hasInit()) {
+      SourceLocation InitStart =
+          Result.SourceManager->getExpansionLoc(Var->getInit()->getLocStart())
+              .getLocWithOffset(-1);
+      removeVoidArgumentTokens(Result, SourceRange(Begin, InitStart),
+                               "variable declaration with initializer");
+    } else {
+      removeVoidArgumentTokens(Result, Var->getSourceRange(),
+                               "variable declaration");
+    }
+  }
+}
+
+void RedundantVoidArgCheck::processNamedCastExpr(
+    const MatchFinder::MatchResult &Result, const CXXNamedCastExpr *NamedCast) {
+  if (protoTypeHasNoParms(NamedCast->getTypeAsWritten())) {
+    removeVoidArgumentTokens(
+        Result,
+        NamedCast->getTypeInfoAsWritten()->getTypeLoc().getSourceRange(),
+        "named cast");
+  }
+}
+
+void RedundantVoidArgCheck::processExplicitCastExpr(
+    const MatchFinder::MatchResult &Result,
+    const ExplicitCastExpr *ExplicitCast) {
+  if (protoTypeHasNoParms(ExplicitCast->getTypeAsWritten())) {
+    removeVoidArgumentTokens(Result, ExplicitCast->getSourceRange(),
+                             "cast expression");
+  }
+}
+
+void RedundantVoidArgCheck::processLambdaExpr(
+    const MatchFinder::MatchResult &Result, const LambdaExpr *Lambda) {
+  if (Lambda->getLambdaClass()->getLambdaCallOperator()->getNumParams() == 0 &&
+      Lambda->hasExplicitParameters()) {
+    SourceLocation Begin =
+        Lambda->getIntroducerRange().getEnd().getLocWithOffset(1);
+    SourceLocation End = Lambda->getBody()->getLocStart().getLocWithOffset(-1);
+    removeVoidArgumentTokens(Result, SourceRange(Begin, End),
+                             "lambda expression");
+  }
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/modernize/RedundantVoidArgCheck.h b/clang-tidy/modernize/RedundantVoidArgCheck.h
new file mode 100644 (file)
index 0000000..c990ef4
--- /dev/null
@@ -0,0 +1,77 @@
+//===--- RedundantVoidArgCheck.h - clang-tidy --------------------*- C++-*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_REDUNDANT_VOID_ARG_CHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_REDUNDANT_VOID_ARG_CHECK_H
+
+#include "../ClangTidy.h"
+#include "clang/Lex/Token.h"
+
+#include <string>
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+/// \brief Find and remove redundant void argument lists.
+///
+/// Examples:
+///   `int f(void);`                    becomes `int f();`
+///   `int (*f(void))(void);`           becomes `int (*f())();`
+///   `typedef int (*f_t(void))(void);` becomes `typedef int (*f_t())();`
+///   `void (C::*p)(void);`             becomes `void (C::*p)();`
+///   `C::C(void) {}`                   becomes `C::C() {}`
+///   `C::~C(void) {}`                  becomes `C::~C() {}`
+///
+class RedundantVoidArgCheck : public ClangTidyCheck {
+public:
+  RedundantVoidArgCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+  void processFunctionDecl(const ast_matchers::MatchFinder::MatchResult &Result,
+                           const FunctionDecl *Function);
+
+  void
+  processTypedefNameDecl(const ast_matchers::MatchFinder::MatchResult &Result,
+                         const TypedefNameDecl *Typedef);
+
+  void processFieldDecl(const ast_matchers::MatchFinder::MatchResult &Result,
+                        const FieldDecl *Member);
+
+  void processVarDecl(const ast_matchers::MatchFinder::MatchResult &Result,
+                      const VarDecl *Var);
+
+  void
+  processNamedCastExpr(const ast_matchers::MatchFinder::MatchResult &Result,
+                       const CXXNamedCastExpr *NamedCast);
+
+  void
+  processExplicitCastExpr(const ast_matchers::MatchFinder::MatchResult &Result,
+                          const ExplicitCastExpr *ExplicitCast);
+
+  void processLambdaExpr(const ast_matchers::MatchFinder::MatchResult &Result,
+                         const LambdaExpr *Lambda);
+
+  void
+  removeVoidArgumentTokens(const ast_matchers::MatchFinder::MatchResult &Result,
+                           SourceRange Range, StringRef GrammarLocation);
+
+  void removeVoidToken(Token VoidToken, StringRef Diagnostic);
+};
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_REDUNDANT_VOID_ARG_CHECK_H
diff --git a/clang-tidy/modernize/ReplaceAutoPtrCheck.cpp b/clang-tidy/modernize/ReplaceAutoPtrCheck.cpp
new file mode 100644 (file)
index 0000000..969a55b
--- /dev/null
@@ -0,0 +1,199 @@
+//===--- ReplaceAutoPtrCheck.cpp - clang-tidy------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ReplaceAutoPtrCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Lex/Preprocessor.h"
+
+using namespace clang;
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+static const char AutoPtrTokenId[] = "AutoPrTokenId";
+static const char AutoPtrOwnershipTransferId[] = "AutoPtrOwnershipTransferId";
+
+/// \brief Matches expressions that are lvalues.
+///
+/// In the following example, a[0] matches expr(isLValue()):
+/// \code
+///   std::string a[2];
+///   std::string b;
+///   b = a[0];
+///   b = "this string won't match";
+/// \endcode
+AST_MATCHER(Expr, isLValue) { return Node.getValueKind() == VK_LValue; }
+
+/// Matches declarations whose declaration context is the C++ standard library
+/// namespace std.
+///
+/// Note that inline namespaces are silently ignored during the lookup since
+/// both libstdc++ and libc++ are known to use them for versioning purposes.
+///
+/// Given:
+/// \code
+///   namespace ns {
+///     struct my_type {};
+///     using namespace std;
+///   }
+///
+///   using std::vector;
+///   using ns:my_type;
+///   using ns::list;
+/// \code
+///
+/// usingDecl(hasAnyUsingShadowDecl(hasTargetDecl(isFromStdNamespace())))
+/// matches "using std::vector" and "using ns::list".
+AST_MATCHER(Decl, isFromStdNamespace) {
+  const DeclContext *D = Node.getDeclContext();
+
+  while (D->isInlineNamespace())
+    D = D->getParent();
+
+  if (!D->isNamespace() || !D->getParent()->isTranslationUnit())
+    return false;
+
+  const IdentifierInfo *Info = cast<NamespaceDecl>(D)->getIdentifier();
+
+  return (Info && Info->isStr("std"));
+}
+
+ReplaceAutoPtrCheck::ReplaceAutoPtrCheck(StringRef Name,
+                                         ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      IncludeStyle(utils::IncludeSorter::parseIncludeStyle(
+          Options.get("IncludeStyle", "llvm"))) {}
+
+void ReplaceAutoPtrCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "IncludeStyle",
+                utils::IncludeSorter::toString(IncludeStyle));
+}
+
+void ReplaceAutoPtrCheck::registerMatchers(MatchFinder *Finder) {
+  // Only register the matchers for C++; the functionality currently does not
+  // provide any benefit to other languages, despite being benign.
+  if (!getLangOpts().CPlusPlus)
+    return;
+
+  auto AutoPtrDecl = recordDecl(hasName("auto_ptr"), isFromStdNamespace());
+  auto AutoPtrType = qualType(hasDeclaration(AutoPtrDecl));
+
+  //   std::auto_ptr<int> a;
+  //        ^~~~~~~~~~~~~
+  //
+  //   typedef std::auto_ptr<int> int_ptr_t;
+  //                ^~~~~~~~~~~~~
+  //
+  //   std::auto_ptr<int> fn(std::auto_ptr<int>);
+  //        ^~~~~~~~~~~~~         ^~~~~~~~~~~~~
+  Finder->addMatcher(typeLoc(loc(qualType(AutoPtrType,
+                                          // Skip elaboratedType() as the named
+                                          // type will match soon thereafter.
+                                          unless(elaboratedType()))))
+                         .bind(AutoPtrTokenId),
+                     this);
+
+  //   using std::auto_ptr;
+  //   ^~~~~~~~~~~~~~~~~~~
+  Finder->addMatcher(usingDecl(hasAnyUsingShadowDecl(hasTargetDecl(allOf(
+                                   hasName("auto_ptr"), isFromStdNamespace()))))
+                         .bind(AutoPtrTokenId),
+                     this);
+
+  // Find ownership transfers via copy construction and assignment.
+  // AutoPtrOwnershipTransferId is bound to the the part that has to be wrapped
+  // into std::move().
+  //   std::auto_ptr<int> i, j;
+  //   i = j;
+  //   ~~~~^
+  auto MovableArgumentMatcher =
+      expr(isLValue(), hasType(AutoPtrType)).bind(AutoPtrOwnershipTransferId);
+
+  Finder->addMatcher(
+      cxxOperatorCallExpr(hasOverloadedOperatorName("="),
+                          callee(cxxMethodDecl(ofClass(AutoPtrDecl))),
+                          hasArgument(1, MovableArgumentMatcher)),
+      this);
+  Finder->addMatcher(cxxConstructExpr(hasType(AutoPtrType), argumentCountIs(1),
+                                      hasArgument(0, MovableArgumentMatcher)),
+                     this);
+}
+
+void ReplaceAutoPtrCheck::registerPPCallbacks(CompilerInstance &Compiler) {
+  // Only register the preprocessor callbacks for C++; the functionality
+  // currently does not provide any benefit to other languages, despite being
+  // benign.
+  if (!getLangOpts().CPlusPlus)
+    return;
+  Inserter.reset(new utils::IncludeInserter(
+      Compiler.getSourceManager(), Compiler.getLangOpts(), IncludeStyle));
+  Compiler.getPreprocessor().addPPCallbacks(Inserter->CreatePPCallbacks());
+}
+
+void ReplaceAutoPtrCheck::check(const MatchFinder::MatchResult &Result) {
+  SourceManager &SM = *Result.SourceManager;
+  if (const auto *E =
+          Result.Nodes.getNodeAs<Expr>(AutoPtrOwnershipTransferId)) {
+    CharSourceRange Range = Lexer::makeFileCharRange(
+        CharSourceRange::getTokenRange(E->getSourceRange()), SM, LangOptions());
+
+    if (Range.isInvalid())
+      return;
+
+    auto Diag = diag(Range.getBegin(), "use std::move to transfer ownership")
+                << FixItHint::CreateInsertion(Range.getBegin(), "std::move(")
+                << FixItHint::CreateInsertion(Range.getEnd(), ")");
+
+    if (auto Fix =
+            Inserter->CreateIncludeInsertion(SM.getMainFileID(), "utility",
+                                             /*IsAngled=*/true))
+      Diag << *Fix;
+
+    return;
+  }
+
+  SourceLocation AutoPtrLoc;
+  if (const auto *TL = Result.Nodes.getNodeAs<TypeLoc>(AutoPtrTokenId)) {
+    //   std::auto_ptr<int> i;
+    //        ^
+    if (auto Loc = TL->getAs<TemplateSpecializationTypeLoc>())
+      AutoPtrLoc = Loc.getTemplateNameLoc();
+  } else if (const auto *D =
+                 Result.Nodes.getNodeAs<UsingDecl>(AutoPtrTokenId)) {
+    // using std::auto_ptr;
+    //            ^
+    AutoPtrLoc = D->getNameInfo().getBeginLoc();
+  } else {
+    llvm_unreachable("Bad Callback. No node provided.");
+  }
+
+  if (AutoPtrLoc.isMacroID())
+    AutoPtrLoc = SM.getSpellingLoc(AutoPtrLoc);
+
+  // Ensure that only the 'auto_ptr' token is replaced and not the template
+  // aliases.
+  if (StringRef(SM.getCharacterData(AutoPtrLoc), strlen("auto_ptr")) !=
+      "auto_ptr")
+    return;
+
+  SourceLocation EndLoc =
+      AutoPtrLoc.getLocWithOffset(strlen("auto_ptr") - 1);
+  diag(AutoPtrLoc, "auto_ptr is deprecated, use unique_ptr instead")
+      << FixItHint::CreateReplacement(SourceRange(AutoPtrLoc, EndLoc),
+                                      "unique_ptr");
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/modernize/ReplaceAutoPtrCheck.h b/clang-tidy/modernize/ReplaceAutoPtrCheck.h
new file mode 100644 (file)
index 0000000..5b73d51
--- /dev/null
@@ -0,0 +1,61 @@
+//===--- ReplaceAutoPtrCheck.h - clang-tidy----------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_REPLACE_AUTO_PTR_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_REPLACE_AUTO_PTR_H
+
+#include "../ClangTidy.h"
+#include "../utils/IncludeInserter.h"
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+/// Transforms the deprecated `std::auto_ptr` into the C++11 `std::unique_ptr`.
+///
+/// Note that both the `std::auto_ptr` type and the transfer of ownership are
+/// transformed. `std::auto_ptr` provides two ways to transfer the ownership,
+/// the copy-constructor and the assignment operator. Unlike most classes these
+/// operations do not 'copy' the resource but they 'steal' it.
+/// `std::unique_ptr` uses move semantics instead, which makes the intent of
+/// transferring the resource explicit. This difference between the two smart
+/// pointers requeres to wrap the copy-ctor and assign-operator with
+/// `std::move()`.
+///
+/// For example, given:
+///
+/// \code
+///   std::auto_ptr<int> i, j;
+///   i = j;
+/// \endcode
+///
+/// This code is transformed to:
+///
+/// \code
+///   std::unique_ptr<in> i, j;
+///   i = std::move(j);
+/// \endcode
+class ReplaceAutoPtrCheck : public ClangTidyCheck {
+public:
+  ReplaceAutoPtrCheck(StringRef Name, ClangTidyContext *Context);
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void registerPPCallbacks(CompilerInstance &Compiler) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+  std::unique_ptr<utils::IncludeInserter> Inserter;
+  const utils::IncludeSorter::IncludeStyle IncludeStyle;
+};
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_REPLACE_AUTO_PTR_H
diff --git a/clang-tidy/modernize/ReplaceRandomShuffleCheck.cpp b/clang-tidy/modernize/ReplaceRandomShuffleCheck.cpp
new file mode 100644 (file)
index 0000000..909a171
--- /dev/null
@@ -0,0 +1,109 @@
+//===--- ReplaceRandomShuffleCheck.cpp - clang-tidy------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ReplaceRandomShuffleCheck.h"
+#include "../utils/FixItHintUtils.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Tooling/FixIt.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+ReplaceRandomShuffleCheck::ReplaceRandomShuffleCheck(StringRef Name,
+                                                     ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      IncludeStyle(utils::IncludeSorter::parseIncludeStyle(
+          Options.get("IncludeStyle", "llvm"))) {}
+
+void ReplaceRandomShuffleCheck::registerMatchers(MatchFinder *Finder) {
+  if (!getLangOpts().CPlusPlus11)
+    return;
+
+  const auto Begin = hasArgument(0, expr());
+  const auto End = hasArgument(1, expr());
+  const auto RandomFunc = hasArgument(2, expr().bind("randomFunc"));
+  Finder->addMatcher(
+      callExpr(anyOf(allOf(Begin, End, argumentCountIs(2)),
+                     allOf(Begin, End, RandomFunc, argumentCountIs(3))),
+               hasDeclaration(functionDecl(hasName("::std::random_shuffle"))),
+               has(implicitCastExpr(has(declRefExpr().bind("name")))))
+          .bind("match"),
+      this);
+}
+
+void ReplaceRandomShuffleCheck::registerPPCallbacks(
+    CompilerInstance &Compiler) {
+  IncludeInserter = llvm::make_unique<utils::IncludeInserter>(
+      Compiler.getSourceManager(), Compiler.getLangOpts(), IncludeStyle);
+  Compiler.getPreprocessor().addPPCallbacks(
+      IncludeInserter->CreatePPCallbacks());
+}
+
+void ReplaceRandomShuffleCheck::storeOptions(
+    ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "IncludeStyle",
+                utils::IncludeSorter::toString(IncludeStyle));
+}
+
+void ReplaceRandomShuffleCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *MatchedDecl = Result.Nodes.getNodeAs<DeclRefExpr>("name");
+  const auto *MatchedArgumentThree = Result.Nodes.getNodeAs<Expr>("randomFunc");
+  const auto *MatchedCallExpr = Result.Nodes.getNodeAs<CallExpr>("match");
+
+  if (MatchedCallExpr->getLocStart().isMacroID())
+    return;
+
+  auto Diag = [&] {
+    if (MatchedCallExpr->getNumArgs() == 3) {
+      auto DiagL =
+          diag(MatchedCallExpr->getLocStart(),
+               "'std::random_shuffle' has been removed in C++17; use "
+               "'std::shuffle' and an alternative random mechanism instead");
+      DiagL << FixItHint::CreateReplacement(
+          MatchedArgumentThree->getSourceRange(),
+          "std::mt19937(std::random_device()())");
+      return DiagL;
+    } else {
+      auto DiagL = diag(MatchedCallExpr->getLocStart(),
+                        "'std::random_shuffle' has been removed in C++17; use "
+                        "'std::shuffle' instead");
+      DiagL << FixItHint::CreateInsertion(
+          MatchedCallExpr->getRParenLoc(),
+          ", std::mt19937(std::random_device()())");
+      return DiagL;
+    }
+  }();
+
+  std::string NewName = "shuffle";
+  StringRef ContainerText = Lexer::getSourceText(
+      CharSourceRange::getTokenRange(MatchedDecl->getSourceRange()),
+      *Result.SourceManager, getLangOpts());
+  if (ContainerText.startswith("std::"))
+    NewName = "std::" + NewName;
+
+  Diag << FixItHint::CreateRemoval(MatchedDecl->getSourceRange());
+  Diag << FixItHint::CreateInsertion(MatchedDecl->getLocStart(), NewName);
+
+  if (Optional<FixItHint> IncludeFixit =
+          IncludeInserter->CreateIncludeInsertion(
+              Result.Context->getSourceManager().getFileID(
+                  MatchedCallExpr->getLocStart()),
+              "random", /*IsAngled=*/true))
+    Diag << IncludeFixit.getValue();
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/modernize/ReplaceRandomShuffleCheck.h b/clang-tidy/modernize/ReplaceRandomShuffleCheck.h
new file mode 100644 (file)
index 0000000..050d740
--- /dev/null
@@ -0,0 +1,42 @@
+//===--- ReplaceRandomShuffleCheck.h - clang-tidy----------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_REPLACE_RANDOM_SHUFFLE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_REPLACE_RANDOM_SHUFFLE_H
+
+#include "../ClangTidy.h"
+#include "../utils/IncludeInserter.h"
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+/// std::random_shuffle will be removed as of C++17. This check will find and
+/// replace all occurrences of std::random_shuffle with std::shuffle.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/modernize-replace-random-shuffle.html
+class ReplaceRandomShuffleCheck : public ClangTidyCheck {
+public:
+  ReplaceRandomShuffleCheck(StringRef Name, ClangTidyContext *Context);
+  void registerPPCallbacks(CompilerInstance &Compiler) override;
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+  std::unique_ptr<utils::IncludeInserter> IncludeInserter;
+  const utils::IncludeSorter::IncludeStyle IncludeStyle;
+};
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_REPLACE_RANDOM_SHUFFLE_H
diff --git a/clang-tidy/modernize/ReturnBracedInitListCheck.cpp b/clang-tidy/modernize/ReturnBracedInitListCheck.cpp
new file mode 100644 (file)
index 0000000..e5857f7
--- /dev/null
@@ -0,0 +1,97 @@
+//===--- ReturnBracedInitListCheck.cpp - clang-tidy------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ReturnBracedInitListCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Tooling/FixIt.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+void ReturnBracedInitListCheck::registerMatchers(MatchFinder *Finder) {
+  // Only register the matchers for C++.
+  if (!getLangOpts().CPlusPlus11)
+    return;
+
+  // Skip list initialization and constructors with an initializer list.
+  auto ConstructExpr =
+      cxxConstructExpr(
+          unless(anyOf(hasDeclaration(cxxConstructorDecl(isExplicit())),
+                       isListInitialization(), hasDescendant(initListExpr()),
+                       isInTemplateInstantiation())))
+          .bind("ctor");
+
+  auto CtorAsArgument = materializeTemporaryExpr(anyOf(
+      has(ConstructExpr), has(cxxFunctionalCastExpr(has(ConstructExpr)))));
+
+  Finder->addMatcher(
+      functionDecl(isDefinition(), // Declarations don't have return statements.
+                   returns(unless(anyOf(builtinType(), autoType()))),
+                   hasDescendant(returnStmt(hasReturnValue(
+                       has(cxxConstructExpr(has(CtorAsArgument)))))))
+          .bind("fn"),
+      this);
+}
+
+void ReturnBracedInitListCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *MatchedFunctionDecl = Result.Nodes.getNodeAs<FunctionDecl>("fn");
+  const auto *MatchedConstructExpr =
+      Result.Nodes.getNodeAs<CXXConstructExpr>("ctor");
+
+  // Don't make replacements in macro.
+  SourceLocation Loc = MatchedConstructExpr->getExprLoc();
+  if (Loc.isMacroID())
+    return;
+
+  // Make sure that the return type matches the constructed type.
+  const QualType ReturnType =
+      MatchedFunctionDecl->getReturnType().getCanonicalType();
+  const QualType ConstructType =
+      MatchedConstructExpr->getType().getCanonicalType();
+  if (ReturnType != ConstructType)
+    return;
+
+  auto Diag = diag(Loc, "avoid repeating the return type from the "
+                        "declaration; use a braced initializer list instead");
+
+  const SourceRange CallParensRange =
+      MatchedConstructExpr->getParenOrBraceRange();
+
+  // Make sure there is an explicit constructor call.
+  if (CallParensRange.isInvalid())
+    return;
+
+  // Make sure that the ctor arguments match the declaration.
+  for (unsigned I = 0, NumParams = MatchedConstructExpr->getNumArgs();
+       I < NumParams; ++I) {
+    if (const auto *VD = dyn_cast<VarDecl>(
+            MatchedConstructExpr->getConstructor()->getParamDecl(I))) {
+      if (MatchedConstructExpr->getArg(I)->getType().getCanonicalType() !=
+          VD->getType().getCanonicalType())
+        return;
+    }
+  }
+
+  // Range for constructor name and opening brace.
+  CharSourceRange CtorCallSourceRange = CharSourceRange::getTokenRange(
+      Loc, CallParensRange.getBegin().getLocWithOffset(-1));
+
+  Diag << FixItHint::CreateRemoval(CtorCallSourceRange)
+       << FixItHint::CreateReplacement(CallParensRange.getBegin(), "{")
+       << FixItHint::CreateReplacement(CallParensRange.getEnd(), "}");
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/modernize/ReturnBracedInitListCheck.h b/clang-tidy/modernize/ReturnBracedInitListCheck.h
new file mode 100644 (file)
index 0000000..eda982a
--- /dev/null
@@ -0,0 +1,36 @@
+//===--- ReturnBracedInitListCheck.h - clang-tidy----------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_RETURN_BRACED_INIT_LIST_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_RETURN_BRACED_INIT_LIST_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+/// Use a braced init list for return statements rather than unnecessary
+/// repeating the return type name.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/modernize-return-braced-init-list.html
+class ReturnBracedInitListCheck : public ClangTidyCheck {
+public:
+  ReturnBracedInitListCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_RETURN_BRACED_INIT_LIST_H
diff --git a/clang-tidy/modernize/ShrinkToFitCheck.cpp b/clang-tidy/modernize/ShrinkToFitCheck.cpp
new file mode 100644 (file)
index 0000000..ef92018
--- /dev/null
@@ -0,0 +1,90 @@
+//===--- ShrinkToFitCheck.cpp - clang-tidy---------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ShrinkToFitCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+#include "llvm/ADT/StringRef.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+void ShrinkToFitCheck::registerMatchers(MatchFinder *Finder) {
+  if (!getLangOpts().CPlusPlus11)
+    return;
+
+  // Swap as a function need not to be considered, because rvalue can not
+  // be bound to a non-const reference.
+  const auto ShrinkableAsMember =
+      memberExpr(member(valueDecl().bind("ContainerDecl")));
+  const auto ShrinkableAsDecl =
+      declRefExpr(hasDeclaration(valueDecl().bind("ContainerDecl")));
+  const auto CopyCtorCall = cxxConstructExpr(hasArgument(
+      0, anyOf(ShrinkableAsMember, ShrinkableAsDecl,
+               unaryOperator(has(ignoringParenImpCasts(ShrinkableAsMember))),
+               unaryOperator(has(ignoringParenImpCasts(ShrinkableAsDecl))))));
+  const auto SwapParam =
+      expr(anyOf(memberExpr(member(equalsBoundNode("ContainerDecl"))),
+                 declRefExpr(hasDeclaration(equalsBoundNode("ContainerDecl"))),
+                 unaryOperator(has(ignoringParenImpCasts(
+                     memberExpr(member(equalsBoundNode("ContainerDecl")))))),
+                 unaryOperator(has(ignoringParenImpCasts(declRefExpr(
+                     hasDeclaration(equalsBoundNode("ContainerDecl"))))))));
+
+  Finder->addMatcher(
+      cxxMemberCallExpr(
+          on(hasType(namedDecl(
+              hasAnyName("std::basic_string", "std::deque", "std::vector")))),
+          callee(cxxMethodDecl(hasName("swap"))),
+          has(ignoringParenImpCasts(memberExpr(hasDescendant(CopyCtorCall)))),
+          hasArgument(0, SwapParam.bind("ContainerToShrink")),
+          unless(isInTemplateInstantiation()))
+          .bind("CopyAndSwapTrick"),
+      this);
+}
+
+void ShrinkToFitCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *MemberCall =
+      Result.Nodes.getNodeAs<CXXMemberCallExpr>("CopyAndSwapTrick");
+  const auto *Container = Result.Nodes.getNodeAs<Expr>("ContainerToShrink");
+  FixItHint Hint;
+
+  if (!MemberCall->getLocStart().isMacroID()) {
+    const LangOptions &Opts = getLangOpts();
+    std::string ReplacementText;
+    if (const auto *UnaryOp = llvm::dyn_cast<UnaryOperator>(Container)) {
+      ReplacementText =
+          Lexer::getSourceText(CharSourceRange::getTokenRange(
+                                   UnaryOp->getSubExpr()->getSourceRange()),
+                               *Result.SourceManager, Opts);
+      ReplacementText += "->shrink_to_fit()";
+    } else {
+      ReplacementText = Lexer::getSourceText(
+          CharSourceRange::getTokenRange(Container->getSourceRange()),
+          *Result.SourceManager, Opts);
+      ReplacementText += ".shrink_to_fit()";
+    }
+
+    Hint = FixItHint::CreateReplacement(MemberCall->getSourceRange(),
+                                        ReplacementText);
+  }
+
+  diag(MemberCall->getLocStart(), "the shrink_to_fit method should be used "
+                                  "to reduce the capacity of a shrinkable "
+                                  "container")
+      << Hint;
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/modernize/ShrinkToFitCheck.h b/clang-tidy/modernize/ShrinkToFitCheck.h
new file mode 100644 (file)
index 0000000..1e3745c
--- /dev/null
@@ -0,0 +1,37 @@
+//===--- ShrinkToFitCheck.h - clang-tidy-------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_SHRINKTOFITCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_SHRINKTOFITCHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+/// Replace copy and swap tricks on shrinkable containers with the
+/// `shrink_to_fit()` method call.
+///
+/// The `shrink_to_fit()` method is more readable and more effective than
+/// the copy and swap trick to reduce the capacity of a shrinkable container.
+/// Note that, the `shrink_to_fit()` method is only available in C++11 and up.
+class ShrinkToFitCheck : public ClangTidyCheck {
+public:
+  ShrinkToFitCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_SHRINKTOFITCHECK_H
diff --git a/clang-tidy/modernize/UnaryStaticAssertCheck.cpp b/clang-tidy/modernize/UnaryStaticAssertCheck.cpp
new file mode 100644 (file)
index 0000000..c2129e3
--- /dev/null
@@ -0,0 +1,45 @@
+//===--- UnaryStaticAssertCheck.cpp - clang-tidy---------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "UnaryStaticAssertCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+void UnaryStaticAssertCheck::registerMatchers(MatchFinder *Finder) {
+  if (!getLangOpts().CPlusPlus1z)
+    return;
+
+  Finder->addMatcher(staticAssertDecl().bind("static_assert"), this);
+}
+
+void UnaryStaticAssertCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *MatchedDecl =
+      Result.Nodes.getNodeAs<StaticAssertDecl>("static_assert");
+  const StringLiteral *AssertMessage = MatchedDecl->getMessage();
+
+  SourceLocation Loc = MatchedDecl->getLocation();
+
+  if (!AssertMessage || AssertMessage->getLength() ||
+      AssertMessage->getLocStart().isMacroID() || Loc.isMacroID())
+    return;
+
+  diag(Loc,
+       "use unary 'static_assert' when the string literal is an empty string")
+      << FixItHint::CreateRemoval(AssertMessage->getSourceRange());
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/modernize/UnaryStaticAssertCheck.h b/clang-tidy/modernize/UnaryStaticAssertCheck.h
new file mode 100644 (file)
index 0000000..b83c2c4
--- /dev/null
@@ -0,0 +1,36 @@
+//===--- UnaryStaticAssertCheck.h - clang-tidy-------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_UNARY_STATIC_ASSERT_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_UNARY_STATIC_ASSERT_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+/// Replaces a static_assert declaration with an empty message
+/// with the unary version.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/modernize-unary-static-assert.html
+class UnaryStaticAssertCheck : public ClangTidyCheck {
+public:
+  UnaryStaticAssertCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_UNARY_STATIC_ASSERT_H
diff --git a/clang-tidy/modernize/UseAutoCheck.cpp b/clang-tidy/modernize/UseAutoCheck.cpp
new file mode 100644 (file)
index 0000000..d1361a0
--- /dev/null
@@ -0,0 +1,463 @@
+//===--- UseAutoCheck.cpp - clang-tidy-------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "UseAutoCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+
+using namespace clang;
+using namespace clang::ast_matchers;
+using namespace clang::ast_matchers::internal;
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+namespace {
+
+const char IteratorDeclStmtId[] = "iterator_decl";
+const char DeclWithNewId[] = "decl_new";
+const char DeclWithCastId[] = "decl_cast";
+const char DeclWithTemplateCastId[] = "decl_template";
+
+/// \brief Matches variable declarations that have explicit initializers that
+/// are not initializer lists.
+///
+/// Given
+/// \code
+///   iterator I = Container.begin();
+///   MyType A(42);
+///   MyType B{2};
+///   MyType C;
+/// \endcode
+///
+/// varDecl(hasWrittenNonListInitializer()) maches \c I and \c A but not \c B
+/// or \c C.
+AST_MATCHER(VarDecl, hasWrittenNonListInitializer) {
+  const Expr *Init = Node.getAnyInitializer();
+  if (!Init)
+    return false;
+
+  Init = Init->IgnoreImplicit();
+
+  // The following test is based on DeclPrinter::VisitVarDecl() to find if an
+  // initializer is implicit or not.
+  if (const auto *Construct = dyn_cast<CXXConstructExpr>(Init)) {
+    return !Construct->isListInitialization() && Construct->getNumArgs() > 0 &&
+           !Construct->getArg(0)->isDefaultArgument();
+  }
+  return Node.getInitStyle() != VarDecl::ListInit;
+}
+
+/// \brief Matches QualTypes that are type sugar for QualTypes that match \c
+/// SugarMatcher.
+///
+/// Given
+/// \code
+///   class C {};
+///   typedef C my_type;
+///   typedef my_type my_other_type;
+/// \endcode
+///
+/// qualType(isSugarFor(recordType(hasDeclaration(namedDecl(hasName("C"))))))
+/// matches \c my_type and \c my_other_type.
+AST_MATCHER_P(QualType, isSugarFor, Matcher<QualType>, SugarMatcher) {
+  QualType QT = Node;
+  while (true) {
+    if (SugarMatcher.matches(QT, Finder, Builder))
+      return true;
+
+    QualType NewQT = QT.getSingleStepDesugaredType(Finder->getASTContext());
+    if (NewQT == QT)
+      return false;
+    QT = NewQT;
+  }
+}
+
+/// \brief Matches named declarations that have one of the standard iterator
+/// names: iterator, reverse_iterator, const_iterator, const_reverse_iterator.
+///
+/// Given
+/// \code
+///   iterator I;
+///   const_iterator CI;
+/// \endcode
+///
+/// namedDecl(hasStdIteratorName()) matches \c I and \c CI.
+AST_MATCHER(NamedDecl, hasStdIteratorName) {
+  static const char *const IteratorNames[] = {"iterator", "reverse_iterator",
+                                              "const_iterator",
+                                              "const_reverse_iterator"};
+
+  for (const char *Name : IteratorNames) {
+    if (hasName(Name).matches(Node, Finder, Builder))
+      return true;
+  }
+  return false;
+}
+
+/// \brief Matches named declarations that have one of the standard container
+/// names.
+///
+/// Given
+/// \code
+///   class vector {};
+///   class forward_list {};
+///   class my_ver{};
+/// \endcode
+///
+/// recordDecl(hasStdContainerName()) matches \c vector and \c forward_list
+/// but not \c my_vec.
+AST_MATCHER(NamedDecl, hasStdContainerName) {
+  static const char *const ContainerNames[] = {
+      "array",         "deque",
+      "forward_list",  "list",
+      "vector",
+
+      "map",           "multimap",
+      "set",           "multiset",
+
+      "unordered_map", "unordered_multimap",
+      "unordered_set", "unordered_multiset",
+
+      "queue",         "priority_queue",
+      "stack"};
+
+  for (const char *Name : ContainerNames) {
+    if (hasName(Name).matches(Node, Finder, Builder))
+      return true;
+  }
+  return false;
+}
+
+/// Matches declarations whose declaration context is the C++ standard library
+/// namespace std.
+///
+/// Note that inline namespaces are silently ignored during the lookup since
+/// both libstdc++ and libc++ are known to use them for versioning purposes.
+///
+/// Given:
+/// \code
+///   namespace ns {
+///     struct my_type {};
+///     using namespace std;
+///   }
+///
+///   using std::vector;
+///   using ns:my_type;
+///   using ns::list;
+/// \code
+///
+/// usingDecl(hasAnyUsingShadowDecl(hasTargetDecl(isFromStdNamespace())))
+/// matches "using std::vector" and "using ns::list".
+AST_MATCHER(Decl, isFromStdNamespace) {
+  const DeclContext *D = Node.getDeclContext();
+
+  while (D->isInlineNamespace())
+    D = D->getParent();
+
+  if (!D->isNamespace() || !D->getParent()->isTranslationUnit())
+    return false;
+
+  const IdentifierInfo *Info = cast<NamespaceDecl>(D)->getIdentifier();
+
+  return (Info && Info->isStr("std"));
+}
+
+/// Matches declaration reference or member expressions with explicit template
+/// arguments.
+AST_POLYMORPHIC_MATCHER(hasExplicitTemplateArgs,
+                        AST_POLYMORPHIC_SUPPORTED_TYPES(DeclRefExpr,
+                                                        MemberExpr)) {
+  return Node.hasExplicitTemplateArgs();
+}
+
+/// \brief Returns a DeclarationMatcher that matches standard iterators nested
+/// inside records with a standard container name.
+DeclarationMatcher standardIterator() {
+  return allOf(
+      namedDecl(hasStdIteratorName()),
+      hasDeclContext(recordDecl(hasStdContainerName(), isFromStdNamespace())));
+}
+
+/// \brief Returns a TypeMatcher that matches typedefs for standard iterators
+/// inside records with a standard container name.
+TypeMatcher typedefIterator() {
+  return typedefType(hasDeclaration(standardIterator()));
+}
+
+/// \brief Returns a TypeMatcher that matches records named for standard
+/// iterators nested inside records named for standard containers.
+TypeMatcher nestedIterator() {
+  return recordType(hasDeclaration(standardIterator()));
+}
+
+/// \brief Returns a TypeMatcher that matches types declared with using
+/// declarations and which name standard iterators for standard containers.
+TypeMatcher iteratorFromUsingDeclaration() {
+  auto HasIteratorDecl = hasDeclaration(namedDecl(hasStdIteratorName()));
+  // Types resulting from using declarations are represented by elaboratedType.
+  return elaboratedType(allOf(
+      // Unwrap the nested name specifier to test for one of the standard
+      // containers.
+      hasQualifier(specifiesType(templateSpecializationType(hasDeclaration(
+          namedDecl(hasStdContainerName(), isFromStdNamespace()))))),
+      // the named type is what comes after the final '::' in the type. It
+      // should name one of the standard iterator names.
+      namesType(
+          anyOf(typedefType(HasIteratorDecl), recordType(HasIteratorDecl)))));
+}
+
+/// \brief This matcher returns declaration statements that contain variable
+/// declarations with written non-list initializer for standard iterators.
+StatementMatcher makeIteratorDeclMatcher() {
+  return declStmt(unless(has(
+                      varDecl(anyOf(unless(hasWrittenNonListInitializer()),
+                                    unless(hasType(isSugarFor(anyOf(
+                                        typedefIterator(), nestedIterator(),
+                                        iteratorFromUsingDeclaration())))))))))
+      .bind(IteratorDeclStmtId);
+}
+
+StatementMatcher makeDeclWithNewMatcher() {
+  return declStmt(
+             unless(has(varDecl(anyOf(
+                 unless(hasInitializer(ignoringParenImpCasts(cxxNewExpr()))),
+                 // FIXME: TypeLoc information is not reliable where CV
+                 // qualifiers are concerned so these types can't be
+                 // handled for now.
+                 hasType(pointerType(
+                     pointee(hasCanonicalType(hasLocalQualifiers())))),
+
+                 // FIXME: Handle function pointers. For now we ignore them
+                 // because the replacement replaces the entire type
+                 // specifier source range which includes the identifier.
+                 hasType(pointsTo(
+                     pointsTo(parenType(innerType(functionType()))))))))))
+      .bind(DeclWithNewId);
+}
+
+StatementMatcher makeDeclWithCastMatcher() {
+  return declStmt(
+             unless(has(varDecl(unless(hasInitializer(explicitCastExpr()))))))
+      .bind(DeclWithCastId);
+}
+
+StatementMatcher makeDeclWithTemplateCastMatcher() {
+  auto ST =
+      substTemplateTypeParmType(hasReplacementType(equalsBoundNode("arg")));
+
+  auto ExplicitCall =
+      anyOf(has(memberExpr(hasExplicitTemplateArgs())),
+            has(ignoringImpCasts(declRefExpr(hasExplicitTemplateArgs()))));
+
+  auto TemplateArg =
+      hasTemplateArgument(0, refersToType(qualType().bind("arg")));
+
+  auto TemplateCall = callExpr(
+      ExplicitCall,
+      callee(functionDecl(TemplateArg,
+                          returns(anyOf(ST, pointsTo(ST), references(ST))))));
+
+  return declStmt(unless(has(varDecl(
+                      unless(hasInitializer(ignoringImplicit(TemplateCall)))))))
+      .bind(DeclWithTemplateCastId);
+}
+
+StatementMatcher makeCombinedMatcher() {
+  return declStmt(
+      // At least one varDecl should be a child of the declStmt to ensure
+      // it's a declaration list and avoid matching other declarations,
+      // e.g. using directives.
+      has(varDecl(unless(isImplicit()))),
+      // Skip declarations that are already using auto.
+      unless(has(varDecl(anyOf(hasType(autoType()),
+                               hasType(qualType(hasDescendant(autoType()))))))),
+      anyOf(makeIteratorDeclMatcher(), makeDeclWithNewMatcher(),
+            makeDeclWithCastMatcher(), makeDeclWithTemplateCastMatcher()));
+}
+
+} // namespace
+
+UseAutoCheck::UseAutoCheck(StringRef Name, ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      RemoveStars(Options.get("RemoveStars", 0)) {}
+
+void UseAutoCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "RemoveStars", RemoveStars ? 1 : 0);
+}
+
+void UseAutoCheck::registerMatchers(MatchFinder *Finder) {
+  // Only register the matchers for C++; the functionality currently does not
+  // provide any benefit to other languages, despite being benign.
+  if (getLangOpts().CPlusPlus) {
+    Finder->addMatcher(makeCombinedMatcher(), this);
+  }
+}
+
+void UseAutoCheck::replaceIterators(const DeclStmt *D, ASTContext *Context) {
+  for (const auto *Dec : D->decls()) {
+    const auto *V = cast<VarDecl>(Dec);
+    const Expr *ExprInit = V->getInit();
+
+    // Skip expressions with cleanups from the intializer expression.
+    if (const auto *E = dyn_cast<ExprWithCleanups>(ExprInit))
+      ExprInit = E->getSubExpr();
+
+    const auto *Construct = dyn_cast<CXXConstructExpr>(ExprInit);
+    if (!Construct)
+      continue;
+
+    // Ensure that the constructor receives a single argument.
+    if (Construct->getNumArgs() != 1)
+      return;
+
+    // Drill down to the as-written initializer.
+    const Expr *E = (*Construct->arg_begin())->IgnoreParenImpCasts();
+    if (E != E->IgnoreConversionOperator()) {
+      // We hit a conversion operator. Early-out now as they imply an implicit
+      // conversion from a different type. Could also mean an explicit
+      // conversion from the same type but that's pretty rare.
+      return;
+    }
+
+    if (const auto *NestedConstruct = dyn_cast<CXXConstructExpr>(E)) {
+      // If we ran into an implicit conversion contructor, can't convert.
+      //
+      // FIXME: The following only checks if the constructor can be used
+      // implicitly, not if it actually was. Cases where the converting
+      // constructor was used explicitly won't get converted.
+      if (NestedConstruct->getConstructor()->isConvertingConstructor(false))
+        return;
+    }
+    if (!Context->hasSameType(V->getType(), E->getType()))
+      return;
+  }
+
+  // Get the type location using the first declaration.
+  const auto *V = cast<VarDecl>(*D->decl_begin());
+
+  // WARNING: TypeLoc::getSourceRange() will include the identifier for things
+  // like function pointers. Not a concern since this action only works with
+  // iterators but something to keep in mind in the future.
+
+  SourceRange Range(V->getTypeSourceInfo()->getTypeLoc().getSourceRange());
+  diag(Range.getBegin(), "use auto when declaring iterators")
+      << FixItHint::CreateReplacement(Range, "auto");
+}
+
+void UseAutoCheck::replaceExpr(
+    const DeclStmt *D, ASTContext *Context,
+    llvm::function_ref<QualType(const Expr *)> GetType, StringRef Message) {
+  const auto *FirstDecl = dyn_cast<VarDecl>(*D->decl_begin());
+  // Ensure that there is at least one VarDecl within the DeclStmt.
+  if (!FirstDecl)
+    return;
+
+  const QualType FirstDeclType = FirstDecl->getType().getCanonicalType();
+
+  std::vector<FixItHint> StarRemovals;
+  for (const auto *Dec : D->decls()) {
+    const auto *V = cast<VarDecl>(Dec);
+    // Ensure that every DeclStmt child is a VarDecl.
+    if (!V)
+      return;
+
+    const auto *Expr = V->getInit()->IgnoreParenImpCasts();
+    // Ensure that every VarDecl has an initializer.
+    if (!Expr)
+      return;
+
+    // If VarDecl and Initializer have mismatching unqualified types.
+    if (!Context->hasSameUnqualifiedType(V->getType(), GetType(Expr)))
+      return;
+
+    // All subsequent variables in this declaration should have the same
+    // canonical type.  For example, we don't want to use `auto` in
+    // `T *p = new T, **pp = new T*;`.
+    if (FirstDeclType != V->getType().getCanonicalType())
+      return;
+
+    if (RemoveStars) {
+      // Remove explicitly written '*' from declarations where there's more than
+      // one declaration in the declaration list.
+      if (Dec == *D->decl_begin())
+        continue;
+
+      auto Q = V->getTypeSourceInfo()->getTypeLoc().getAs<PointerTypeLoc>();
+      while (!Q.isNull()) {
+        StarRemovals.push_back(FixItHint::CreateRemoval(Q.getStarLoc()));
+        Q = Q.getNextTypeLoc().getAs<PointerTypeLoc>();
+      }
+    }
+  }
+
+  // FIXME: There is, however, one case we can address: when the VarDecl pointee
+  // is the same as the initializer, just more CV-qualified. However, TypeLoc
+  // information is not reliable where CV qualifiers are concerned so we can't
+  // do anything about this case for now.
+  TypeLoc Loc = FirstDecl->getTypeSourceInfo()->getTypeLoc();
+  if (!RemoveStars) {
+    while (Loc.getTypeLocClass() == TypeLoc::Pointer ||
+           Loc.getTypeLocClass() == TypeLoc::Qualified)
+      Loc = Loc.getNextTypeLoc();
+  }
+  while (Loc.getTypeLocClass() == TypeLoc::LValueReference ||
+         Loc.getTypeLocClass() == TypeLoc::RValueReference ||
+         Loc.getTypeLocClass() == TypeLoc::Qualified) {
+    Loc = Loc.getNextTypeLoc();
+  }
+  SourceRange Range(Loc.getSourceRange());
+  auto Diag = diag(Range.getBegin(), Message);
+
+  // Space after 'auto' to handle cases where the '*' in the pointer type is
+  // next to the identifier. This avoids changing 'int *p' into 'autop'.
+  // FIXME: This doesn't work for function pointers because the variable name
+  // is inside the type.
+  Diag << FixItHint::CreateReplacement(Range, RemoveStars ? "auto " : "auto")
+       << StarRemovals;
+}
+
+void UseAutoCheck::check(const MatchFinder::MatchResult &Result) {
+  if (const auto *Decl = Result.Nodes.getNodeAs<DeclStmt>(IteratorDeclStmtId)) {
+    replaceIterators(Decl, Result.Context);
+  } else if (const auto *Decl =
+                 Result.Nodes.getNodeAs<DeclStmt>(DeclWithNewId)) {
+    replaceExpr(Decl, Result.Context,
+                [](const Expr *Expr) { return Expr->getType(); },
+                "use auto when initializing with new to avoid "
+                "duplicating the type name");
+  } else if (const auto *Decl =
+                 Result.Nodes.getNodeAs<DeclStmt>(DeclWithCastId)) {
+    replaceExpr(
+        Decl, Result.Context,
+        [](const Expr *Expr) {
+          return cast<ExplicitCastExpr>(Expr)->getTypeAsWritten();
+        },
+        "use auto when initializing with a cast to avoid duplicating the type "
+        "name");
+  } else if (const auto *Decl =
+                 Result.Nodes.getNodeAs<DeclStmt>(DeclWithTemplateCastId)) {
+    replaceExpr(
+        Decl, Result.Context,
+        [](const Expr *Expr) {
+          return cast<CallExpr>(Expr->IgnoreImplicit())
+              ->getDirectCallee()
+              ->getReturnType();
+        },
+        "use auto when initializing with a template cast to avoid duplicating "
+        "the type name");
+  } else {
+    llvm_unreachable("Bad Callback. No node provided.");
+  }
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/modernize/UseAutoCheck.h b/clang-tidy/modernize/UseAutoCheck.h
new file mode 100644 (file)
index 0000000..7bf3292
--- /dev/null
@@ -0,0 +1,39 @@
+//===--- UseAutoCheck.h - clang-tidy-----------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_AUTO_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_AUTO_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+class UseAutoCheck : public ClangTidyCheck {
+public:
+  UseAutoCheck(StringRef Name, ClangTidyContext *Context);
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+  void replaceIterators(const DeclStmt *D, ASTContext *Context);
+  void replaceExpr(const DeclStmt *D, ASTContext *Context,
+                   llvm::function_ref<QualType(const Expr *)> GetType,
+                   StringRef Message);
+
+  const bool RemoveStars;
+};
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_AUTO_H
diff --git a/clang-tidy/modernize/UseBoolLiteralsCheck.cpp b/clang-tidy/modernize/UseBoolLiteralsCheck.cpp
new file mode 100644 (file)
index 0000000..ece8cd5
--- /dev/null
@@ -0,0 +1,76 @@
+//===--- UseBoolLiteralsCheck.cpp - clang-tidy-----------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "UseBoolLiteralsCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+UseBoolLiteralsCheck::UseBoolLiteralsCheck(StringRef Name,
+                                           ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      IgnoreMacros(Options.getLocalOrGlobal("IgnoreMacros", true)) {}
+
+void UseBoolLiteralsCheck::registerMatchers(MatchFinder *Finder) {
+  if (!getLangOpts().CPlusPlus)
+    return;
+
+  Finder->addMatcher(
+      implicitCastExpr(
+          has(ignoringParenImpCasts(integerLiteral().bind("literal"))),
+          hasImplicitDestinationType(qualType(booleanType())),
+          unless(isInTemplateInstantiation()),
+          anyOf(hasParent(explicitCastExpr().bind("cast")), anything())),
+      this);
+
+  Finder->addMatcher(
+      conditionalOperator(
+          hasParent(implicitCastExpr(
+              hasImplicitDestinationType(qualType(booleanType())),
+              unless(isInTemplateInstantiation()))),
+          eachOf(hasTrueExpression(
+                     ignoringParenImpCasts(integerLiteral().bind("literal"))),
+                 hasFalseExpression(
+                     ignoringParenImpCasts(integerLiteral().bind("literal"))))),
+      this);
+}
+
+void UseBoolLiteralsCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *Literal = Result.Nodes.getNodeAs<IntegerLiteral>("literal");
+  const auto *Cast = Result.Nodes.getNodeAs<Expr>("cast");
+  bool LiteralBooleanValue = Literal->getValue().getBoolValue();
+
+  if (Literal->isInstantiationDependent())
+    return;
+
+  const Expr *Expression = Cast ? Cast : Literal;
+
+  bool InMacro = Expression->getLocStart().isMacroID();
+
+  if (InMacro && IgnoreMacros)
+    return;
+
+  auto Diag =
+      diag(Expression->getExprLoc(),
+           "converting integer literal to bool, use bool literal instead");
+
+  if (!InMacro)
+    Diag << FixItHint::CreateReplacement(
+        Expression->getSourceRange(), LiteralBooleanValue ? "true" : "false");
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/modernize/UseBoolLiteralsCheck.h b/clang-tidy/modernize/UseBoolLiteralsCheck.h
new file mode 100644 (file)
index 0000000..c9c7363
--- /dev/null
@@ -0,0 +1,37 @@
+//===--- UseBoolLiteralsCheck.h - clang-tidy---------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_BOOL_LITERALS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_BOOL_LITERALS_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+/// Finds integer literals which are cast to bool.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/modernize-use-bool-literals.html
+class UseBoolLiteralsCheck : public ClangTidyCheck {
+public:
+  UseBoolLiteralsCheck(StringRef Name, ClangTidyContext *Context);
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+  const bool IgnoreMacros;
+};
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_BOOL_LITERALS_H
diff --git a/clang-tidy/modernize/UseDefaultMemberInitCheck.cpp b/clang-tidy/modernize/UseDefaultMemberInitCheck.cpp
new file mode 100644 (file)
index 0000000..7fa773c
--- /dev/null
@@ -0,0 +1,244 @@
+//===--- UseDefaultMemberInitCheck.cpp - clang-tidy------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "UseDefaultMemberInitCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+static StringRef getValueOfValueInit(const QualType InitType) {
+  switch (InitType->getScalarTypeKind()) {
+  case Type::STK_CPointer:
+  case Type::STK_BlockPointer:
+  case Type::STK_ObjCObjectPointer:
+  case Type::STK_MemberPointer:
+    return "nullptr";
+
+  case Type::STK_Bool:
+    return "false";
+
+  case Type::STK_Integral:
+    switch (InitType->getAs<BuiltinType>()->getKind()) {
+    case BuiltinType::Char_U:
+    case BuiltinType::UChar:
+    case BuiltinType::Char_S:
+    case BuiltinType::SChar:
+      return "'\\0'";
+    case BuiltinType::WChar_U:
+    case BuiltinType::WChar_S:
+      return "L'\\0'";
+    case BuiltinType::Char16:
+      return "u'\\0'";
+    case BuiltinType::Char32:
+      return "U'\\0'";
+    default:
+      return "0";
+    }
+
+  case Type::STK_Floating:
+    switch (InitType->getAs<BuiltinType>()->getKind()) {
+    case BuiltinType::Half:
+    case BuiltinType::Float:
+      return "0.0f";
+    default:
+      return "0.0";
+    }
+
+  case Type::STK_FloatingComplex:
+  case Type::STK_IntegralComplex:
+    return getValueOfValueInit(
+        InitType->getAs<ComplexType>()->getElementType());
+  }
+  llvm_unreachable("Invalid scalar type kind");
+}
+
+static bool isZero(const Expr *E) {
+  switch (E->getStmtClass()) {
+  case Stmt::CXXNullPtrLiteralExprClass:
+  case Stmt::ImplicitValueInitExprClass:
+    return true;
+  case Stmt::InitListExprClass:
+    return cast<InitListExpr>(E)->getNumInits() == 0;
+  case Stmt::CharacterLiteralClass:
+    return !cast<CharacterLiteral>(E)->getValue();
+  case Stmt::CXXBoolLiteralExprClass:
+    return !cast<CXXBoolLiteralExpr>(E)->getValue();
+  case Stmt::IntegerLiteralClass:
+    return !cast<IntegerLiteral>(E)->getValue();
+  case Stmt::FloatingLiteralClass: {
+    llvm::APFloat Value = cast<FloatingLiteral>(E)->getValue();
+    return Value.isZero() && !Value.isNegative();
+  }
+  default:
+    return false;
+  }
+}
+
+static const Expr *ignoreUnaryPlus(const Expr *E) {
+  auto *UnaryOp = dyn_cast<UnaryOperator>(E);
+  if (UnaryOp && UnaryOp->getOpcode() == UO_Plus)
+    return UnaryOp->getSubExpr();
+  return E;
+}
+
+static const Expr *getInitializer(const Expr *E) {
+  auto *InitList = dyn_cast<InitListExpr>(E);
+  if (InitList && InitList->getNumInits() == 1)
+    return InitList->getInit(0);
+  return E;
+}
+
+static bool sameValue(const Expr *E1, const Expr *E2) {
+  E1 = ignoreUnaryPlus(getInitializer(E1->IgnoreParenImpCasts()));
+  E2 = ignoreUnaryPlus(getInitializer(E2->IgnoreParenImpCasts()));
+
+  if (isZero(E1) && isZero(E2))
+    return true;
+
+  if (E1->getStmtClass() != E2->getStmtClass())
+    return false;
+
+  switch (E1->getStmtClass()) {
+  case Stmt::UnaryOperatorClass:
+    return sameValue(cast<UnaryOperator>(E1)->getSubExpr(),
+                     cast<UnaryOperator>(E2)->getSubExpr());
+  case Stmt::CharacterLiteralClass:
+    return cast<CharacterLiteral>(E1)->getValue() ==
+           cast<CharacterLiteral>(E2)->getValue();
+  case Stmt::CXXBoolLiteralExprClass:
+    return cast<CXXBoolLiteralExpr>(E1)->getValue() ==
+           cast<CXXBoolLiteralExpr>(E2)->getValue();
+  case Stmt::IntegerLiteralClass:
+    return cast<IntegerLiteral>(E1)->getValue() ==
+           cast<IntegerLiteral>(E2)->getValue();
+  case Stmt::FloatingLiteralClass:
+    return cast<FloatingLiteral>(E1)->getValue().bitwiseIsEqual(
+        cast<FloatingLiteral>(E2)->getValue());
+  case Stmt::StringLiteralClass:
+    return cast<StringLiteral>(E1)->getString() ==
+           cast<StringLiteral>(E2)->getString();
+  case Stmt::DeclRefExprClass:
+    return cast<DeclRefExpr>(E1)->getDecl() == cast<DeclRefExpr>(E2)->getDecl();
+  default:
+    return false;
+  }
+}
+
+UseDefaultMemberInitCheck::UseDefaultMemberInitCheck(StringRef Name,
+                                                     ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      UseAssignment(Options.get("UseAssignment", 0) != 0),
+      IgnoreMacros(Options.getLocalOrGlobal("IgnoreMacros", 1) != 0) {}
+
+void UseDefaultMemberInitCheck::storeOptions(
+    ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "UseAssignment", UseAssignment);
+  Options.store(Opts, "IgnoreMacros", IgnoreMacros);
+}
+
+void UseDefaultMemberInitCheck::registerMatchers(MatchFinder *Finder) {
+  if (!getLangOpts().CPlusPlus11)
+    return;
+
+  auto Init =
+      anyOf(stringLiteral(), characterLiteral(), integerLiteral(),
+            unaryOperator(anyOf(hasOperatorName("+"), hasOperatorName("-")),
+                          hasUnaryOperand(integerLiteral())),
+            floatLiteral(),
+            unaryOperator(anyOf(hasOperatorName("+"), hasOperatorName("-")),
+                          hasUnaryOperand(floatLiteral())),
+            cxxBoolLiteral(), cxxNullPtrLiteralExpr(), implicitValueInitExpr(),
+            declRefExpr(to(enumConstantDecl())));
+
+  Finder->addMatcher(
+      cxxConstructorDecl(
+          isDefaultConstructor(), unless(isInstantiated()),
+          forEachConstructorInitializer(
+              allOf(forField(unless(anyOf(isBitField(),
+                                          hasInClassInitializer(anything())))),
+                    cxxCtorInitializer(isWritten(),
+                                       withInitializer(ignoringImplicit(Init)))
+                        .bind("default")))),
+      this);
+
+  Finder->addMatcher(
+      cxxConstructorDecl(
+          unless(ast_matchers::isTemplateInstantiation()),
+          forEachConstructorInitializer(
+              allOf(forField(hasInClassInitializer(anything())),
+                    cxxCtorInitializer(isWritten(),
+                                       withInitializer(ignoringImplicit(Init)))
+                        .bind("existing")))),
+      this);
+}
+
+void UseDefaultMemberInitCheck::check(const MatchFinder::MatchResult &Result) {
+  if (const auto *Default =
+          Result.Nodes.getNodeAs<CXXCtorInitializer>("default"))
+    checkDefaultInit(Result, Default);
+  else if (const auto *Existing =
+               Result.Nodes.getNodeAs<CXXCtorInitializer>("existing"))
+    checkExistingInit(Result, Existing);
+  else
+    llvm_unreachable("Bad Callback. No node provided.");
+}
+
+void UseDefaultMemberInitCheck::checkDefaultInit(
+    const MatchFinder::MatchResult &Result, const CXXCtorInitializer *Init) {
+  const FieldDecl *Field = Init->getMember();
+
+  SourceLocation StartLoc = Field->getLocStart();
+  if (StartLoc.isMacroID() && IgnoreMacros)
+    return;
+
+  SourceLocation FieldEnd =
+      Lexer::getLocForEndOfToken(Field->getSourceRange().getEnd(), 0,
+                                 *Result.SourceManager, getLangOpts());
+  SourceLocation LParenEnd = Lexer::getLocForEndOfToken(
+      Init->getLParenLoc(), 0, *Result.SourceManager, getLangOpts());
+  CharSourceRange InitRange =
+      CharSourceRange::getCharRange(LParenEnd, Init->getRParenLoc());
+
+  auto Diag =
+      diag(Field->getLocation(), "use default member initializer for %0")
+      << Field
+      << FixItHint::CreateInsertion(FieldEnd, UseAssignment ? " = " : "{")
+      << FixItHint::CreateInsertionFromRange(FieldEnd, InitRange);
+
+  if (UseAssignment && isa<ImplicitValueInitExpr>(Init->getInit()))
+    Diag << FixItHint::CreateInsertion(
+        FieldEnd, getValueOfValueInit(Init->getInit()->getType()));
+
+  if (!UseAssignment)
+    Diag << FixItHint::CreateInsertion(FieldEnd, "}");
+
+  Diag << FixItHint::CreateRemoval(Init->getSourceRange());
+}
+
+void UseDefaultMemberInitCheck::checkExistingInit(
+    const MatchFinder::MatchResult &Result, const CXXCtorInitializer *Init) {
+  const FieldDecl *Field = Init->getMember();
+
+  if (!sameValue(Field->getInClassInitializer(), Init->getInit()))
+    return;
+
+  diag(Init->getSourceLocation(), "member initializer for %0 is redundant")
+      << Field
+      << FixItHint::CreateRemoval(Init->getSourceRange());
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/modernize/UseDefaultMemberInitCheck.h b/clang-tidy/modernize/UseDefaultMemberInitCheck.h
new file mode 100644 (file)
index 0000000..d8887a0
--- /dev/null
@@ -0,0 +1,46 @@
+//===--- UseDefaultMemberInitCheck.h - clang-tidy----------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_DEFAULT_MEMBER_INIT_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_DEFAULT_MEMBER_INIT_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+/// Convert a default constructor's member initializers into default member
+/// initializers.  Remove member initializers that are the same as a default
+/// member initializer.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/modernize-use-default-member-init.html
+class UseDefaultMemberInitCheck : public ClangTidyCheck {
+public:
+  UseDefaultMemberInitCheck(StringRef Name, ClangTidyContext *Context);
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+  void checkDefaultInit(const ast_matchers::MatchFinder::MatchResult &Result,
+                        const CXXCtorInitializer *Init);
+  void checkExistingInit(const ast_matchers::MatchFinder::MatchResult &Result,
+                         const CXXCtorInitializer *Init);
+
+  const bool UseAssignment;
+  const bool IgnoreMacros;
+};
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_DEFAULT_MEMBER_INIT_H
diff --git a/clang-tidy/modernize/UseEmplaceCheck.cpp b/clang-tidy/modernize/UseEmplaceCheck.cpp
new file mode 100644 (file)
index 0000000..48fbbfc
--- /dev/null
@@ -0,0 +1,172 @@
+//===--- UseEmplaceCheck.cpp - clang-tidy----------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "UseEmplaceCheck.h"
+#include "../utils/OptionsUtils.h"
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+namespace {
+AST_MATCHER(DeclRefExpr, hasExplicitTemplateArgs) {
+  return Node.hasExplicitTemplateArgs();
+}
+
+const auto DefaultContainersWithPushBack =
+    "::std::vector; ::std::list; ::std::deque";
+const auto DefaultSmartPointers =
+    "::std::shared_ptr; ::std::unique_ptr; ::std::auto_ptr; ::std::weak_ptr";
+const auto DefaultTupleTypes = "::std::pair; ::std::tuple";
+const auto DefaultTupleMakeFunctions = "::std::make_pair; ::std::make_tuple";
+} // namespace
+
+UseEmplaceCheck::UseEmplaceCheck(StringRef Name, ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      ContainersWithPushBack(utils::options::parseStringList(Options.get(
+          "ContainersWithPushBack", DefaultContainersWithPushBack))),
+      SmartPointers(utils::options::parseStringList(
+          Options.get("SmartPointers", DefaultSmartPointers))),
+      TupleTypes(utils::options::parseStringList(
+          Options.get("TupleTypes", DefaultTupleTypes))),
+      TupleMakeFunctions(utils::options::parseStringList(
+          Options.get("TupleMakeFunctions", DefaultTupleMakeFunctions))) {}
+
+void UseEmplaceCheck::registerMatchers(MatchFinder *Finder) {
+  if (!getLangOpts().CPlusPlus11)
+    return;
+
+  // FIXME: Bunch of functionality that could be easily added:
+  // + add handling of `push_front` for std::forward_list, std::list
+  // and std::deque.
+  // + add handling of `push` for std::stack, std::queue, std::priority_queue
+  // + add handling of `insert` for stl associative container, but be careful
+  // because this requires special treatment (it could cause performance
+  // regression)
+  // + match for emplace calls that should be replaced with insertion
+  auto CallPushBack = cxxMemberCallExpr(
+      hasDeclaration(functionDecl(hasName("push_back"))),
+      on(hasType(cxxRecordDecl(hasAnyName(SmallVector<StringRef, 5>(
+          ContainersWithPushBack.begin(), ContainersWithPushBack.end()))))));
+
+  // We can't replace push_backs of smart pointer because
+  // if emplacement fails (f.e. bad_alloc in vector) we will have leak of
+  // passed pointer because smart pointer won't be constructed
+  // (and destructed) as in push_back case.
+  auto IsCtorOfSmartPtr = hasDeclaration(cxxConstructorDecl(ofClass(hasAnyName(
+      SmallVector<StringRef, 5>(SmartPointers.begin(), SmartPointers.end())))));
+
+  // Bitfields binds only to consts and emplace_back take it by universal ref.
+  auto BitFieldAsArgument = hasAnyArgument(
+      ignoringImplicit(memberExpr(hasDeclaration(fieldDecl(isBitField())))));
+
+  // Initializer list can't be passed to universal reference.
+  auto InitializerListAsArgument = hasAnyArgument(
+      ignoringImplicit(cxxConstructExpr(isListInitialization())));
+
+  // We could have leak of resource.
+  auto NewExprAsArgument = hasAnyArgument(ignoringImplicit(cxxNewExpr()));
+  // We would call another constructor.
+  auto ConstructingDerived =
+      hasParent(implicitCastExpr(hasCastKind(CastKind::CK_DerivedToBase)));
+
+  // emplace_back can't access private constructor.
+  auto IsPrivateCtor = hasDeclaration(cxxConstructorDecl(isPrivate()));
+
+  auto HasInitList = anyOf(has(ignoringImplicit(initListExpr())),
+                           has(cxxStdInitializerListExpr()));
+
+  // FIXME: Discard 0/NULL (as nullptr), static inline const data members,
+  // overloaded functions and template names.
+  auto SoughtConstructExpr =
+      cxxConstructExpr(
+          unless(anyOf(IsCtorOfSmartPtr, HasInitList, BitFieldAsArgument,
+                       InitializerListAsArgument, NewExprAsArgument,
+                       ConstructingDerived, IsPrivateCtor)))
+          .bind("ctor");
+  auto HasConstructExpr = has(ignoringImplicit(SoughtConstructExpr));
+
+  auto MakeTuple = ignoringImplicit(
+      callExpr(
+          callee(expr(ignoringImplicit(declRefExpr(
+              unless(hasExplicitTemplateArgs()),
+              to(functionDecl(hasAnyName(SmallVector<StringRef, 2>(
+                  TupleMakeFunctions.begin(), TupleMakeFunctions.end())))))))))
+          .bind("make"));
+
+  // make_something can return type convertible to container's element type.
+  // Allow the conversion only on containers of pairs.
+  auto MakeTupleCtor = ignoringImplicit(cxxConstructExpr(
+      has(materializeTemporaryExpr(MakeTuple)),
+      hasDeclaration(cxxConstructorDecl(ofClass(hasAnyName(
+          SmallVector<StringRef, 2>(TupleTypes.begin(), TupleTypes.end())))))));
+
+  auto SoughtParam = materializeTemporaryExpr(
+      anyOf(has(MakeTuple), has(MakeTupleCtor),
+            HasConstructExpr, has(cxxFunctionalCastExpr(HasConstructExpr))));
+
+  Finder->addMatcher(cxxMemberCallExpr(CallPushBack, has(SoughtParam),
+                                       unless(isInTemplateInstantiation()))
+                         .bind("call"),
+                     this);
+}
+
+void UseEmplaceCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *Call = Result.Nodes.getNodeAs<CXXMemberCallExpr>("call");
+  const auto *InnerCtorCall = Result.Nodes.getNodeAs<CXXConstructExpr>("ctor");
+  const auto *MakeCall = Result.Nodes.getNodeAs<CallExpr>("make");
+  assert((InnerCtorCall || MakeCall) && "No push_back parameter matched");
+
+  const auto FunctionNameSourceRange = CharSourceRange::getCharRange(
+      Call->getExprLoc(), Call->getArg(0)->getExprLoc());
+
+  auto Diag = diag(Call->getExprLoc(), "use emplace_back instead of push_back");
+
+  if (FunctionNameSourceRange.getBegin().isMacroID())
+    return;
+
+  const auto *EmplacePrefix = MakeCall ? "emplace_back" : "emplace_back(";
+  Diag << FixItHint::CreateReplacement(FunctionNameSourceRange, EmplacePrefix);
+
+  const SourceRange CallParensRange =
+      MakeCall ? SourceRange(MakeCall->getCallee()->getLocEnd(),
+                             MakeCall->getRParenLoc())
+               : InnerCtorCall->getParenOrBraceRange();
+
+  // Finish if there is no explicit constructor call.
+  if (CallParensRange.getBegin().isInvalid())
+    return;
+
+  const SourceLocation ExprBegin =
+      MakeCall ? MakeCall->getExprLoc() : InnerCtorCall->getExprLoc();
+
+  // Range for constructor name and opening brace.
+  const auto ParamCallSourceRange =
+      CharSourceRange::getTokenRange(ExprBegin, CallParensRange.getBegin());
+
+  Diag << FixItHint::CreateRemoval(ParamCallSourceRange)
+       << FixItHint::CreateRemoval(CharSourceRange::getTokenRange(
+           CallParensRange.getEnd(), CallParensRange.getEnd()));
+}
+
+void UseEmplaceCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "ContainersWithPushBack",
+                utils::options::serializeStringList(ContainersWithPushBack));
+  Options.store(Opts, "SmartPointers",
+                utils::options::serializeStringList(SmartPointers));
+  Options.store(Opts, "TupleTypes",
+                utils::options::serializeStringList(TupleTypes));
+  Options.store(Opts, "TupleMakeFunctions",
+                utils::options::serializeStringList(TupleMakeFunctions));
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/modernize/UseEmplaceCheck.h b/clang-tidy/modernize/UseEmplaceCheck.h
new file mode 100644 (file)
index 0000000..5bd0d8e
--- /dev/null
@@ -0,0 +1,46 @@
+//===--- UseEmplaceCheck.h - clang-tidy--------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_EMPLACE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_EMPLACE_H
+
+#include "../ClangTidy.h"
+#include <string>
+#include <vector>
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+/// This check looks for cases when inserting new element into std::vector but
+/// the element is constructed temporarily.
+/// It replaces those calls for emplace_back of arguments passed to
+/// constructor of temporary object.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/modernize-use-emplace.html
+class UseEmplaceCheck : public ClangTidyCheck {
+public:
+  UseEmplaceCheck(StringRef Name, ClangTidyContext *Context);
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+
+private:
+  std::vector<std::string> ContainersWithPushBack;
+  std::vector<std::string> SmartPointers;
+  std::vector<std::string> TupleTypes;
+  std::vector<std::string> TupleMakeFunctions;
+};
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_EMPLACE_H
diff --git a/clang-tidy/modernize/UseEqualsDefaultCheck.cpp b/clang-tidy/modernize/UseEqualsDefaultCheck.cpp
new file mode 100644 (file)
index 0000000..1b67714
--- /dev/null
@@ -0,0 +1,299 @@
+//===--- UseEqualsDefaultCheck.cpp - clang-tidy----------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "UseEqualsDefaultCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+static const char SpecialFunction[] = "SpecialFunction";
+
+/// \brief Finds all the named non-static fields of \p Record.
+static std::set<const FieldDecl *>
+getAllNamedFields(const CXXRecordDecl *Record) {
+  std::set<const FieldDecl *> Result;
+  for (const auto *Field : Record->fields()) {
+    // Static data members are not in this range.
+    if (Field->isUnnamedBitfield())
+      continue;
+    Result.insert(Field);
+  }
+  return Result;
+}
+
+/// \brief Returns the names of the direct bases of \p Record, both virtual and
+/// non-virtual.
+static std::set<const Type *> getAllDirectBases(const CXXRecordDecl *Record) {
+  std::set<const Type *> Result;
+  for (auto Base : Record->bases()) {
+    // CXXBaseSpecifier.
+    const auto *BaseType = Base.getTypeSourceInfo()->getType().getTypePtr();
+    Result.insert(BaseType);
+  }
+  return Result;
+}
+
+/// \brief Returns a matcher that matches member expressions where the base is
+/// the variable declared as \p Var and the accessed member is the one declared
+/// as \p Field.
+internal::Matcher<Expr> accessToFieldInVar(const FieldDecl *Field,
+                                           const ValueDecl *Var) {
+  return ignoringImpCasts(
+      memberExpr(hasObjectExpression(declRefExpr(to(varDecl(equalsNode(Var))))),
+                 member(fieldDecl(equalsNode(Field)))));
+}
+
+/// \brief Check that the given constructor has copy signature and that it
+/// copy-initializes all its bases and members.
+static bool isCopyConstructorAndCanBeDefaulted(ASTContext *Context,
+                                               const CXXConstructorDecl *Ctor) {
+  // An explicitly-defaulted constructor cannot have default arguments.
+  if (Ctor->getMinRequiredArguments() != 1)
+    return false;
+
+  const auto *Record = Ctor->getParent();
+  const auto *Param = Ctor->getParamDecl(0);
+
+  // Base classes and members that have to be copied.
+  auto BasesToInit = getAllDirectBases(Record);
+  auto FieldsToInit = getAllNamedFields(Record);
+
+  // Ensure that all the bases are copied.
+  for (const auto *Base : BasesToInit) {
+    // The initialization of a base class should be a call to a copy
+    // constructor of the base.
+    if (match(
+            cxxConstructorDecl(forEachConstructorInitializer(cxxCtorInitializer(
+                isBaseInitializer(),
+                withInitializer(cxxConstructExpr(allOf(
+                    hasType(equalsNode(Base)),
+                    hasDeclaration(cxxConstructorDecl(isCopyConstructor())),
+                    argumentCountIs(1),
+                    hasArgument(
+                        0, declRefExpr(to(varDecl(equalsNode(Param))))))))))),
+            *Ctor, *Context)
+            .empty())
+      return false;
+  }
+
+  // Ensure that all the members are copied.
+  for (const auto *Field : FieldsToInit) {
+    auto AccessToFieldInParam = accessToFieldInVar(Field, Param);
+    // The initialization is a CXXConstructExpr for class types.
+    if (match(
+            cxxConstructorDecl(forEachConstructorInitializer(cxxCtorInitializer(
+                isMemberInitializer(), forField(equalsNode(Field)),
+                withInitializer(anyOf(
+                    AccessToFieldInParam,
+                    cxxConstructExpr(allOf(
+                        hasDeclaration(cxxConstructorDecl(isCopyConstructor())),
+                        argumentCountIs(1),
+                        hasArgument(0, AccessToFieldInParam)))))))),
+            *Ctor, *Context)
+            .empty())
+      return false;
+  }
+
+  // Ensure that we don't do anything else, like initializing an indirect base.
+  return Ctor->getNumCtorInitializers() ==
+         BasesToInit.size() + FieldsToInit.size();
+}
+
+/// \brief Checks that the given method is an overloading of the assignment
+/// operator, has copy signature, returns a reference to "*this" and copies
+/// all its members and subobjects.
+static bool isCopyAssignmentAndCanBeDefaulted(ASTContext *Context,
+                                              const CXXMethodDecl *Operator) {
+  const auto *Record = Operator->getParent();
+  const auto *Param = Operator->getParamDecl(0);
+
+  // Base classes and members that have to be copied.
+  auto BasesToInit = getAllDirectBases(Record);
+  auto FieldsToInit = getAllNamedFields(Record);
+
+  const auto *Compound = cast<CompoundStmt>(Operator->getBody());
+
+  // The assignment operator definition has to end with the following return
+  // statement:
+  //   return *this;
+  if (Compound->body_empty() ||
+      match(returnStmt(has(ignoringParenImpCasts(unaryOperator(
+                hasOperatorName("*"), hasUnaryOperand(cxxThisExpr()))))),
+            *Compound->body_back(), *Context)
+          .empty())
+    return false;
+
+  // Ensure that all the bases are copied.
+  for (const auto *Base : BasesToInit) {
+    // Assignment operator of a base class:
+    //   Base::operator=(Other);
+    //
+    // Clang translates this into:
+    //   ((Base*)this)->operator=((Base)Other);
+    //
+    // So we are looking for a member call that fulfills:
+    if (match(compoundStmt(has(ignoringParenImpCasts(cxxMemberCallExpr(allOf(
+                  // - The object is an implicit cast of 'this' to a pointer to
+                  //   a base class.
+                  onImplicitObjectArgument(
+                      implicitCastExpr(hasImplicitDestinationType(
+                                           pointsTo(type(equalsNode(Base)))),
+                                       hasSourceExpression(cxxThisExpr()))),
+                  // - The called method is the operator=.
+                  callee(cxxMethodDecl(isCopyAssignmentOperator())),
+                  // - The argument is (an implicit cast to a Base of) the
+                  // argument taken by "Operator".
+                  argumentCountIs(1),
+                  hasArgument(0,
+                              declRefExpr(to(varDecl(equalsNode(Param)))))))))),
+              *Compound, *Context)
+            .empty())
+      return false;
+  }
+
+  // Ensure that all the members are copied.
+  for (const auto *Field : FieldsToInit) {
+    // The assignment of data members:
+    //   Field = Other.Field;
+    // Is a BinaryOperator in non-class types, and a CXXOperatorCallExpr
+    // otherwise.
+    auto LHS = memberExpr(hasObjectExpression(cxxThisExpr()),
+                          member(fieldDecl(equalsNode(Field))));
+    auto RHS = accessToFieldInVar(Field, Param);
+    if (match(
+            compoundStmt(has(ignoringParenImpCasts(stmt(anyOf(
+                binaryOperator(hasOperatorName("="), hasLHS(LHS), hasRHS(RHS)),
+                cxxOperatorCallExpr(hasOverloadedOperatorName("="),
+                                    argumentCountIs(2), hasArgument(0, LHS),
+                                    hasArgument(1, RHS))))))),
+            *Compound, *Context)
+            .empty())
+      return false;
+  }
+
+  // Ensure that we don't do anything else.
+  return Compound->size() == BasesToInit.size() + FieldsToInit.size() + 1;
+}
+
+/// \brief Returns false if the body has any non-whitespace character.
+static bool bodyEmpty(const ASTContext *Context, const CompoundStmt *Body) {
+  bool Invalid = false;
+  StringRef Text = Lexer::getSourceText(
+      CharSourceRange::getCharRange(Body->getLBracLoc().getLocWithOffset(1),
+                                    Body->getRBracLoc()),
+      Context->getSourceManager(), Context->getLangOpts(), &Invalid);
+  return !Invalid && std::strspn(Text.data(), " \t\r\n") == Text.size();
+}
+
+void UseEqualsDefaultCheck::registerMatchers(MatchFinder *Finder) {
+  if (getLangOpts().CPlusPlus) {
+    // Destructor.
+    Finder->addMatcher(cxxDestructorDecl(isDefinition()).bind(SpecialFunction),
+                       this);
+    Finder->addMatcher(
+        cxxConstructorDecl(
+            isDefinition(),
+            anyOf(
+                // Default constructor.
+                allOf(unless(hasAnyConstructorInitializer(isWritten())),
+                      parameterCountIs(0)),
+                // Copy constructor.
+                allOf(isCopyConstructor(),
+                      // Discard constructors that can be used as a copy
+                      // constructor because all the other arguments have
+                      // default values.
+                      parameterCountIs(1))))
+            .bind(SpecialFunction),
+        this);
+    // Copy-assignment operator.
+    Finder->addMatcher(
+        cxxMethodDecl(isDefinition(), isCopyAssignmentOperator(),
+                      // isCopyAssignmentOperator() allows the parameter to be
+                      // passed by value, and in this case it cannot be
+                      // defaulted.
+                      hasParameter(0, hasType(lValueReferenceType())))
+            .bind(SpecialFunction),
+        this);
+  }
+}
+
+void UseEqualsDefaultCheck::check(const MatchFinder::MatchResult &Result) {
+  std::string SpecialFunctionName;
+
+  // Both CXXConstructorDecl and CXXDestructorDecl inherit from CXXMethodDecl.
+  const auto *SpecialFunctionDecl =
+      Result.Nodes.getNodeAs<CXXMethodDecl>(SpecialFunction);
+
+  // Discard explicitly deleted/defaulted special member functions and those
+  // that are not user-provided (automatically generated).
+  if (SpecialFunctionDecl->isDeleted() ||
+      SpecialFunctionDecl->isExplicitlyDefaulted() ||
+      SpecialFunctionDecl->isLateTemplateParsed() ||
+      SpecialFunctionDecl->isTemplateInstantiation() ||
+      !SpecialFunctionDecl->isUserProvided() || !SpecialFunctionDecl->hasBody())
+    return;
+
+  const auto *Body = dyn_cast<CompoundStmt>(SpecialFunctionDecl->getBody());
+  if (!Body)
+    return;
+
+  // If there is code inside the body, don't warn.
+  if (!SpecialFunctionDecl->isCopyAssignmentOperator() && !Body->body_empty())
+    return;
+
+  // If there are comments inside the body, don't do the change.
+  bool ApplyFix = SpecialFunctionDecl->isCopyAssignmentOperator() ||
+                  bodyEmpty(Result.Context, Body);
+
+  std::vector<FixItHint> RemoveInitializers;
+
+  if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(SpecialFunctionDecl)) {
+    if (Ctor->getNumParams() == 0) {
+      SpecialFunctionName = "default constructor";
+    } else {
+      if (!isCopyConstructorAndCanBeDefaulted(Result.Context, Ctor))
+        return;
+      SpecialFunctionName = "copy constructor";
+      // If there are constructor initializers, they must be removed.
+      for (const auto *Init : Ctor->inits()) {
+        RemoveInitializers.emplace_back(
+            FixItHint::CreateRemoval(Init->getSourceRange()));
+      }
+    }
+  } else if (isa<CXXDestructorDecl>(SpecialFunctionDecl)) {
+    SpecialFunctionName = "destructor";
+  } else {
+    if (!isCopyAssignmentAndCanBeDefaulted(Result.Context, SpecialFunctionDecl))
+      return;
+    SpecialFunctionName = "copy-assignment operator";
+  }
+
+  // The location of the body is more useful inside a macro as spelling and
+  // expansion locations are reported.
+  SourceLocation Location = SpecialFunctionDecl->getLocation();
+  if (Location.isMacroID())
+    Location = Body->getLocStart();
+
+  auto Diag = diag(Location, "use '= default' to define a trivial " +
+                                 SpecialFunctionName);
+
+  if (ApplyFix)
+    Diag << FixItHint::CreateReplacement(Body->getSourceRange(), "= default;")
+         << RemoveInitializers;
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/modernize/UseEqualsDefaultCheck.h b/clang-tidy/modernize/UseEqualsDefaultCheck.h
new file mode 100644 (file)
index 0000000..dfefed6
--- /dev/null
@@ -0,0 +1,50 @@
+//===--- UseEqualsDefaultCheck.h - clang-tidy--------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_EQUALS_DEFAULT_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_EQUALS_DEFAULT_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+/// \brief Replace default bodies of special member functions with '= default;'.
+/// \code
+///   struct A {
+///     A() {}
+///     ~A();
+///   };
+///   A::~A() {}
+/// \endcode
+/// Is converted to:
+/// \code
+///   struct A {
+///     A() = default;
+///     ~A();
+///   };
+///   A::~A() = default;
+/// \endcode
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/modernize-use-equals-default.html
+class UseEqualsDefaultCheck : public ClangTidyCheck {
+public:
+  UseEqualsDefaultCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_EQUALS_DEFAULT_H
diff --git a/clang-tidy/modernize/UseEqualsDeleteCheck.cpp b/clang-tidy/modernize/UseEqualsDeleteCheck.cpp
new file mode 100644 (file)
index 0000000..18190b1
--- /dev/null
@@ -0,0 +1,78 @@
+//===--- UseEqualsDeleteCheck.cpp - clang-tidy-----------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "UseEqualsDeleteCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+static const char SpecialFunction[] = "SpecialFunction";
+static const char DeletedNotPublic[] = "DeletedNotPublic";
+
+void UseEqualsDeleteCheck::registerMatchers(MatchFinder *Finder) {
+  if (!getLangOpts().CPlusPlus)
+    return;
+
+  auto PrivateSpecialFn = cxxMethodDecl(
+      isPrivate(),
+      anyOf(cxxConstructorDecl(anyOf(isDefaultConstructor(),
+                                     isCopyConstructor(), isMoveConstructor())),
+            cxxMethodDecl(
+                anyOf(isCopyAssignmentOperator(), isMoveAssignmentOperator())),
+            cxxDestructorDecl()));
+
+  Finder->addMatcher(
+      cxxMethodDecl(
+          PrivateSpecialFn,
+          unless(anyOf(hasBody(stmt()), isDefaulted(), isDeleted(),
+                       ast_matchers::isTemplateInstantiation(),
+                       // Ensure that all methods except private special member
+                       // functions are defined.
+                       hasParent(cxxRecordDecl(hasMethod(unless(
+                           anyOf(PrivateSpecialFn, hasBody(stmt()), isPure(),
+                                 isDefaulted(), isDeleted()))))))))
+          .bind(SpecialFunction),
+      this);
+
+  Finder->addMatcher(
+      cxxMethodDecl(isDeleted(), unless(isPublic())).bind(DeletedNotPublic),
+      this);
+}
+
+void UseEqualsDeleteCheck::check(const MatchFinder::MatchResult &Result) {
+  if (const auto *Func =
+          Result.Nodes.getNodeAs<CXXMethodDecl>(SpecialFunction)) {
+    SourceLocation EndLoc = Lexer::getLocForEndOfToken(
+        Func->getLocEnd(), 0, *Result.SourceManager, getLangOpts());
+
+    // FIXME: Improve FixItHint to make the method public.
+    diag(Func->getLocation(),
+         "use '= delete' to prohibit calling of a special member function")
+        << FixItHint::CreateInsertion(EndLoc, " = delete");
+  } else if (const auto *Func =
+                 Result.Nodes.getNodeAs<CXXMethodDecl>(DeletedNotPublic)) {
+    // Ignore this warning in macros, since it's extremely noisy in code using
+    // DISALLOW_COPY_AND_ASSIGN-style macros and there's no easy way to
+    // automatically fix the warning when macros are in play.
+    if (Func->getLocation().isMacroID())
+      return;
+    // FIXME: Add FixItHint to make the method public.
+    diag(Func->getLocation(), "deleted member function should be public");
+  }
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/modernize/UseEqualsDeleteCheck.h b/clang-tidy/modernize/UseEqualsDeleteCheck.h
new file mode 100644 (file)
index 0000000..1daa1a8
--- /dev/null
@@ -0,0 +1,50 @@
+//===--- UseEqualsDeleteCheck.h - clang-tidy---------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_EQUALS_DELETE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_EQUALS_DELETE_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+/// \brief Mark unimplemented private special member functions with '= delete'.
+/// \code
+///   struct A {
+///   private:
+///     A(const A&);
+///     A& operator=(const A&);
+///   };
+/// \endcode
+/// Is converted to:
+/// \code
+///   struct A {
+///   private:
+///     A(const A&) = delete;
+///     A& operator=(const A&) = delete;
+///   };
+/// \endcode
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/modernize-use-equals-delete.html
+class UseEqualsDeleteCheck : public ClangTidyCheck {
+public:
+  UseEqualsDeleteCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_EQUALS_DELETE_H
diff --git a/clang-tidy/modernize/UseNoexceptCheck.cpp b/clang-tidy/modernize/UseNoexceptCheck.cpp
new file mode 100644 (file)
index 0000000..e32aef9
--- /dev/null
@@ -0,0 +1,114 @@
+//===--- UseNoexceptCheck.cpp - clang-tidy---------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "UseNoexceptCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+UseNoexceptCheck::UseNoexceptCheck(StringRef Name, ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      NoexceptMacro(Options.get("ReplacementString", "")),
+      UseNoexceptFalse(Options.get("UseNoexceptFalse", true)) {}
+
+void UseNoexceptCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "ReplacementString", NoexceptMacro);
+  Options.store(Opts, "UseNoexceptFalse", UseNoexceptFalse);
+}
+
+void UseNoexceptCheck::registerMatchers(MatchFinder *Finder) {
+  if (!getLangOpts().CPlusPlus11)
+    return;
+
+  Finder->addMatcher(
+      functionDecl(
+          cxxMethodDecl(
+              hasTypeLoc(loc(functionProtoType(hasDynamicExceptionSpec()))),
+              anyOf(hasOverloadedOperatorName("delete[]"),
+                    hasOverloadedOperatorName("delete"), cxxDestructorDecl()))
+              .bind("del-dtor"))
+          .bind("funcDecl"),
+      this);
+
+  Finder->addMatcher(
+      functionDecl(
+          hasTypeLoc(loc(functionProtoType(hasDynamicExceptionSpec()))),
+          unless(anyOf(hasOverloadedOperatorName("delete[]"),
+                       hasOverloadedOperatorName("delete"),
+                       cxxDestructorDecl())))
+          .bind("funcDecl"),
+      this);
+
+  Finder->addMatcher(
+      parmVarDecl(anyOf(hasType(pointerType(pointee(parenType(innerType(
+                            functionProtoType(hasDynamicExceptionSpec())))))),
+                        hasType(memberPointerType(pointee(parenType(innerType(
+                            functionProtoType(hasDynamicExceptionSpec()))))))))
+          .bind("parmVarDecl"),
+      this);
+}
+
+void UseNoexceptCheck::check(const MatchFinder::MatchResult &Result) {
+  const FunctionProtoType *FnTy = nullptr;
+  bool DtorOrOperatorDel = false;
+  SourceRange Range;
+
+  if (const auto *FuncDecl = Result.Nodes.getNodeAs<FunctionDecl>("funcDecl")) {
+    DtorOrOperatorDel = Result.Nodes.getNodeAs<FunctionDecl>("del-dtor");
+    FnTy = FuncDecl->getType()->getAs<FunctionProtoType>();
+    if (const auto *TSI = FuncDecl->getTypeSourceInfo())
+      Range =
+          TSI->getTypeLoc().castAs<FunctionTypeLoc>().getExceptionSpecRange();
+  } else if (const auto *ParmDecl =
+                 Result.Nodes.getNodeAs<ParmVarDecl>("parmVarDecl")) {
+    FnTy = ParmDecl->getType()
+               ->getAs<Type>()
+               ->getPointeeType()
+               ->getAs<FunctionProtoType>();
+
+    if (const auto *TSI = ParmDecl->getTypeSourceInfo())
+      Range = TSI->getTypeLoc()
+                  .getNextTypeLoc()
+                  .IgnoreParens()
+                  .castAs<FunctionProtoTypeLoc>()
+                  .getExceptionSpecRange();
+  }
+  CharSourceRange CRange = Lexer::makeFileCharRange(
+      CharSourceRange::getTokenRange(Range), *Result.SourceManager,
+      Result.Context->getLangOpts());
+
+  assert(FnTy && "FunctionProtoType is null.");
+  bool IsNoThrow = FnTy->isNothrow(*Result.Context);
+  StringRef ReplacementStr =
+      IsNoThrow
+          ? NoexceptMacro.empty() ? "noexcept" : NoexceptMacro.c_str()
+          : NoexceptMacro.empty()
+                ? (DtorOrOperatorDel || UseNoexceptFalse) ? "noexcept(false)"
+                                                          : ""
+                : "";
+
+  FixItHint FixIt;
+  if ((IsNoThrow || NoexceptMacro.empty()) && CRange.isValid())
+    FixIt = FixItHint::CreateReplacement(CRange, ReplacementStr);
+
+  diag(Range.getBegin(), "dynamic exception specification '%0' is deprecated; "
+                         "consider %select{using '%2'|removing it}1 instead")
+      << Lexer::getSourceText(CRange, *Result.SourceManager,
+                              Result.Context->getLangOpts())
+      << ReplacementStr.empty() << ReplacementStr << FixIt;
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/modernize/UseNoexceptCheck.h b/clang-tidy/modernize/UseNoexceptCheck.h
new file mode 100644 (file)
index 0000000..a15867b
--- /dev/null
@@ -0,0 +1,49 @@
+//===--- UseNoexceptCheck.h - clang-tidy-------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_NOEXCEPT_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_NOEXCEPT_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+/// \brief Replace dynamic exception specifications, with
+/// `noexcept` (or user-defined macro) or `noexcept(false)`.
+/// \code
+///   void foo() throw();
+///   void bar() throw(int);
+/// \endcode
+/// Is converted to:
+/// \code
+///   void foo() ;
+///   void bar() noexcept(false);
+/// \endcode
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/modernize-use-noexcept.html
+class UseNoexceptCheck : public ClangTidyCheck {
+public:
+  UseNoexceptCheck(StringRef Name, ClangTidyContext *Context);
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+  const std::string NoexceptMacro;
+  bool UseNoexceptFalse;
+};
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_NOEXCEPT_H
diff --git a/clang-tidy/modernize/UseNullptrCheck.cpp b/clang-tidy/modernize/UseNullptrCheck.cpp
new file mode 100644 (file)
index 0000000..9c7eb04
--- /dev/null
@@ -0,0 +1,500 @@
+//===--- UseNullptrCheck.cpp - clang-tidy----------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "UseNullptrCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang;
+using namespace clang::ast_matchers;
+using namespace llvm;
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+namespace {
+
+const char CastSequence[] = "sequence";
+
+AST_MATCHER(Type, sugaredNullptrType) {
+  const Type *DesugaredType = Node.getUnqualifiedDesugaredType();
+  if (const auto *BT = dyn_cast<BuiltinType>(DesugaredType))
+    return BT->getKind() == BuiltinType::NullPtr;
+  return false;
+}
+
+/// \brief Create a matcher that finds implicit casts as well as the head of a
+/// sequence of zero or more nested explicit casts that have an implicit cast
+/// to null within.
+/// Finding sequences of explict casts is necessary so that an entire sequence
+/// can be replaced instead of just the inner-most implicit cast.
+StatementMatcher makeCastSequenceMatcher() {
+  StatementMatcher ImplicitCastToNull = implicitCastExpr(
+      anyOf(hasCastKind(CK_NullToPointer), hasCastKind(CK_NullToMemberPointer)),
+      unless(hasImplicitDestinationType(qualType(substTemplateTypeParmType()))),
+      unless(hasSourceExpression(hasType(sugaredNullptrType()))));
+
+  return castExpr(anyOf(ImplicitCastToNull,
+                        explicitCastExpr(hasDescendant(ImplicitCastToNull))),
+                  unless(hasAncestor(explicitCastExpr())))
+      .bind(CastSequence);
+}
+
+bool isReplaceableRange(SourceLocation StartLoc, SourceLocation EndLoc,
+                        const SourceManager &SM) {
+  return SM.isWrittenInSameFile(StartLoc, EndLoc);
+}
+
+/// \brief Replaces the provided range with the text "nullptr", but only if
+/// the start and end location are both in main file.
+/// Returns true if and only if a replacement was made.
+void replaceWithNullptr(ClangTidyCheck &Check, SourceManager &SM,
+                        SourceLocation StartLoc, SourceLocation EndLoc) {
+  CharSourceRange Range(SourceRange(StartLoc, EndLoc), true);
+  // Add a space if nullptr follows an alphanumeric character. This happens
+  // whenever there is an c-style explicit cast to nullptr not surrounded by
+  // parentheses and right beside a return statement.
+  SourceLocation PreviousLocation = StartLoc.getLocWithOffset(-1);
+  bool NeedsSpace = isAlphanumeric(*SM.getCharacterData(PreviousLocation));
+  Check.diag(Range.getBegin(), "use nullptr") << FixItHint::CreateReplacement(
+      Range, NeedsSpace ? " nullptr" : "nullptr");
+}
+
+/// \brief Returns the name of the outermost macro.
+///
+/// Given
+/// \code
+/// #define MY_NULL NULL
+/// \endcode
+/// If \p Loc points to NULL, this function will return the name MY_NULL.
+StringRef getOutermostMacroName(SourceLocation Loc, const SourceManager &SM,
+                                const LangOptions &LO) {
+  assert(Loc.isMacroID());
+  SourceLocation OutermostMacroLoc;
+
+  while (Loc.isMacroID()) {
+    OutermostMacroLoc = Loc;
+    Loc = SM.getImmediateMacroCallerLoc(Loc);
+  }
+
+  return Lexer::getImmediateMacroName(OutermostMacroLoc, SM, LO);
+}
+
+/// \brief RecursiveASTVisitor for ensuring all nodes rooted at a given AST
+/// subtree that have file-level source locations corresponding to a macro
+/// argument have implicit NullTo(Member)Pointer nodes as ancestors.
+class MacroArgUsageVisitor : public RecursiveASTVisitor<MacroArgUsageVisitor> {
+public:
+  MacroArgUsageVisitor(SourceLocation CastLoc, const SourceManager &SM)
+      : CastLoc(CastLoc), SM(SM), Visited(false), CastFound(false),
+        InvalidFound(false) {
+    assert(CastLoc.isFileID());
+  }
+
+  bool TraverseStmt(Stmt *S) {
+    bool VisitedPreviously = Visited;
+
+    if (!RecursiveASTVisitor<MacroArgUsageVisitor>::TraverseStmt(S))
+      return false;
+
+    // The point at which VisitedPreviously is false and Visited is true is the
+    // root of a subtree containing nodes whose locations match CastLoc. It's
+    // at this point we test that the Implicit NullTo(Member)Pointer cast was
+    // found or not.
+    if (!VisitedPreviously) {
+      if (Visited && !CastFound) {
+        // Found nodes with matching SourceLocations but didn't come across a
+        // cast. This is an invalid macro arg use. Can stop traversal
+        // completely now.
+        InvalidFound = true;
+        return false;
+      }
+      // Reset state as we unwind back up the tree.
+      CastFound = false;
+      Visited = false;
+    }
+    return true;
+  }
+
+  bool VisitStmt(Stmt *S) {
+    if (SM.getFileLoc(S->getLocStart()) != CastLoc)
+      return true;
+    Visited = true;
+
+    const ImplicitCastExpr *Cast = dyn_cast<ImplicitCastExpr>(S);
+    if (Cast && (Cast->getCastKind() == CK_NullToPointer ||
+                 Cast->getCastKind() == CK_NullToMemberPointer))
+      CastFound = true;
+
+    return true;
+  }
+
+  bool TraverseInitListExpr(InitListExpr *S) {
+    // Only go through the semantic form of the InitListExpr, because
+    // ImplicitCast might not appear in the syntactic form, and this results in
+    // finding usages of the macro argument that don't have a ImplicitCast as an
+    // ancestor (thus invalidating the replacement) when they actually have.
+    return RecursiveASTVisitor<MacroArgUsageVisitor>::
+        TraverseSynOrSemInitListExpr(
+            S->isSemanticForm() ? S : S->getSemanticForm());
+  }
+
+  bool foundInvalid() const { return InvalidFound; }
+
+private:
+  SourceLocation CastLoc;
+  const SourceManager &SM;
+
+  bool Visited;
+  bool CastFound;
+  bool InvalidFound;
+};
+
+/// \brief Looks for implicit casts as well as sequences of 0 or more explicit
+/// casts with an implicit null-to-pointer cast within.
+///
+/// The matcher this visitor is used with will find a single implicit cast or a
+/// top-most explicit cast (i.e. it has no explicit casts as an ancestor) where
+/// an implicit cast is nested within. However, there is no guarantee that only
+/// explicit casts exist between the found top-most explicit cast and the
+/// possibly more than one nested implicit cast. This visitor finds all cast
+/// sequences with an implicit cast to null within and creates a replacement
+/// leaving the outermost explicit cast unchanged to avoid introducing
+/// ambiguities.
+class CastSequenceVisitor : public RecursiveASTVisitor<CastSequenceVisitor> {
+public:
+  CastSequenceVisitor(ASTContext &Context, ArrayRef<StringRef> NullMacros,
+                      ClangTidyCheck &check)
+      : SM(Context.getSourceManager()), Context(Context),
+        NullMacros(NullMacros), Check(check), FirstSubExpr(nullptr),
+        PruneSubtree(false) {}
+
+  bool TraverseStmt(Stmt *S) {
+    // Stop traversing down the tree if requested.
+    if (PruneSubtree) {
+      PruneSubtree = false;
+      return true;
+    }
+    return RecursiveASTVisitor<CastSequenceVisitor>::TraverseStmt(S);
+  }
+
+  // Only VisitStmt is overridden as we shouldn't find other base AST types
+  // within a cast expression.
+  bool VisitStmt(Stmt *S) {
+    auto *C = dyn_cast<CastExpr>(S);
+    // Catch the castExpr inside cxxDefaultArgExpr.
+    if (auto *E = dyn_cast<CXXDefaultArgExpr>(S)) {
+      C = dyn_cast<CastExpr>(E->getExpr());
+      FirstSubExpr = nullptr;
+    }
+    if (!C) {
+      FirstSubExpr = nullptr;
+      return true;
+    }
+
+    auto* CastSubExpr = C->getSubExpr()->IgnoreParens();
+    // Ignore cast expressions which cast nullptr literal.
+    if (isa<CXXNullPtrLiteralExpr>(CastSubExpr)) {
+      return true;
+    }
+
+    if (!FirstSubExpr)
+      FirstSubExpr = CastSubExpr;
+
+    if (C->getCastKind() != CK_NullToPointer &&
+        C->getCastKind() != CK_NullToMemberPointer) {
+      return true;
+    }
+
+    SourceLocation StartLoc = FirstSubExpr->getLocStart();
+    SourceLocation EndLoc = FirstSubExpr->getLocEnd();
+
+    // If the location comes from a macro arg expansion, *all* uses of that
+    // arg must be checked to result in NullTo(Member)Pointer casts.
+    //
+    // If the location comes from a macro body expansion, check to see if its
+    // coming from one of the allowed 'NULL' macros.
+    if (SM.isMacroArgExpansion(StartLoc) && SM.isMacroArgExpansion(EndLoc)) {
+      SourceLocation FileLocStart = SM.getFileLoc(StartLoc),
+                     FileLocEnd = SM.getFileLoc(EndLoc);
+      SourceLocation ImmediateMarcoArgLoc, MacroLoc;
+      // Skip NULL macros used in macro.
+      if (!getMacroAndArgLocations(StartLoc, ImmediateMarcoArgLoc, MacroLoc) ||
+          ImmediateMarcoArgLoc != FileLocStart)
+        return skipSubTree();
+
+      if (isReplaceableRange(FileLocStart, FileLocEnd, SM) &&
+          allArgUsesValid(C)) {
+        replaceWithNullptr(Check, SM, FileLocStart, FileLocEnd);
+      }
+      return true;
+    }
+
+    if (SM.isMacroBodyExpansion(StartLoc) && SM.isMacroBodyExpansion(EndLoc)) {
+      StringRef OutermostMacroName =
+          getOutermostMacroName(StartLoc, SM, Context.getLangOpts());
+
+      // Check to see if the user wants to replace the macro being expanded.
+      if (std::find(NullMacros.begin(), NullMacros.end(), OutermostMacroName) ==
+          NullMacros.end()) {
+        return skipSubTree();
+      }
+
+      StartLoc = SM.getFileLoc(StartLoc);
+      EndLoc = SM.getFileLoc(EndLoc);
+    }
+
+    if (!isReplaceableRange(StartLoc, EndLoc, SM)) {
+      return skipSubTree();
+    }
+    replaceWithNullptr(Check, SM, StartLoc, EndLoc);
+
+    return true;
+  }
+
+private:
+  bool skipSubTree() {
+    PruneSubtree = true;
+    return true;
+  }
+
+  /// \brief Tests that all expansions of a macro arg, one of which expands to
+  /// result in \p CE, yield NullTo(Member)Pointer casts.
+  bool allArgUsesValid(const CastExpr *CE) {
+    SourceLocation CastLoc = CE->getLocStart();
+
+    // Step 1: Get location of macro arg and location of the macro the arg was
+    // provided to.
+    SourceLocation ArgLoc, MacroLoc;
+    if (!getMacroAndArgLocations(CastLoc, ArgLoc, MacroLoc))
+      return false;
+
+    // Step 2: Find the first ancestor that doesn't expand from this macro.
+    ast_type_traits::DynTypedNode ContainingAncestor;
+    if (!findContainingAncestor(
+            ast_type_traits::DynTypedNode::create<Stmt>(*CE), MacroLoc,
+            ContainingAncestor))
+      return false;
+
+    // Step 3:
+    // Visit children of this containing parent looking for the least-descended
+    // nodes of the containing parent which are macro arg expansions that expand
+    // from the given arg location.
+    // Visitor needs: arg loc.
+    MacroArgUsageVisitor ArgUsageVisitor(SM.getFileLoc(CastLoc), SM);
+    if (const auto *D = ContainingAncestor.get<Decl>())
+      ArgUsageVisitor.TraverseDecl(const_cast<Decl *>(D));
+    else if (const auto *S = ContainingAncestor.get<Stmt>())
+      ArgUsageVisitor.TraverseStmt(const_cast<Stmt *>(S));
+    else
+      llvm_unreachable("Unhandled ContainingAncestor node type");
+
+    return !ArgUsageVisitor.foundInvalid();
+  }
+
+  /// \brief Given the SourceLocation for a macro arg expansion, finds the
+  /// non-macro SourceLocation of the macro the arg was passed to and the
+  /// non-macro SourceLocation of the argument in the arg list to that macro.
+  /// These results are returned via \c MacroLoc and \c ArgLoc respectively.
+  /// These values are undefined if the return value is false.
+  ///
+  /// \returns false if one of the returned SourceLocations would be a
+  /// SourceLocation pointing within the definition of another macro.
+  bool getMacroAndArgLocations(SourceLocation Loc, SourceLocation &ArgLoc,
+                               SourceLocation &MacroLoc) {
+    assert(Loc.isMacroID() && "Only reasonble to call this on macros");
+
+    ArgLoc = Loc;
+
+    // Find the location of the immediate macro expansion.
+    while (true) {
+      std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(ArgLoc);
+      const SrcMgr::SLocEntry *E = &SM.getSLocEntry(LocInfo.first);
+      const SrcMgr::ExpansionInfo &Expansion = E->getExpansion();
+
+      SourceLocation OldArgLoc = ArgLoc;
+      ArgLoc = Expansion.getExpansionLocStart();
+      if (!Expansion.isMacroArgExpansion()) {
+        if (!MacroLoc.isFileID())
+          return false;
+
+        StringRef Name =
+            Lexer::getImmediateMacroName(OldArgLoc, SM, Context.getLangOpts());
+        return std::find(NullMacros.begin(), NullMacros.end(), Name) !=
+               NullMacros.end();
+      }
+
+      MacroLoc = SM.getExpansionRange(ArgLoc).first;
+
+      ArgLoc = Expansion.getSpellingLoc().getLocWithOffset(LocInfo.second);
+      if (ArgLoc.isFileID())
+        return true;
+
+      // If spelling location resides in the same FileID as macro expansion
+      // location, it means there is no inner macro.
+      FileID MacroFID = SM.getFileID(MacroLoc);
+      if (SM.isInFileID(ArgLoc, MacroFID)) {
+        // Don't transform this case. If the characters that caused the
+        // null-conversion come from within a macro, they can't be changed.
+        return false;
+      }
+    }
+
+    llvm_unreachable("getMacroAndArgLocations");
+  }
+
+  /// \brief Tests if TestMacroLoc is found while recursively unravelling
+  /// expansions starting at TestLoc. TestMacroLoc.isFileID() must be true.
+  /// Implementation is very similar to getMacroAndArgLocations() except in this
+  /// case, it's not assumed that TestLoc is expanded from a macro argument.
+  /// While unravelling expansions macro arguments are handled as with
+  /// getMacroAndArgLocations() but in this function macro body expansions are
+  /// also handled.
+  ///
+  /// False means either:
+  /// - TestLoc is not from a macro expansion.
+  /// - TestLoc is from a different macro expansion.
+  bool expandsFrom(SourceLocation TestLoc, SourceLocation TestMacroLoc) {
+    if (TestLoc.isFileID()) {
+      return false;
+    }
+
+    SourceLocation Loc = TestLoc, MacroLoc;
+
+    while (true) {
+      std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
+      const SrcMgr::SLocEntry *E = &SM.getSLocEntry(LocInfo.first);
+      const SrcMgr::ExpansionInfo &Expansion = E->getExpansion();
+
+      Loc = Expansion.getExpansionLocStart();
+
+      if (!Expansion.isMacroArgExpansion()) {
+        if (Loc.isFileID()) {
+          return Loc == TestMacroLoc;
+        }
+        // Since Loc is still a macro ID and it's not an argument expansion, we
+        // don't need to do the work of handling an argument expansion. Simply
+        // keep recursively expanding until we hit a FileID or a macro arg
+        // expansion or a macro arg expansion.
+        continue;
+      }
+
+      MacroLoc = SM.getImmediateExpansionRange(Loc).first;
+      if (MacroLoc.isFileID() && MacroLoc == TestMacroLoc) {
+        // Match made.
+        return true;
+      }
+
+      Loc = Expansion.getSpellingLoc().getLocWithOffset(LocInfo.second);
+      if (Loc.isFileID()) {
+        // If we made it this far without finding a match, there is no match to
+        // be made.
+        return false;
+      }
+    }
+
+    llvm_unreachable("expandsFrom");
+  }
+
+  /// \brief Given a starting point \c Start in the AST, find an ancestor that
+  /// doesn't expand from the macro called at file location \c MacroLoc.
+  ///
+  /// \pre MacroLoc.isFileID()
+  /// \returns true if such an ancestor was found, false otherwise.
+  bool findContainingAncestor(ast_type_traits::DynTypedNode Start,
+                              SourceLocation MacroLoc,
+                              ast_type_traits::DynTypedNode &Result) {
+    // Below we're only following the first parent back up the AST. This should
+    // be fine since for the statements we care about there should only be one
+    // parent, except for the case specified below.
+
+    assert(MacroLoc.isFileID());
+
+    while (true) {
+      const auto &Parents = Context.getParents(Start);
+      if (Parents.empty())
+        return false;
+      if (Parents.size() > 1) {
+        // If there are more than one parents, don't do the replacement unless
+        // they are InitListsExpr (semantic and syntactic form). In this case we
+        // can choose any one here, and the ASTVisitor will take care of
+        // traversing the right one.
+        for (const auto &Parent : Parents) {
+          if (!Parent.get<InitListExpr>())
+            return false;
+        }
+      }
+
+      const ast_type_traits::DynTypedNode &Parent = Parents[0];
+
+      SourceLocation Loc;
+      if (const auto *D = Parent.get<Decl>())
+        Loc = D->getLocStart();
+      else if (const auto *S = Parent.get<Stmt>())
+        Loc = S->getLocStart();
+
+      // TypeLoc and NestedNameSpecifierLoc are members of the parent map. Skip
+      // them and keep going up.
+      if (Loc.isValid()) {
+        if (!expandsFrom(Loc, MacroLoc)) {
+          Result = Parent;
+          return true;
+        }
+      }
+      Start = Parent;
+    }
+
+    llvm_unreachable("findContainingAncestor");
+  }
+
+private:
+  SourceManager &SM;
+  ASTContext &Context;
+  ArrayRef<StringRef> NullMacros;
+  ClangTidyCheck &Check;
+  Expr *FirstSubExpr;
+  bool PruneSubtree;
+};
+
+} // namespace
+
+UseNullptrCheck::UseNullptrCheck(StringRef Name, ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      NullMacrosStr(Options.get("NullMacros", "")) {
+  StringRef(NullMacrosStr).split(NullMacros, ",");
+}
+
+void UseNullptrCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "NullMacros", NullMacrosStr);
+}
+
+void UseNullptrCheck::registerMatchers(MatchFinder *Finder) {
+  // Only register the matcher for C++. Because this checker is used for
+  // modernization, it is reasonable to run it on any C++ standard with the
+  // assumption the user is trying to modernize their codebase.
+  if (getLangOpts().CPlusPlus)
+    Finder->addMatcher(makeCastSequenceMatcher(), this);
+}
+
+void UseNullptrCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *NullCast = Result.Nodes.getNodeAs<CastExpr>(CastSequence);
+  assert(NullCast && "Bad Callback. No node provided");
+
+  // Given an implicit null-ptr cast or an explicit cast with an implicit
+  // null-to-pointer cast within use CastSequenceVisitor to identify sequences
+  // of explicit casts that can be converted into 'nullptr'.
+  CastSequenceVisitor(*Result.Context, NullMacros, *this)
+      .TraverseStmt(const_cast<CastExpr *>(NullCast));
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/modernize/UseNullptrCheck.h b/clang-tidy/modernize/UseNullptrCheck.h
new file mode 100644 (file)
index 0000000..4b33f1e
--- /dev/null
@@ -0,0 +1,35 @@
+//===--- UseNullptrCheck.h - clang-tidy--------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_NULLPTR_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_NULLPTR_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+class UseNullptrCheck : public ClangTidyCheck {
+public:
+  UseNullptrCheck(StringRef Name, ClangTidyContext *Context);
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+  const std::string NullMacrosStr;
+  SmallVector<StringRef, 1> NullMacros;
+};
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_NULLPTR_H
diff --git a/clang-tidy/modernize/UseOverrideCheck.cpp b/clang-tidy/modernize/UseOverrideCheck.cpp
new file mode 100644 (file)
index 0000000..9429eb2
--- /dev/null
@@ -0,0 +1,207 @@
+//===--- UseOverrideCheck.cpp - clang-tidy --------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "UseOverrideCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+void UseOverrideCheck::registerMatchers(MatchFinder *Finder) {
+  // Only register the matcher for C++11.
+  if (getLangOpts().CPlusPlus11)
+    Finder->addMatcher(cxxMethodDecl(isOverride()).bind("method"), this);
+}
+
+// Re-lex the tokens to get precise locations to insert 'override' and remove
+// 'virtual'.
+static SmallVector<Token, 16>
+ParseTokens(CharSourceRange Range, const MatchFinder::MatchResult &Result) {
+  const SourceManager &Sources = *Result.SourceManager;
+  std::pair<FileID, unsigned> LocInfo =
+      Sources.getDecomposedLoc(Range.getBegin());
+  StringRef File = Sources.getBufferData(LocInfo.first);
+  const char *TokenBegin = File.data() + LocInfo.second;
+  Lexer RawLexer(Sources.getLocForStartOfFile(LocInfo.first),
+                 Result.Context->getLangOpts(), File.begin(), TokenBegin,
+                 File.end());
+  SmallVector<Token, 16> Tokens;
+  Token Tok;
+  int NestedParens = 0;
+  while (!RawLexer.LexFromRawLexer(Tok)) {
+    if ((Tok.is(tok::semi) || Tok.is(tok::l_brace)) && NestedParens == 0)
+      break;
+    if (Sources.isBeforeInTranslationUnit(Range.getEnd(), Tok.getLocation()))
+      break;
+    if (Tok.is(tok::l_paren))
+      ++NestedParens;
+    else if (Tok.is(tok::r_paren))
+      --NestedParens;
+    if (Tok.is(tok::raw_identifier)) {
+      IdentifierInfo &Info = Result.Context->Idents.get(StringRef(
+          Sources.getCharacterData(Tok.getLocation()), Tok.getLength()));
+      Tok.setIdentifierInfo(&Info);
+      Tok.setKind(Info.getTokenID());
+    }
+    Tokens.push_back(Tok);
+  }
+  return Tokens;
+}
+
+static StringRef GetText(const Token &Tok, const SourceManager &Sources) {
+  return StringRef(Sources.getCharacterData(Tok.getLocation()),
+                   Tok.getLength());
+}
+
+void UseOverrideCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *Method = Result.Nodes.getNodeAs<FunctionDecl>("method");
+  const SourceManager &Sources = *Result.SourceManager;
+
+  assert(Method != nullptr);
+  if (Method->getInstantiatedFromMemberFunction() != nullptr)
+    Method = Method->getInstantiatedFromMemberFunction();
+
+  if (Method->isImplicit() || Method->getLocation().isMacroID() ||
+      Method->isOutOfLine())
+    return;
+
+  bool HasVirtual = Method->isVirtualAsWritten();
+  bool HasOverride = Method->getAttr<OverrideAttr>();
+  bool HasFinal = Method->getAttr<FinalAttr>();
+
+  bool OnlyVirtualSpecified = HasVirtual && !HasOverride && !HasFinal;
+  unsigned KeywordCount = HasVirtual + HasOverride + HasFinal;
+
+  if (!OnlyVirtualSpecified && KeywordCount == 1)
+    return; // Nothing to do.
+
+  std::string Message;
+
+  if (OnlyVirtualSpecified) {
+    Message =
+        "prefer using 'override' or (rarely) 'final' instead of 'virtual'";
+  } else if (KeywordCount == 0) {
+    Message = "annotate this function with 'override' or (rarely) 'final'";
+  } else {
+    StringRef Redundant =
+        HasVirtual ? (HasOverride && HasFinal ? "'virtual' and 'override' are"
+                                              : "'virtual' is")
+                   : "'override' is";
+    StringRef Correct = HasFinal ? "'final'" : "'override'";
+
+    Message = (llvm::Twine(Redundant) +
+               " redundant since the function is already declared " + Correct)
+                  .str();
+  }
+
+  DiagnosticBuilder Diag = diag(Method->getLocation(), Message);
+
+  CharSourceRange FileRange = Lexer::makeFileCharRange(
+      CharSourceRange::getTokenRange(Method->getSourceRange()), Sources,
+      getLangOpts());
+
+  if (!FileRange.isValid())
+    return;
+
+  // FIXME: Instead of re-lexing and looking for specific macros such as
+  // 'ABSTRACT', properly store the location of 'virtual' and '= 0' in each
+  // FunctionDecl.
+  SmallVector<Token, 16> Tokens = ParseTokens(FileRange, Result);
+
+  // Add 'override' on inline declarations that don't already have it.
+  if (!HasFinal && !HasOverride) {
+    SourceLocation InsertLoc;
+    StringRef ReplacementText = "override ";
+    SourceLocation MethodLoc = Method->getLocation();
+
+    for (Token T : Tokens) {
+      if (T.is(tok::kw___attribute) &&
+          !Sources.isBeforeInTranslationUnit(T.getLocation(), MethodLoc)) {
+        InsertLoc = T.getLocation();
+        break;
+      }
+    }
+
+    if (Method->hasAttrs()) {
+      for (const clang::Attr *A : Method->getAttrs()) {
+        if (!A->isImplicit() && !A->isInherited()) {
+          SourceLocation Loc =
+              Sources.getExpansionLoc(A->getRange().getBegin());
+          if ((!InsertLoc.isValid() ||
+               Sources.isBeforeInTranslationUnit(Loc, InsertLoc)) &&
+              !Sources.isBeforeInTranslationUnit(Loc, MethodLoc))
+            InsertLoc = Loc;
+        }
+      }
+    }
+
+    if (InsertLoc.isInvalid() && Method->doesThisDeclarationHaveABody() &&
+        Method->getBody() && !Method->isDefaulted()) {
+      // For methods with inline definition, add the override keyword at the
+      // end of the declaration of the function, but prefer to put it on the
+      // same line as the declaration if the beginning brace for the start of
+      // the body falls on the next line.
+      ReplacementText = " override";
+      auto LastTokenIter = std::prev(Tokens.end());
+      // When try statement is used instead of compound statement as
+      // method body - insert override keyword before it.
+      if (LastTokenIter->is(tok::kw_try))
+        LastTokenIter = std::prev(LastTokenIter);
+      InsertLoc = LastTokenIter->getEndLoc();
+    }
+
+    if (!InsertLoc.isValid()) {
+      // For declarations marked with "= 0" or "= [default|delete]", the end
+      // location will point until after those markings. Therefore, the override
+      // keyword shouldn't be inserted at the end, but before the '='.
+      if (Tokens.size() > 2 && (GetText(Tokens.back(), Sources) == "0" ||
+                                Tokens.back().is(tok::kw_default) ||
+                                Tokens.back().is(tok::kw_delete)) &&
+          GetText(Tokens[Tokens.size() - 2], Sources) == "=") {
+        InsertLoc = Tokens[Tokens.size() - 2].getLocation();
+        // Check if we need to insert a space.
+        if ((Tokens[Tokens.size() - 2].getFlags() & Token::LeadingSpace) == 0)
+          ReplacementText = " override ";
+      } else if (GetText(Tokens.back(), Sources) == "ABSTRACT") {
+        InsertLoc = Tokens.back().getLocation();
+      }
+    }
+
+    if (!InsertLoc.isValid()) {
+      InsertLoc = FileRange.getEnd();
+      ReplacementText = " override";
+    }
+    Diag << FixItHint::CreateInsertion(InsertLoc, ReplacementText);
+  }
+
+  if (HasFinal && HasOverride) {
+    SourceLocation OverrideLoc = Method->getAttr<OverrideAttr>()->getLocation();
+    Diag << FixItHint::CreateRemoval(
+        CharSourceRange::getTokenRange(OverrideLoc, OverrideLoc));
+  }
+
+  if (HasVirtual) {
+    for (Token Tok : Tokens) {
+      if (Tok.is(tok::kw_virtual)) {
+        Diag << FixItHint::CreateRemoval(CharSourceRange::getTokenRange(
+            Tok.getLocation(), Tok.getLocation()));
+        break;
+      }
+    }
+  }
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/modernize/UseOverrideCheck.h b/clang-tidy/modernize/UseOverrideCheck.h
new file mode 100644 (file)
index 0000000..83ce7da
--- /dev/null
@@ -0,0 +1,32 @@
+//===--- UseOverrideCheck.h - clang-tidy ------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USEOVERRIDECHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USEOVERRIDECHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+/// Use C++11's `override` and remove `virtual` where applicable.
+class UseOverrideCheck : public ClangTidyCheck {
+public:
+  UseOverrideCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USEOVERRIDECHECK_H
diff --git a/clang-tidy/modernize/UseTransparentFunctorsCheck.cpp b/clang-tidy/modernize/UseTransparentFunctorsCheck.cpp
new file mode 100644 (file)
index 0000000..7f59200
--- /dev/null
@@ -0,0 +1,131 @@
+//===--- UseTransparentFunctorsCheck.cpp - clang-tidy----------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "UseTransparentFunctorsCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+UseTransparentFunctorsCheck::UseTransparentFunctorsCheck(
+    StringRef Name, ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context), SafeMode(Options.get("SafeMode", 0)) {}
+
+void UseTransparentFunctorsCheck::storeOptions(
+    ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "SafeMode", SafeMode ? 1 : 0);
+}
+
+void UseTransparentFunctorsCheck::registerMatchers(MatchFinder *Finder) {
+  if (!getLangOpts().CPlusPlus14)
+    return;
+
+  const auto TransparentFunctors =
+      classTemplateSpecializationDecl(
+          unless(hasAnyTemplateArgument(refersToType(voidType()))),
+          hasAnyName("::std::plus", "::std::minus", "::std::multiplies",
+                     "::std::divides", "::std::modulus", "::std::negate",
+                     "::std::equal_to", "::std::not_equal_to", "::std::greater",
+                     "::std::less", "::std::greater_equal", "::std::less_equal",
+                     "::std::logical_and", "::std::logical_or",
+                     "::std::logical_not", "::std::bit_and", "::std::bit_or",
+                     "::std::bit_xor", "::std::bit_not"))
+          .bind("FunctorClass");
+
+  // Non-transparent functor mentioned as a template parameter. FIXIT.
+  Finder->addMatcher(
+      loc(qualType(
+              unless(elaboratedType()),
+              hasDeclaration(classTemplateSpecializationDecl(
+                  unless(hasAnyTemplateArgument(templateArgument(refersToType(
+                      qualType(pointsTo(qualType(isAnyCharacter()))))))),
+                  hasAnyTemplateArgument(
+                      templateArgument(refersToType(qualType(hasDeclaration(
+                                           TransparentFunctors))))
+                          .bind("Functor"))))))
+          .bind("FunctorParentLoc"),
+      this);
+
+  if (SafeMode)
+    return;
+
+  // Non-transparent functor constructed. No FIXIT. There is no easy way
+  // to rule out the problematic char* vs string case.
+  Finder->addMatcher(cxxConstructExpr(hasDeclaration(cxxMethodDecl(
+                                          ofClass(TransparentFunctors))),
+                                      unless(isInTemplateInstantiation()))
+                         .bind("FuncInst"),
+                     this);
+}
+
+static const StringRef Message = "prefer transparent functors '%0'";
+
+template <typename T> static T getInnerTypeLocAs(TypeLoc Loc) {
+  T Result;
+  while (Result.isNull() && !Loc.isNull()) {
+    Result = Loc.getAs<T>();
+    Loc = Loc.getNextTypeLoc();
+  }
+  return Result;
+}
+
+void UseTransparentFunctorsCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  const auto *FuncClass =
+      Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>("FunctorClass");
+  if (const auto *FuncInst =
+          Result.Nodes.getNodeAs<CXXConstructExpr>("FuncInst")) {
+    diag(FuncInst->getLocStart(), Message)
+          << (FuncClass->getName() + "<>").str();
+    return;
+  }
+
+  const auto *Functor = Result.Nodes.getNodeAs<TemplateArgument>("Functor");
+  const auto FunctorParentLoc =
+      Result.Nodes.getNodeAs<TypeLoc>("FunctorParentLoc")
+          ->getAs<TemplateSpecializationTypeLoc>();
+
+  if (!FunctorParentLoc)
+    return;
+
+  unsigned ArgNum = 0;
+  const auto *FunctorParentType =
+      FunctorParentLoc.getType()->castAs<TemplateSpecializationType>();
+  for (; ArgNum < FunctorParentType->getNumArgs(); ++ArgNum) {
+    const TemplateArgument &Arg = FunctorParentType->getArg(ArgNum);
+    if (Arg.getKind() != TemplateArgument::Type)
+      continue;
+    QualType ParentArgType = Arg.getAsType();
+    if (ParentArgType->isRecordType() &&
+        ParentArgType->getAsCXXRecordDecl() ==
+            Functor->getAsType()->getAsCXXRecordDecl())
+      break;
+  }
+  // Functor is a default template argument.
+  if (ArgNum == FunctorParentType->getNumArgs())
+    return;
+  TemplateArgumentLoc FunctorLoc = FunctorParentLoc.getArgLoc(ArgNum);
+  auto FunctorTypeLoc = getInnerTypeLocAs<TemplateSpecializationTypeLoc>(
+      FunctorLoc.getTypeSourceInfo()->getTypeLoc());
+  if (FunctorTypeLoc.isNull())
+    return;
+
+  SourceLocation ReportLoc = FunctorLoc.getLocation();
+  diag(ReportLoc, Message) << (FuncClass->getName() + "<>").str()
+                           << FixItHint::CreateRemoval(
+                                  FunctorTypeLoc.getArgLoc(0).getSourceRange());
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/modernize/UseTransparentFunctorsCheck.h b/clang-tidy/modernize/UseTransparentFunctorsCheck.h
new file mode 100644 (file)
index 0000000..4bdce76
--- /dev/null
@@ -0,0 +1,37 @@
+//===--- UseTransparentFunctorsCheck.h - clang-tidy--------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_TRANSPARENT_FUNCTORS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_TRANSPARENT_FUNCTORS_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+/// Prefer using transparent functors to non-transparent ones.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/modernize-use-transparent-functors.html
+class UseTransparentFunctorsCheck : public ClangTidyCheck {
+public:
+  UseTransparentFunctorsCheck(StringRef Name, ClangTidyContext *Context);
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+private:
+  const bool SafeMode;
+};
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_TRANSPARENT_FUNCTORS_H
diff --git a/clang-tidy/modernize/UseUsingCheck.cpp b/clang-tidy/modernize/UseUsingCheck.cpp
new file mode 100644 (file)
index 0000000..cc6d77d
--- /dev/null
@@ -0,0 +1,113 @@
+//===--- UseUsingCheck.cpp - clang-tidy------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "UseUsingCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+UseUsingCheck::UseUsingCheck(StringRef Name, ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      IgnoreMacros(Options.getLocalOrGlobal("IgnoreMacros", true)) {}
+
+void UseUsingCheck::registerMatchers(MatchFinder *Finder) {
+  if (!getLangOpts().CPlusPlus11)
+    return;
+  Finder->addMatcher(typedefDecl().bind("typedef"), this);
+}
+
+// Checks if 'typedef' keyword can be removed - we do it only if
+// it is the only declaration in a declaration chain.
+static bool CheckRemoval(SourceManager &SM, SourceLocation StartLoc,
+                         ASTContext &Context) {
+  assert(StartLoc.isFileID() && "StartLoc must not be in a macro");
+  std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(StartLoc);
+  StringRef File = SM.getBufferData(LocInfo.first);
+  const char *TokenBegin = File.data() + LocInfo.second;
+  Lexer DeclLexer(SM.getLocForStartOfFile(LocInfo.first), Context.getLangOpts(),
+                  File.begin(), TokenBegin, File.end());
+
+  Token Tok;
+  int ParenLevel = 0;
+  bool FoundTypedef = false;
+
+  while (!DeclLexer.LexFromRawLexer(Tok) && !Tok.is(tok::semi)) {
+    switch (Tok.getKind()) {
+    case tok::l_brace:
+    case tok::r_brace:
+      // This might be the `typedef struct {...} T;` case.
+      return false;
+    case tok::l_paren:
+      ParenLevel++;
+      break;
+    case tok::r_paren:
+      ParenLevel--;
+      break;
+    case tok::comma:
+      if (ParenLevel == 0) {
+        // If there is comma and we are not between open parenthesis then it is
+        // two or more declarations in this chain.
+        return false;
+      }
+      break;
+    case tok::raw_identifier:
+      if (Tok.getRawIdentifier() == "typedef") {
+        FoundTypedef = true;
+      }
+      break;
+    default:
+      break;
+    }
+  }
+
+  // Sanity check against weird macro cases.
+  return FoundTypedef;
+}
+
+void UseUsingCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *MatchedDecl = Result.Nodes.getNodeAs<TypedefDecl>("typedef");
+  if (MatchedDecl->getLocation().isInvalid())
+    return;
+
+  auto &Context = *Result.Context;
+  auto &SM = *Result.SourceManager;
+
+  SourceLocation StartLoc = MatchedDecl->getLocStart();
+
+  if (StartLoc.isMacroID() && IgnoreMacros)
+    return;
+
+  auto Diag =
+      diag(StartLoc, "use 'using' instead of 'typedef'");
+
+  // do not fix if there is macro or array
+  if (MatchedDecl->getUnderlyingType()->isArrayType() || StartLoc.isMacroID())
+    return;
+
+  if (CheckRemoval(SM, StartLoc, Context)) {
+    auto printPolicy = PrintingPolicy(getLangOpts());
+    printPolicy.SuppressScope = true;
+    printPolicy.ConstantArraySizeAsWritten = true;
+    printPolicy.UseVoidForZeroParams = false;
+
+    Diag << FixItHint::CreateReplacement(
+        MatchedDecl->getSourceRange(),
+        "using " + MatchedDecl->getNameAsString() + " = " +
+            MatchedDecl->getUnderlyingType().getAsString(printPolicy));
+  }
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/modernize/UseUsingCheck.h b/clang-tidy/modernize/UseUsingCheck.h
new file mode 100644 (file)
index 0000000..022eef0
--- /dev/null
@@ -0,0 +1,40 @@
+//===--- UseUsingCheck.h - clang-tidy----------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_USING_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_USING_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+/// Check finds typedefs and replaces it with usings.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/modernize-use-using.html
+class UseUsingCheck : public ClangTidyCheck {
+
+  const bool IgnoreMacros;
+
+public:
+  UseUsingCheck(StringRef Name, ClangTidyContext *Context);
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override {
+    Options.store(Opts, "IgnoreMacros", IgnoreMacros);
+  }
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_USING_H
diff --git a/clang-tidy/mpi/BufferDerefCheck.cpp b/clang-tidy/mpi/BufferDerefCheck.cpp
new file mode 100644 (file)
index 0000000..8e2c0e3
--- /dev/null
@@ -0,0 +1,132 @@
+//===--- BufferDerefCheck.cpp - clang-tidy---------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "BufferDerefCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/StaticAnalyzer/Checkers/MPIFunctionClassifier.h"
+#include "clang/Tooling/FixIt.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace mpi {
+
+void BufferDerefCheck::registerMatchers(MatchFinder *Finder) {
+  Finder->addMatcher(callExpr().bind("CE"), this);
+}
+
+void BufferDerefCheck::check(const MatchFinder::MatchResult &Result) {
+  static ento::mpi::MPIFunctionClassifier FuncClassifier(*Result.Context);
+  const auto *CE = Result.Nodes.getNodeAs<CallExpr>("CE");
+  if (!CE->getDirectCallee())
+    return;
+
+  const IdentifierInfo *Identifier = CE->getDirectCallee()->getIdentifier();
+  if (!Identifier || !FuncClassifier.isMPIType(Identifier))
+    return;
+
+  // These containers are used, to capture the type and expression of a buffer.
+  SmallVector<const Type *, 1> BufferTypes;
+  SmallVector<const Expr *, 1> BufferExprs;
+
+  // Adds the type and expression of a buffer that is used in the MPI call
+  // expression to the captured containers.
+  auto addBuffer = [&CE, &Result, &BufferTypes,
+                    &BufferExprs](const size_t BufferIdx) {
+    // Skip null pointer constants and in place 'operators'.
+    if (CE->getArg(BufferIdx)->isNullPointerConstant(
+            *Result.Context, Expr::NPC_ValueDependentIsNull) ||
+        tooling::fixit::getText(*CE->getArg(BufferIdx), *Result.Context) ==
+            "MPI_IN_PLACE")
+      return;
+
+    const Expr *ArgExpr = CE->getArg(BufferIdx);
+    if (!ArgExpr)
+      return;
+    const Type *ArgType = ArgExpr->IgnoreImpCasts()->getType().getTypePtr();
+    if (!ArgType)
+      return;
+    BufferExprs.push_back(ArgExpr);
+    BufferTypes.push_back(ArgType);
+  };
+
+  // Collect buffer types and argument expressions for all buffers used in the
+  // MPI call expression. The number passed to the lambda corresponds to the
+  // argument index of the currently verified MPI function call.
+  if (FuncClassifier.isPointToPointType(Identifier)) {
+    addBuffer(0);
+  } else if (FuncClassifier.isCollectiveType(Identifier)) {
+    if (FuncClassifier.isReduceType(Identifier)) {
+      addBuffer(0);
+      addBuffer(1);
+    } else if (FuncClassifier.isScatterType(Identifier) ||
+               FuncClassifier.isGatherType(Identifier) ||
+               FuncClassifier.isAlltoallType(Identifier)) {
+      addBuffer(0);
+      addBuffer(3);
+    } else if (FuncClassifier.isBcastType(Identifier)) {
+      addBuffer(0);
+    }
+  }
+
+  checkBuffers(BufferTypes, BufferExprs);
+}
+
+void BufferDerefCheck::checkBuffers(ArrayRef<const Type *> BufferTypes,
+                                    ArrayRef<const Expr *> BufferExprs) {
+  for (size_t i = 0; i < BufferTypes.size(); ++i) {
+    unsigned IndirectionCount = 0;
+    const Type *BufferType = BufferTypes[i];
+    llvm::SmallVector<IndirectionType, 1> Indirections;
+
+    // Capture the depth and types of indirections for the passed buffer.
+    while (true) {
+      if (BufferType->isPointerType()) {
+        BufferType = BufferType->getPointeeType().getTypePtr();
+        Indirections.push_back(IndirectionType::Pointer);
+      } else if (BufferType->isArrayType()) {
+        BufferType = BufferType->getArrayElementTypeNoTypeQual();
+        Indirections.push_back(IndirectionType::Array);
+      } else {
+        break;
+      }
+      ++IndirectionCount;
+    }
+
+    if (IndirectionCount > 1) {
+      // Referencing an array with '&' is valid, as this also points to the
+      // beginning of the array.
+      if (IndirectionCount == 2 &&
+          Indirections[0] == IndirectionType::Pointer &&
+          Indirections[1] == IndirectionType::Array)
+        return;
+
+      // Build the indirection description in reverse order of discovery.
+      std::string IndirectionDesc;
+      for (auto It = Indirections.rbegin(); It != Indirections.rend(); ++It) {
+        if (!IndirectionDesc.empty())
+          IndirectionDesc += "->";
+        if (*It == IndirectionType::Pointer) {
+          IndirectionDesc += "pointer";
+        } else {
+          IndirectionDesc += "array";
+        }
+      }
+
+      const auto Loc = BufferExprs[i]->getSourceRange().getBegin();
+      diag(Loc, "buffer is insufficiently dereferenced: %0") << IndirectionDesc;
+    }
+  }
+}
+
+} // namespace mpi
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/mpi/BufferDerefCheck.h b/clang-tidy/mpi/BufferDerefCheck.h
new file mode 100644 (file)
index 0000000..8490fa1
--- /dev/null
@@ -0,0 +1,51 @@
+//===--- BufferDerefCheck.h - clang-tidy-------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MPI_BUFFER_DEREF_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MPI_BUFFER_DEREF_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace mpi {
+
+/// This check verifies if a buffer passed to an MPI (Message Passing Interface)
+/// function is sufficiently dereferenced. Buffers should be passed as a single
+/// pointer or array. As MPI function signatures specify void * for their buffer
+/// types, insufficiently dereferenced buffers can be passed, like for example
+/// as double pointers or multidimensional arrays, without a compiler warning
+/// emitted.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/mpi-buffer-deref.html
+class BufferDerefCheck : public ClangTidyCheck {
+public:
+  BufferDerefCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+  /// Checks for all buffers in an MPI call if they are sufficiently
+  /// dereferenced.
+  ///
+  /// \param BufferTypes buffer types
+  /// \param BufferExprs buffer arguments as expressions
+  void checkBuffers(ArrayRef<const Type *> BufferTypes,
+                    ArrayRef<const Expr *> BufferExprs);
+
+  enum class IndirectionType : unsigned char { Pointer, Array };
+};
+
+} // namespace mpi
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MPI_BUFFER_DEREF_H
diff --git a/clang-tidy/mpi/CMakeLists.txt b/clang-tidy/mpi/CMakeLists.txt
new file mode 100644 (file)
index 0000000..3658434
--- /dev/null
@@ -0,0 +1,17 @@
+set(LLVM_LINK_COMPONENTS support)
+
+add_clang_library(clangTidyMPIModule
+  BufferDerefCheck.cpp
+  MPITidyModule.cpp
+  TypeMismatchCheck.cpp
+
+  LINK_LIBS
+  clangAST
+  clangASTMatchers
+  clangBasic
+  clangLex
+  clangTidy
+  clangTidyUtils
+  clangTooling
+  clangStaticAnalyzerCheckers
+  )
diff --git a/clang-tidy/mpi/MPITidyModule.cpp b/clang-tidy/mpi/MPITidyModule.cpp
new file mode 100644 (file)
index 0000000..55e187d
--- /dev/null
@@ -0,0 +1,39 @@
+//===--- MPITidyModule.cpp - clang-tidy -----------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "../ClangTidy.h"
+#include "../ClangTidyModule.h"
+#include "../ClangTidyModuleRegistry.h"
+#include "BufferDerefCheck.h"
+#include "TypeMismatchCheck.h"
+
+namespace clang {
+namespace tidy {
+namespace mpi {
+
+class MPIModule : public ClangTidyModule {
+public:
+  void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
+    CheckFactories.registerCheck<BufferDerefCheck>("mpi-buffer-deref");
+    CheckFactories.registerCheck<TypeMismatchCheck>("mpi-type-mismatch");
+  }
+};
+
+} // namespace mpi
+
+// Register the MPITidyModule using this statically initialized variable.
+static ClangTidyModuleRegistry::Add<mpi::MPIModule>
+    X("mpi-module", "Adds MPI clang-tidy checks.");
+
+// This anchor is used to force the linker to link in the generated object file
+// and thus register the MPIModule.
+volatile int MPIModuleAnchorSource = 0;
+
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/mpi/TypeMismatchCheck.cpp b/clang-tidy/mpi/TypeMismatchCheck.cpp
new file mode 100644 (file)
index 0000000..a1f92b8
--- /dev/null
@@ -0,0 +1,335 @@
+//===--- TypeMismatchCheck.cpp - clang-tidy--------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "TypeMismatchCheck.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/StaticAnalyzer/Checkers/MPIFunctionClassifier.h"
+#include "clang/Tooling/FixIt.h"
+#include <map>
+#include <unordered_set>
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace mpi {
+
+/// Check if a BuiltinType::Kind matches the MPI datatype.
+///
+/// \param MultiMap datatype group
+/// \param Kind buffer type kind
+/// \param MPIDatatype name of the MPI datatype
+///
+/// \returns true if the pair matches
+static bool
+isMPITypeMatching(const std::multimap<BuiltinType::Kind, std::string> &MultiMap,
+                  const BuiltinType::Kind Kind,
+                  const std::string &MPIDatatype) {
+  auto ItPair = MultiMap.equal_range(Kind);
+  while (ItPair.first != ItPair.second) {
+    if (ItPair.first->second == MPIDatatype)
+      return true;
+    ++ItPair.first;
+  }
+  return false;
+}
+
+/// Check if the MPI datatype is a standard type.
+///
+/// \param MPIDatatype name of the MPI datatype
+///
+/// \returns true if the type is a standard type
+static bool isStandardMPIDatatype(const std::string &MPIDatatype) {
+  static std::unordered_set<std::string> AllTypes = {
+      "MPI_C_BOOL",
+      "MPI_CHAR",
+      "MPI_SIGNED_CHAR",
+      "MPI_UNSIGNED_CHAR",
+      "MPI_WCHAR",
+      "MPI_INT",
+      "MPI_LONG",
+      "MPI_SHORT",
+      "MPI_LONG_LONG",
+      "MPI_LONG_LONG_INT",
+      "MPI_UNSIGNED",
+      "MPI_UNSIGNED_SHORT",
+      "MPI_UNSIGNED_LONG",
+      "MPI_UNSIGNED_LONG_LONG",
+      "MPI_FLOAT",
+      "MPI_DOUBLE",
+      "MPI_LONG_DOUBLE",
+      "MPI_C_COMPLEX",
+      "MPI_C_FLOAT_COMPLEX",
+      "MPI_C_DOUBLE_COMPLEX",
+      "MPI_C_LONG_DOUBLE_COMPLEX",
+      "MPI_INT8_T",
+      "MPI_INT16_T",
+      "MPI_INT32_T",
+      "MPI_INT64_T",
+      "MPI_UINT8_T",
+      "MPI_UINT16_T",
+      "MPI_UINT32_T",
+      "MPI_UINT64_T",
+      "MPI_CXX_BOOL",
+      "MPI_CXX_FLOAT_COMPLEX",
+      "MPI_CXX_DOUBLE_COMPLEX",
+      "MPI_CXX_LONG_DOUBLE_COMPLEX"};
+
+  return AllTypes.find(MPIDatatype) != AllTypes.end();
+}
+
+/// Check if a BuiltinType matches the MPI datatype.
+///
+/// \param Builtin the builtin type
+/// \param BufferTypeName buffer type name, gets assigned
+/// \param MPIDatatype name of the MPI datatype
+/// \param LO language options
+///
+/// \returns true if the type matches
+static bool isBuiltinTypeMatching(const BuiltinType *Builtin,
+                                  std::string &BufferTypeName,
+                                  const std::string &MPIDatatype,
+                                  const LangOptions &LO) {
+  static std::multimap<BuiltinType::Kind, std::string> BuiltinMatches = {
+      // On some systems like PPC or ARM, 'char' is unsigned by default which is
+      // why distinct signedness for the buffer and MPI type is tolerated.
+      {BuiltinType::SChar, "MPI_CHAR"},
+      {BuiltinType::SChar, "MPI_SIGNED_CHAR"},
+      {BuiltinType::SChar, "MPI_UNSIGNED_CHAR"},
+      {BuiltinType::Char_S, "MPI_CHAR"},
+      {BuiltinType::Char_S, "MPI_SIGNED_CHAR"},
+      {BuiltinType::Char_S, "MPI_UNSIGNED_CHAR"},
+      {BuiltinType::UChar, "MPI_CHAR"},
+      {BuiltinType::UChar, "MPI_SIGNED_CHAR"},
+      {BuiltinType::UChar, "MPI_UNSIGNED_CHAR"},
+      {BuiltinType::Char_U, "MPI_CHAR"},
+      {BuiltinType::Char_U, "MPI_SIGNED_CHAR"},
+      {BuiltinType::Char_U, "MPI_UNSIGNED_CHAR"},
+      {BuiltinType::WChar_S, "MPI_WCHAR"},
+      {BuiltinType::WChar_U, "MPI_WCHAR"},
+      {BuiltinType::Bool, "MPI_C_BOOL"},
+      {BuiltinType::Bool, "MPI_CXX_BOOL"},
+      {BuiltinType::Short, "MPI_SHORT"},
+      {BuiltinType::Int, "MPI_INT"},
+      {BuiltinType::Long, "MPI_LONG"},
+      {BuiltinType::LongLong, "MPI_LONG_LONG"},
+      {BuiltinType::LongLong, "MPI_LONG_LONG_INT"},
+      {BuiltinType::UShort, "MPI_UNSIGNED_SHORT"},
+      {BuiltinType::UInt, "MPI_UNSIGNED"},
+      {BuiltinType::ULong, "MPI_UNSIGNED_LONG"},
+      {BuiltinType::ULongLong, "MPI_UNSIGNED_LONG_LONG"},
+      {BuiltinType::Float, "MPI_FLOAT"},
+      {BuiltinType::Double, "MPI_DOUBLE"},
+      {BuiltinType::LongDouble, "MPI_LONG_DOUBLE"}};
+
+  if (!isMPITypeMatching(BuiltinMatches, Builtin->getKind(), MPIDatatype)) {
+    BufferTypeName = Builtin->getName(LO);
+    return false;
+  }
+
+  return true;
+}
+
+/// Check if a complex float/double/long double buffer type matches
+/// the MPI datatype.
+///
+/// \param Complex buffer type
+/// \param BufferTypeName buffer type name, gets assigned
+/// \param MPIDatatype name of the MPI datatype
+/// \param LO language options
+///
+/// \returns true if the type matches or the buffer type is unknown
+static bool isCComplexTypeMatching(const ComplexType *const Complex,
+                                   std::string &BufferTypeName,
+                                   const std::string &MPIDatatype,
+                                   const LangOptions &LO) {
+  static std::multimap<BuiltinType::Kind, std::string> ComplexCMatches = {
+      {BuiltinType::Float, "MPI_C_COMPLEX"},
+      {BuiltinType::Float, "MPI_C_FLOAT_COMPLEX"},
+      {BuiltinType::Double, "MPI_C_DOUBLE_COMPLEX"},
+      {BuiltinType::LongDouble, "MPI_C_LONG_DOUBLE_COMPLEX"}};
+
+  const auto *Builtin =
+      Complex->getElementType().getTypePtr()->getAs<BuiltinType>();
+
+  if (Builtin &&
+      !isMPITypeMatching(ComplexCMatches, Builtin->getKind(), MPIDatatype)) {
+    BufferTypeName = (llvm::Twine(Builtin->getName(LO)) + " _Complex").str();
+    return false;
+  }
+  return true;
+}
+
+/// Check if a complex<float/double/long double> templated buffer type matches
+/// the MPI datatype.
+///
+/// \param Template buffer type
+/// \param BufferTypeName buffer type name, gets assigned
+/// \param MPIDatatype name of the MPI datatype
+/// \param LO language options
+///
+/// \returns true if the type matches or the buffer type is unknown
+static bool
+isCXXComplexTypeMatching(const TemplateSpecializationType *const Template,
+                         std::string &BufferTypeName,
+                         const std::string &MPIDatatype,
+                         const LangOptions &LO) {
+  static std::multimap<BuiltinType::Kind, std::string> ComplexCXXMatches = {
+      {BuiltinType::Float, "MPI_CXX_FLOAT_COMPLEX"},
+      {BuiltinType::Double, "MPI_CXX_DOUBLE_COMPLEX"},
+      {BuiltinType::LongDouble, "MPI_CXX_LONG_DOUBLE_COMPLEX"}};
+
+  if (Template->getAsCXXRecordDecl()->getName() != "complex")
+    return true;
+
+  const auto *Builtin =
+      Template->getArg(0).getAsType().getTypePtr()->getAs<BuiltinType>();
+
+  if (Builtin &&
+      !isMPITypeMatching(ComplexCXXMatches, Builtin->getKind(), MPIDatatype)) {
+    BufferTypeName =
+        (llvm::Twine("complex<") + Builtin->getName(LO) + ">").str();
+    return false;
+  }
+
+  return true;
+}
+
+/// Check if a fixed size width buffer type matches the MPI datatype.
+///
+/// \param Typedef buffer type
+/// \param BufferTypeName buffer type name, gets assigned
+/// \param MPIDatatype name of the MPI datatype
+///
+/// \returns true if the type matches or the buffer type is unknown
+static bool isTypedefTypeMatching(const TypedefType *const Typedef,
+                                  std::string &BufferTypeName,
+                                  const std::string &MPIDatatype) {
+  static llvm::StringMap<std::string> FixedWidthMatches = {
+      {"int8_t", "MPI_INT8_T"},     {"int16_t", "MPI_INT16_T"},
+      {"int32_t", "MPI_INT32_T"},   {"int64_t", "MPI_INT64_T"},
+      {"uint8_t", "MPI_UINT8_T"},   {"uint16_t", "MPI_UINT16_T"},
+      {"uint32_t", "MPI_UINT32_T"}, {"uint64_t", "MPI_UINT64_T"}};
+
+  const auto it = FixedWidthMatches.find(Typedef->getDecl()->getName());
+  // Check if the typedef is known and not matching the MPI datatype.
+  if (it != FixedWidthMatches.end() && it->getValue() != MPIDatatype) {
+    BufferTypeName = Typedef->getDecl()->getName();
+    return false;
+  }
+  return true;
+}
+
+/// Get the unqualified, dereferenced type of an argument.
+///
+/// \param CE call expression
+/// \param idx argument index
+///
+/// \returns type of the argument
+static const Type *argumentType(const CallExpr *const CE, const size_t idx) {
+  const QualType QT = CE->getArg(idx)->IgnoreImpCasts()->getType();
+  return QT.getTypePtr()->getPointeeOrArrayElementType();
+}
+
+void TypeMismatchCheck::registerMatchers(MatchFinder *Finder) {
+  Finder->addMatcher(callExpr().bind("CE"), this);
+}
+
+void TypeMismatchCheck::check(const MatchFinder::MatchResult &Result) {
+  static ento::mpi::MPIFunctionClassifier FuncClassifier(*Result.Context);
+  const auto *const CE = Result.Nodes.getNodeAs<CallExpr>("CE");
+  if (!CE->getDirectCallee())
+    return;
+
+  const IdentifierInfo *Identifier = CE->getDirectCallee()->getIdentifier();
+  if (!Identifier || !FuncClassifier.isMPIType(Identifier))
+    return;
+
+  // These containers are used, to capture buffer, MPI datatype pairs.
+  SmallVector<const Type *, 1> BufferTypes;
+  SmallVector<const Expr *, 1> BufferExprs;
+  SmallVector<StringRef, 1> MPIDatatypes;
+
+  // Adds a buffer, MPI datatype pair of an MPI call expression to the
+  // containers. For buffers, the type and expression is captured.
+  auto addPair = [&CE, &Result, &BufferTypes, &BufferExprs, &MPIDatatypes](
+      const size_t BufferIdx, const size_t DatatypeIdx) {
+    // Skip null pointer constants and in place 'operators'.
+    if (CE->getArg(BufferIdx)->isNullPointerConstant(
+            *Result.Context, Expr::NPC_ValueDependentIsNull) ||
+        tooling::fixit::getText(*CE->getArg(BufferIdx), *Result.Context) ==
+            "MPI_IN_PLACE")
+      return;
+
+    StringRef MPIDatatype =
+        tooling::fixit::getText(*CE->getArg(DatatypeIdx), *Result.Context);
+
+    const Type *ArgType = argumentType(CE, BufferIdx);
+    // Skip unknown MPI datatypes and void pointers.
+    if (!isStandardMPIDatatype(MPIDatatype) || ArgType->isVoidType())
+      return;
+
+    BufferTypes.push_back(ArgType);
+    BufferExprs.push_back(CE->getArg(BufferIdx));
+    MPIDatatypes.push_back(MPIDatatype);
+  };
+
+  // Collect all buffer, MPI datatype pairs for the inspected call expression.
+  if (FuncClassifier.isPointToPointType(Identifier)) {
+    addPair(0, 2);
+  } else if (FuncClassifier.isCollectiveType(Identifier)) {
+    if (FuncClassifier.isReduceType(Identifier)) {
+      addPair(0, 3);
+      addPair(1, 3);
+    } else if (FuncClassifier.isScatterType(Identifier) ||
+               FuncClassifier.isGatherType(Identifier) ||
+               FuncClassifier.isAlltoallType(Identifier)) {
+      addPair(0, 2);
+      addPair(3, 5);
+    } else if (FuncClassifier.isBcastType(Identifier)) {
+      addPair(0, 2);
+    }
+  }
+  checkArguments(BufferTypes, BufferExprs, MPIDatatypes, getLangOpts());
+}
+
+void TypeMismatchCheck::checkArguments(ArrayRef<const Type *> BufferTypes,
+                                       ArrayRef<const Expr *> BufferExprs,
+                                       ArrayRef<StringRef> MPIDatatypes,
+                                       const LangOptions &LO) {
+  std::string BufferTypeName;
+
+  for (size_t i = 0; i < MPIDatatypes.size(); ++i) {
+    const Type *const BT = BufferTypes[i];
+    bool Error = false;
+
+    if (const auto *Typedef = BT->getAs<TypedefType>()) {
+      Error = !isTypedefTypeMatching(Typedef, BufferTypeName, MPIDatatypes[i]);
+    } else if (const auto *Complex = BT->getAs<ComplexType>()) {
+      Error =
+          !isCComplexTypeMatching(Complex, BufferTypeName, MPIDatatypes[i], LO);
+    } else if (const auto *Template = BT->getAs<TemplateSpecializationType>()) {
+      Error = !isCXXComplexTypeMatching(Template, BufferTypeName,
+                                        MPIDatatypes[i], LO);
+    } else if (const auto *Builtin = BT->getAs<BuiltinType>()) {
+      Error =
+          !isBuiltinTypeMatching(Builtin, BufferTypeName, MPIDatatypes[i], LO);
+    }
+
+    if (Error) {
+      const auto Loc = BufferExprs[i]->getSourceRange().getBegin();
+      diag(Loc, "buffer type '%0' does not match the MPI datatype '%1'")
+          << BufferTypeName << MPIDatatypes[i];
+    }
+  }
+}
+
+} // namespace mpi
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/mpi/TypeMismatchCheck.h b/clang-tidy/mpi/TypeMismatchCheck.h
new file mode 100644 (file)
index 0000000..dd56c46
--- /dev/null
@@ -0,0 +1,51 @@
+//===--- TypeMismatchCheck.h - clang-tidy------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MPI_TYPE_MISMATCH_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MPI_TYPE_MISMATCH_H
+
+#include "../ClangTidy.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+namespace clang {
+namespace tidy {
+namespace mpi {
+
+/// This check verifies if buffer type and MPI (Message Passing Interface)
+/// datatype pairs match. All MPI datatypes defined by the MPI standard (3.1)
+/// are verified by this check. User defined typedefs, custom MPI datatypes and
+/// null pointer constants are skipped, in the course of verification.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/mpi-type-mismatch.html
+class TypeMismatchCheck : public ClangTidyCheck {
+public:
+  TypeMismatchCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+  /// Check if the buffer type MPI datatype pairs match.
+  ///
+  /// \param BufferTypes buffer types
+  /// \param BufferExprs buffer arguments as expressions
+  /// \param MPIDatatypes MPI datatype
+  /// \param LO language options
+  void checkArguments(ArrayRef<const Type *> BufferTypes,
+                      ArrayRef<const Expr *> BufferExprs,
+                      ArrayRef<StringRef> MPIDatatypes, const LangOptions &LO);
+};
+
+} // namespace mpi
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MPI_TYPE_MISMATCH_H
diff --git a/clang-tidy/performance/CMakeLists.txt b/clang-tidy/performance/CMakeLists.txt
new file mode 100644 (file)
index 0000000..102e76e
--- /dev/null
@@ -0,0 +1,21 @@
+set(LLVM_LINK_COMPONENTS support)
+
+add_clang_library(clangTidyPerformanceModule
+  FasterStringFindCheck.cpp
+  ForRangeCopyCheck.cpp
+  ImplicitCastInLoopCheck.cpp
+  InefficientStringConcatenationCheck.cpp
+  InefficientVectorOperationCheck.cpp
+  PerformanceTidyModule.cpp
+  TypePromotionInMathFnCheck.cpp
+  UnnecessaryCopyInitialization.cpp
+  UnnecessaryValueParamCheck.cpp
+
+  LINK_LIBS
+  clangAST
+  clangASTMatchers
+  clangBasic
+  clangLex
+  clangTidy
+  clangTidyUtils
+  )
diff --git a/clang-tidy/performance/FasterStringFindCheck.cpp b/clang-tidy/performance/FasterStringFindCheck.cpp
new file mode 100644 (file)
index 0000000..29dac1b
--- /dev/null
@@ -0,0 +1,102 @@
+//===--- FasterStringFindCheck.cpp - clang-tidy----------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "FasterStringFindCheck.h"
+#include "../utils/OptionsUtils.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace performance {
+
+namespace {
+
+llvm::Optional<std::string> MakeCharacterLiteral(const StringLiteral *Literal) {
+  std::string Result;
+  {
+    llvm::raw_string_ostream OS(Result);
+    Literal->outputString(OS);
+  }
+  // Now replace the " with '.
+  auto pos = Result.find_first_of('"');
+  if (pos == Result.npos)
+    return llvm::None;
+  Result[pos] = '\'';
+  pos = Result.find_last_of('"');
+  if (pos == Result.npos)
+    return llvm::None;
+  Result[pos] = '\'';
+  return Result;
+}
+
+AST_MATCHER_FUNCTION(ast_matchers::internal::Matcher<Expr>,
+                     hasSubstitutedType) {
+  return hasType(qualType(anyOf(substTemplateTypeParmType(),
+                                hasDescendant(substTemplateTypeParmType()))));
+}
+
+} // namespace
+
+FasterStringFindCheck::FasterStringFindCheck(StringRef Name,
+                                             ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      StringLikeClasses(utils::options::parseStringList(
+          Options.get("StringLikeClasses", "std::basic_string"))) {}
+
+void FasterStringFindCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "StringLikeClasses",
+                utils::options::serializeStringList(StringLikeClasses));
+}
+
+void FasterStringFindCheck::registerMatchers(MatchFinder *Finder) {
+  if (!getLangOpts().CPlusPlus)
+    return;
+
+  const auto SingleChar =
+      expr(ignoringParenCasts(stringLiteral(hasSize(1)).bind("literal")));
+  const auto StringFindFunctions =
+      hasAnyName("find", "rfind", "find_first_of", "find_first_not_of",
+                 "find_last_of", "find_last_not_of");
+
+  Finder->addMatcher(
+      cxxMemberCallExpr(
+          callee(functionDecl(StringFindFunctions).bind("func")),
+          anyOf(argumentCountIs(1), argumentCountIs(2)),
+          hasArgument(0, SingleChar),
+          on(expr(hasType(recordDecl(hasAnyName(SmallVector<StringRef, 4>(
+                      StringLikeClasses.begin(), StringLikeClasses.end())))),
+                  unless(hasSubstitutedType())))),
+      this);
+}
+
+void FasterStringFindCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *Literal = Result.Nodes.getNodeAs<StringLiteral>("literal");
+  const auto *FindFunc = Result.Nodes.getNodeAs<FunctionDecl>("func");
+
+  auto Replacement = MakeCharacterLiteral(Literal);
+  if (!Replacement)
+    return;
+
+  diag(Literal->getLocStart(), "%0 called with a string literal consisting of "
+                               "a single character; consider using the more "
+                               "effective overload accepting a character")
+      << FindFunc << FixItHint::CreateReplacement(
+                         CharSourceRange::getTokenRange(Literal->getLocStart(),
+                                                        Literal->getLocEnd()),
+                         *Replacement);
+}
+
+} // namespace performance
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/performance/FasterStringFindCheck.h b/clang-tidy/performance/FasterStringFindCheck.h
new file mode 100644 (file)
index 0000000..ff666f2
--- /dev/null
@@ -0,0 +1,43 @@
+//===--- FasterStringFindCheck.h - clang-tidy--------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_FASTER_STRING_FIND_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_FASTER_STRING_FIND_H
+
+#include "../ClangTidy.h"
+
+#include <string>
+#include <vector>
+
+namespace clang {
+namespace tidy {
+namespace performance {
+
+/// Optimize calls to std::string::find() and friends when the needle passed is
+/// a single character string literal.
+/// The character literal overload is more efficient.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/performance-faster-string-find.html
+class FasterStringFindCheck : public ClangTidyCheck {
+public:
+  FasterStringFindCheck(StringRef Name, ClangTidyContext *Context);
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+
+private:
+  const std::vector<std::string> StringLikeClasses;
+};
+
+} // namespace performance
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_FASTER_STRING_FIND_H
diff --git a/clang-tidy/performance/ForRangeCopyCheck.cpp b/clang-tidy/performance/ForRangeCopyCheck.cpp
new file mode 100644 (file)
index 0000000..0b9dd23
--- /dev/null
@@ -0,0 +1,95 @@
+//===--- ForRangeCopyCheck.cpp - clang-tidy--------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ForRangeCopyCheck.h"
+#include "../utils/DeclRefExprUtils.h"
+#include "../utils/FixItHintUtils.h"
+#include "../utils/TypeTraits.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace performance {
+
+ForRangeCopyCheck::ForRangeCopyCheck(StringRef Name, ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      WarnOnAllAutoCopies(Options.get("WarnOnAllAutoCopies", 0)) {}
+
+void ForRangeCopyCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "WarnOnAllAutoCopies", WarnOnAllAutoCopies);
+}
+
+void ForRangeCopyCheck::registerMatchers(MatchFinder *Finder) {
+  // Match loop variables that are not references or pointers or are already
+  // initialized through MaterializeTemporaryExpr which indicates a type
+  // conversion.
+  auto LoopVar = varDecl(
+      hasType(hasCanonicalType(unless(anyOf(referenceType(), pointerType())))),
+      unless(hasInitializer(expr(hasDescendant(materializeTemporaryExpr())))));
+  Finder->addMatcher(cxxForRangeStmt(hasLoopVariable(LoopVar.bind("loopVar")))
+                         .bind("forRange"),
+                     this);
+}
+
+void ForRangeCopyCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *Var = Result.Nodes.getNodeAs<VarDecl>("loopVar");
+  // Ignore code in macros since we can't place the fixes correctly.
+  if (Var->getLocStart().isMacroID())
+    return;
+  if (handleConstValueCopy(*Var, *Result.Context))
+    return;
+  const auto *ForRange = Result.Nodes.getNodeAs<CXXForRangeStmt>("forRange");
+  handleCopyIsOnlyConstReferenced(*Var, *ForRange, *Result.Context);
+}
+
+bool ForRangeCopyCheck::handleConstValueCopy(const VarDecl &LoopVar,
+                                             ASTContext &Context) {
+  if (WarnOnAllAutoCopies) {
+    // For aggressive check just test that loop variable has auto type.
+    if (!isa<AutoType>(LoopVar.getType()))
+      return false;
+  } else if (!LoopVar.getType().isConstQualified()) {
+    return false;
+  }
+  llvm::Optional<bool> Expensive =
+      utils::type_traits::isExpensiveToCopy(LoopVar.getType(), Context);
+  if (!Expensive || !*Expensive)
+    return false;
+  auto Diagnostic =
+      diag(LoopVar.getLocation(),
+           "the loop variable's type is not a reference type; this creates a "
+           "copy in each iteration; consider making this a reference")
+      << utils::fixit::changeVarDeclToReference(LoopVar, Context);
+  if (!LoopVar.getType().isConstQualified())
+    Diagnostic << utils::fixit::changeVarDeclToConst(LoopVar);
+  return true;
+}
+
+bool ForRangeCopyCheck::handleCopyIsOnlyConstReferenced(
+    const VarDecl &LoopVar, const CXXForRangeStmt &ForRange,
+    ASTContext &Context) {
+  llvm::Optional<bool> Expensive =
+      utils::type_traits::isExpensiveToCopy(LoopVar.getType(), Context);
+  if (LoopVar.getType().isConstQualified() || !Expensive || !*Expensive)
+    return false;
+  if (!utils::decl_ref_expr::isOnlyUsedAsConst(LoopVar, *ForRange.getBody(),
+                                               Context))
+    return false;
+  diag(LoopVar.getLocation(),
+       "loop variable is copied but only used as const reference; consider "
+       "making it a const reference")
+      << utils::fixit::changeVarDeclToConst(LoopVar)
+      << utils::fixit::changeVarDeclToReference(LoopVar, Context);
+  return true;
+}
+
+} // namespace performance
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/performance/ForRangeCopyCheck.h b/clang-tidy/performance/ForRangeCopyCheck.h
new file mode 100644 (file)
index 0000000..f88a126
--- /dev/null
@@ -0,0 +1,49 @@
+//===--- ForRangeCopyCheck.h - clang-tidy------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_FORRANGECOPYCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_FORRANGECOPYCHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace performance {
+
+/// A check that detects copied loop variables and suggests using const
+/// references.
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/performance-for-range-copy.html
+class ForRangeCopyCheck : public ClangTidyCheck {
+public:
+  ForRangeCopyCheck(StringRef Name, ClangTidyContext *Context);
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+  // Checks if the loop variable is a const value and expensive to copy. If so
+  // suggests it be converted to a const reference.
+  bool handleConstValueCopy(const VarDecl &LoopVar, ASTContext &Context);
+
+  // Checks if the loop variable is a non-const value and whether only
+  // const methods are invoked on it or whether it is only used as a const
+  // reference argument. If so it suggests it be made a const reference.
+  bool handleCopyIsOnlyConstReferenced(const VarDecl &LoopVar,
+                                       const CXXForRangeStmt &ForRange,
+                                       ASTContext &Context);
+
+  const bool WarnOnAllAutoCopies;
+};
+
+} // namespace performance
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_FORRANGECOPYCHECK_H
diff --git a/clang-tidy/performance/ImplicitCastInLoopCheck.cpp b/clang-tidy/performance/ImplicitCastInLoopCheck.cpp
new file mode 100644 (file)
index 0000000..7bd3f8a
--- /dev/null
@@ -0,0 +1,97 @@
+//===--- ImplicitCastInLoopCheck.cpp - clang-tidy--------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ImplicitCastInLoopCheck.h"
+
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace performance {
+
+namespace {
+// Checks if the stmt is a ImplicitCastExpr with a CastKind that is not a NoOp.
+// The subtelty is that in some cases (user defined conversions), we can
+// get to ImplicitCastExpr inside each other, with the outer one a NoOp. In this
+// case we skip the first cast expr.
+bool IsNonTrivialImplicitCast(const Stmt *ST) {
+  if (const auto *ICE = dyn_cast<ImplicitCastExpr>(ST)) {
+    return (ICE->getCastKind() != CK_NoOp) ||
+           IsNonTrivialImplicitCast(ICE->getSubExpr());
+  }
+  return false;
+}
+} // namespace
+
+void ImplicitCastInLoopCheck::registerMatchers(MatchFinder *Finder) {
+  // We look for const ref loop variables that (optionally inside an
+  // ExprWithCleanup) materialize a temporary, and contain a implicit cast. The
+  // check on the implicit cast is done in check() because we can't access
+  // implicit cast subnode via matchers: has() skips casts and materialize!
+  // We also bind on the call to operator* to get the proper type in the
+  // diagnostic message.
+  // Note that when the implicit cast is done through a user defined cast
+  // operator, the node is a CXXMemberCallExpr, not a CXXOperatorCallExpr, so
+  // it should not get caught by the cxxOperatorCallExpr() matcher.
+  Finder->addMatcher(
+      cxxForRangeStmt(hasLoopVariable(
+          varDecl(hasType(qualType(references(qualType(isConstQualified())))),
+                  hasInitializer(expr(hasDescendant(cxxOperatorCallExpr().bind(
+                                          "operator-call")))
+                                     .bind("init")))
+              .bind("faulty-var"))),
+      this);
+}
+
+void ImplicitCastInLoopCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *VD = Result.Nodes.getNodeAs<VarDecl>("faulty-var");
+  const auto *Init = Result.Nodes.getNodeAs<Expr>("init");
+  const auto *OperatorCall =
+      Result.Nodes.getNodeAs<CXXOperatorCallExpr>("operator-call");
+
+  if (const auto *Cleanup = dyn_cast<ExprWithCleanups>(Init))
+    Init = Cleanup->getSubExpr();
+
+  const auto *Materialized = dyn_cast<MaterializeTemporaryExpr>(Init);
+  if (!Materialized)
+    return;
+
+  // We ignore NoOp casts. Those are generated if the * operator on the
+  // iterator returns a value instead of a reference, and the loop variable
+  // is a reference. This situation is fine (it probably produces the same
+  // code at the end).
+  if (IsNonTrivialImplicitCast(Materialized->getTemporary()))
+    ReportAndFix(Result.Context, VD, OperatorCall);
+}
+
+void ImplicitCastInLoopCheck::ReportAndFix(
+    const ASTContext *Context, const VarDecl *VD,
+    const CXXOperatorCallExpr *OperatorCall) {
+  // We only match on const ref, so we should print a const ref version of the
+  // type.
+  QualType ConstType = OperatorCall->getType().withConst();
+  QualType ConstRefType = Context->getLValueReferenceType(ConstType);
+  const char Message[] =
+      "the type of the loop variable %0 is different from the one returned "
+      "by the iterator and generates an implicit cast; you can either "
+      "change the type to the correct one (%1 but 'const auto&' is always a "
+      "valid option) or remove the reference to make it explicit that you are "
+      "creating a new value";
+  diag(VD->getLocStart(), Message) << VD << ConstRefType;
+}
+
+} // namespace performance
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/performance/ImplicitCastInLoopCheck.h b/clang-tidy/performance/ImplicitCastInLoopCheck.h
new file mode 100644 (file)
index 0000000..3a9659d
--- /dev/null
@@ -0,0 +1,38 @@
+//===--- ImplicitCastInLoopCheck.h - clang-tidy------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_IMPLICIT_CAST_IN_LOOP_CHECK_H_
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_IMPLICIT_CAST_IN_LOOP_CHECK_H_
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace performance {
+
+// Checks that in a for range loop, if the provided type is a reference, then
+// the underlying type is the one returned by the iterator (i.e. that there
+// isn't any implicit conversion).
+class ImplicitCastInLoopCheck : public ClangTidyCheck {
+public:
+  ImplicitCastInLoopCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+  void ReportAndFix(const ASTContext *Context, const VarDecl *VD,
+                    const CXXOperatorCallExpr *OperatorCall);
+};
+
+} // namespace performance
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_IMPLICIT_CAST_IN_LOOP_CHECK_H_
diff --git a/clang-tidy/performance/InefficientStringConcatenationCheck.cpp b/clang-tidy/performance/InefficientStringConcatenationCheck.cpp
new file mode 100644 (file)
index 0000000..fb53167
--- /dev/null
@@ -0,0 +1,87 @@
+//===--- InefficientStringConcatenationCheck.cpp - clang-tidy--------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "InefficientStringConcatenationCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace performance {
+
+void InefficientStringConcatenationCheck::storeOptions(
+    ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "StrictMode", StrictMode);
+}
+
+InefficientStringConcatenationCheck::InefficientStringConcatenationCheck(
+    StringRef Name, ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      StrictMode(Options.getLocalOrGlobal("StrictMode", 0)) {}
+
+void InefficientStringConcatenationCheck::registerMatchers(
+    MatchFinder *Finder) {
+  if (!getLangOpts().CPlusPlus)
+    return;
+
+  const auto BasicStringType =
+      hasType(cxxRecordDecl(hasName("::std::basic_string")));
+
+  const auto BasicStringPlusOperator = cxxOperatorCallExpr(
+      hasOverloadedOperatorName("+"),
+      hasAnyArgument(ignoringImpCasts(declRefExpr(BasicStringType))));
+
+  const auto PlusOperator =
+      cxxOperatorCallExpr(
+          hasOverloadedOperatorName("+"),
+          hasAnyArgument(ignoringImpCasts(declRefExpr(BasicStringType))),
+          hasDescendant(BasicStringPlusOperator))
+          .bind("plusOperator");
+
+  const auto AssignOperator = cxxOperatorCallExpr(
+      hasOverloadedOperatorName("="),
+      hasArgument(0, declRefExpr(BasicStringType,
+                                 hasDeclaration(decl().bind("lhsStrT")))
+                         .bind("lhsStr")),
+      hasArgument(1, stmt(hasDescendant(declRefExpr(
+                         hasDeclaration(decl(equalsBoundNode("lhsStrT"))))))),
+      hasDescendant(BasicStringPlusOperator));
+
+  if (StrictMode) {
+    Finder->addMatcher(cxxOperatorCallExpr(anyOf(AssignOperator, PlusOperator)),
+                       this);
+  } else {
+    Finder->addMatcher(
+        cxxOperatorCallExpr(anyOf(AssignOperator, PlusOperator),
+                            hasAncestor(stmt(anyOf(cxxForRangeStmt(),
+                                                   whileStmt(), forStmt())))),
+        this);
+  }
+}
+
+void InefficientStringConcatenationCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  const auto *LhsStr = Result.Nodes.getNodeAs<DeclRefExpr>("lhsStr");
+  const auto *PlusOperator =
+      Result.Nodes.getNodeAs<CXXOperatorCallExpr>("plusOperator");
+  const auto DiagMsg =
+      "string concatenation results in allocation of unnecessary temporary "
+      "strings; consider using 'operator+=' or 'string::append()' instead";
+
+  if (LhsStr)
+    diag(LhsStr->getExprLoc(), DiagMsg);
+  else if (PlusOperator)
+    diag(PlusOperator->getExprLoc(), DiagMsg);
+}
+
+} // namespace performance
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/performance/InefficientStringConcatenationCheck.h b/clang-tidy/performance/InefficientStringConcatenationCheck.h
new file mode 100644 (file)
index 0000000..12a154c
--- /dev/null
@@ -0,0 +1,41 @@
+//===--- InefficientStringConcatenationCheck.h - clang-tidy-----------*- C++
+//-*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_INEFFICIENTSTRINGCONCATENATION_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_INEFFICIENTSTRINGCONCATENATION_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace performance {
+
+/// This check is to warn about the performance overhead arising from
+/// concatenating strings, using the operator+, instead of operator+=.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/performance-inefficient-string-concatenation.html
+class InefficientStringConcatenationCheck : public ClangTidyCheck {
+public:
+  InefficientStringConcatenationCheck(StringRef Name,
+                                      ClangTidyContext *Context);
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+
+private:
+  const bool StrictMode;
+};
+
+} // namespace performance
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_INEFFICIENTSTRINGCONCATENATION_H
diff --git a/clang-tidy/performance/InefficientVectorOperationCheck.cpp b/clang-tidy/performance/InefficientVectorOperationCheck.cpp
new file mode 100644 (file)
index 0000000..33c68f0
--- /dev/null
@@ -0,0 +1,214 @@
+//===--- InefficientVectorOperationCheck.cpp - clang-tidy------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "InefficientVectorOperationCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+#include "../utils/DeclRefExprUtils.h"
+#include "../utils/OptionsUtils.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace performance {
+
+namespace {
+
+// Matcher names. Given the code:
+//
+// \code
+// void f() {
+//   vector<T> v;
+//   for (int i = 0; i < 10 + 1; ++i) {
+//     v.push_back(i);
+//   }
+// }
+// \endcode
+//
+// The matcher names are bound to following parts of the AST:
+//   - LoopCounterName: The entire for loop (as ForStmt).
+//   - LoopParentName: The body of function f (as CompoundStmt).
+//   - VectorVarDeclName: 'v' in  (as VarDecl).
+//   - VectorVarDeclStmatName: The entire 'std::vector<T> v;' statement (as
+//     DeclStmt).
+//   - PushBackOrEmplaceBackCallName: 'v.push_back(i)' (as cxxMemberCallExpr).
+//   - LoopInitVarName: 'i' (as VarDecl).
+//   - LoopEndExpr: '10+1' (as Expr).
+static const char LoopCounterName[] = "for_loop_counter";
+static const char LoopParentName[] = "loop_parent";
+static const char VectorVarDeclName[] = "vector_var_decl";
+static const char VectorVarDeclStmtName[] = "vector_var_decl_stmt";
+static const char PushBackOrEmplaceBackCallName[] = "append_call";
+static const char LoopInitVarName[] = "loop_init_var";
+static const char LoopEndExprName[] = "loop_end_expr";
+
+static const char RangeLoopName[] = "for_range_loop";
+
+ast_matchers::internal::Matcher<Expr> supportedContainerTypesMatcher() {
+  return hasType(cxxRecordDecl(hasAnyName(
+      "::std::vector", "::std::set", "::std::unordered_set", "::std::map",
+      "::std::unordered_map", "::std::array", "::std::deque")));
+}
+
+} // namespace
+
+InefficientVectorOperationCheck::InefficientVectorOperationCheck(
+    StringRef Name, ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      VectorLikeClasses(utils::options::parseStringList(
+          Options.get("VectorLikeClasses", "::std::vector"))) {}
+
+void InefficientVectorOperationCheck::storeOptions(
+    ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "VectorLikeClasses",
+                utils::options::serializeStringList(VectorLikeClasses));
+}
+
+void InefficientVectorOperationCheck::registerMatchers(MatchFinder *Finder) {
+  const auto VectorDecl = cxxRecordDecl(hasAnyName(SmallVector<StringRef, 5>(
+      VectorLikeClasses.begin(), VectorLikeClasses.end())));
+  const auto VectorDefaultConstructorCall = cxxConstructExpr(
+      hasType(VectorDecl),
+      hasDeclaration(cxxConstructorDecl(isDefaultConstructor())));
+  const auto VectorVarDecl =
+      varDecl(hasInitializer(VectorDefaultConstructorCall))
+          .bind(VectorVarDeclName);
+  const auto VectorAppendCallExpr =
+      cxxMemberCallExpr(
+          callee(cxxMethodDecl(hasAnyName("push_back", "emplace_back"))),
+          on(hasType(VectorDecl)),
+          onImplicitObjectArgument(declRefExpr(to(VectorVarDecl))))
+          .bind(PushBackOrEmplaceBackCallName);
+  const auto VectorAppendCall = expr(ignoringImplicit(VectorAppendCallExpr));
+  const auto VectorVarDefStmt =
+      declStmt(hasSingleDecl(equalsBoundNode(VectorVarDeclName)))
+          .bind(VectorVarDeclStmtName);
+
+  const auto LoopVarInit =
+      declStmt(hasSingleDecl(varDecl(hasInitializer(integerLiteral(equals(0))))
+                                 .bind(LoopInitVarName)));
+  const auto RefersToLoopVar = ignoringParenImpCasts(
+      declRefExpr(to(varDecl(equalsBoundNode(LoopInitVarName)))));
+
+  // Matchers for the loop whose body has only 1 push_back/emplace_back calling
+  // statement.
+  const auto HasInterestingLoopBody =
+      hasBody(anyOf(compoundStmt(statementCountIs(1), has(VectorAppendCall)),
+                    VectorAppendCall));
+  const auto InInterestingCompoundStmt =
+      hasParent(compoundStmt(has(VectorVarDefStmt)).bind(LoopParentName));
+
+  // Match counter-based for loops:
+  //  for (int i = 0; i < n; ++i) { v.push_back(...); }
+  //
+  // FIXME: Support more types of counter-based loops like decrement loops.
+  Finder->addMatcher(
+      forStmt(
+          hasLoopInit(LoopVarInit),
+          hasCondition(binaryOperator(
+              hasOperatorName("<"), hasLHS(RefersToLoopVar),
+              hasRHS(expr(unless(hasDescendant(expr(RefersToLoopVar))))
+                         .bind(LoopEndExprName)))),
+          hasIncrement(unaryOperator(hasOperatorName("++"),
+                                     hasUnaryOperand(RefersToLoopVar))),
+          HasInterestingLoopBody, InInterestingCompoundStmt)
+          .bind(LoopCounterName),
+      this);
+
+  // Match for-range loops:
+  //   for (const auto& E : data) { v.push_back(...); }
+  //
+  // FIXME: Support more complex range-expressions.
+  Finder->addMatcher(
+      cxxForRangeStmt(
+          hasRangeInit(declRefExpr(supportedContainerTypesMatcher())),
+          HasInterestingLoopBody, InInterestingCompoundStmt)
+          .bind(RangeLoopName),
+      this);
+}
+
+void InefficientVectorOperationCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  auto* Context = Result.Context;
+  if (Context->getDiagnostics().hasUncompilableErrorOccurred())
+    return;
+
+  const SourceManager &SM = *Result.SourceManager;
+  const auto *VectorVarDecl =
+      Result.Nodes.getNodeAs<VarDecl>(VectorVarDeclName);
+  const auto *ForLoop = Result.Nodes.getNodeAs<ForStmt>(LoopCounterName);
+  const auto *RangeLoop =
+      Result.Nodes.getNodeAs<CXXForRangeStmt>(RangeLoopName);
+  const auto *VectorAppendCall =
+      Result.Nodes.getNodeAs<CXXMemberCallExpr>(PushBackOrEmplaceBackCallName);
+  const auto *LoopEndExpr = Result.Nodes.getNodeAs<Expr>(LoopEndExprName);
+  const auto *LoopParent = Result.Nodes.getNodeAs<CompoundStmt>(LoopParentName);
+
+  const Stmt *LoopStmt = ForLoop;
+  if (!LoopStmt)
+    LoopStmt = RangeLoop;
+
+  llvm::SmallPtrSet<const DeclRefExpr *, 16> AllVectorVarRefs =
+      utils::decl_ref_expr::allDeclRefExprs(*VectorVarDecl, *LoopParent,
+                                            *Context);
+  for (const auto *Ref : AllVectorVarRefs) {
+    // Skip cases where there are usages (defined as DeclRefExpr that refers to
+    // "v") of vector variable `v` before the for loop. We consider these usages
+    // are operations causing memory preallocation (e.g. "v.resize(n)",
+    // "v.reserve(n)").
+    //
+    // FIXME: make it more intelligent to identify the pre-allocating operations
+    // before the for loop.
+    if (SM.isBeforeInTranslationUnit(Ref->getLocation(),
+                                     LoopStmt->getLocStart())) {
+      return;
+    }
+  }
+
+  llvm::StringRef VectorVarName = Lexer::getSourceText(
+      CharSourceRange::getTokenRange(
+          VectorAppendCall->getImplicitObjectArgument()->getSourceRange()),
+      SM, Context->getLangOpts());
+
+  std::string ReserveStmt;
+  // Handle for-range loop cases.
+  if (RangeLoop) {
+    // Get the range-expression in a for-range statement represented as
+    // `for (range-declarator: range-expression)`.
+    StringRef RangeInitExpName = Lexer::getSourceText(
+        CharSourceRange::getTokenRange(
+            RangeLoop->getRangeInit()->getSourceRange()),
+        SM, Context->getLangOpts());
+
+    ReserveStmt =
+        (VectorVarName + ".reserve(" + RangeInitExpName + ".size()" + ");\n")
+            .str();
+  } else if (ForLoop) {
+    // Handle counter-based loop cases.
+    StringRef LoopEndSource = Lexer::getSourceText(
+        CharSourceRange::getTokenRange(LoopEndExpr->getSourceRange()), SM,
+        Context->getLangOpts());
+    ReserveStmt = (VectorVarName + ".reserve(" + LoopEndSource + ");\n").str();
+  }
+
+  auto Diag =
+      diag(VectorAppendCall->getLocStart(),
+           "%0 is called inside a loop; "
+           "consider pre-allocating the vector capacity before the loop")
+      << VectorAppendCall->getMethodDecl()->getDeclName();
+
+  if (!ReserveStmt.empty())
+    Diag << FixItHint::CreateInsertion(LoopStmt->getLocStart(), ReserveStmt);
+}
+
+} // namespace performance
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/performance/InefficientVectorOperationCheck.h b/clang-tidy/performance/InefficientVectorOperationCheck.h
new file mode 100644 (file)
index 0000000..1427ff1
--- /dev/null
@@ -0,0 +1,39 @@
+//===--- InefficientVectorOperationCheck.h - clang-tidy----------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_INEFFICIENT_VECTOR_OPERATION_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_INEFFICIENT_VECTOR_OPERATION_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace performance {
+
+/// Finds possible inefficient `std::vector` operations (e.g. `push_back`) in
+/// for loops that may cause unnecessary memory reallocations.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/performance-inefficient-vector-operation.html
+class InefficientVectorOperationCheck : public ClangTidyCheck {
+public:
+  InefficientVectorOperationCheck(StringRef Name, ClangTidyContext *Context);
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+
+private:
+  const std::vector<std::string> VectorLikeClasses;
+};
+
+} // namespace performance
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_INEFFICIENT_VECTOR_OPERATION_H
diff --git a/clang-tidy/performance/PerformanceTidyModule.cpp b/clang-tidy/performance/PerformanceTidyModule.cpp
new file mode 100644 (file)
index 0000000..072f817
--- /dev/null
@@ -0,0 +1,59 @@
+//===--- PeformanceTidyModule.cpp - clang-tidy ----------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "../ClangTidy.h"
+#include "../ClangTidyModule.h"
+#include "../ClangTidyModuleRegistry.h"
+#include "FasterStringFindCheck.h"
+#include "ForRangeCopyCheck.h"
+#include "ImplicitCastInLoopCheck.h"
+#include "InefficientStringConcatenationCheck.h"
+#include "InefficientVectorOperationCheck.h"
+#include "TypePromotionInMathFnCheck.h"
+#include "UnnecessaryCopyInitialization.h"
+#include "UnnecessaryValueParamCheck.h"
+
+namespace clang {
+namespace tidy {
+namespace performance {
+
+class PerformanceModule : public ClangTidyModule {
+public:
+  void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
+    CheckFactories.registerCheck<FasterStringFindCheck>(
+        "performance-faster-string-find");
+    CheckFactories.registerCheck<ForRangeCopyCheck>(
+        "performance-for-range-copy");
+    CheckFactories.registerCheck<ImplicitCastInLoopCheck>(
+        "performance-implicit-cast-in-loop");
+    CheckFactories.registerCheck<InefficientStringConcatenationCheck>(
+        "performance-inefficient-string-concatenation");
+    CheckFactories.registerCheck<InefficientVectorOperationCheck>(
+        "performance-inefficient-vector-operation");
+    CheckFactories.registerCheck<TypePromotionInMathFnCheck>(
+        "performance-type-promotion-in-math-fn");
+    CheckFactories.registerCheck<UnnecessaryCopyInitialization>(
+        "performance-unnecessary-copy-initialization");
+    CheckFactories.registerCheck<UnnecessaryValueParamCheck>(
+        "performance-unnecessary-value-param");
+  }
+};
+
+// Register the PerformanceModule using this statically initialized variable.
+static ClangTidyModuleRegistry::Add<PerformanceModule>
+    X("performance-module", "Adds performance checks.");
+
+} // namespace performance
+
+// This anchor is used to force the linker to link in the generated object file
+// and thus register the PerformanceModule.
+volatile int PerformanceModuleAnchorSource = 0;
+
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/performance/TypePromotionInMathFnCheck.cpp b/clang-tidy/performance/TypePromotionInMathFnCheck.cpp
new file mode 100644 (file)
index 0000000..82f9535
--- /dev/null
@@ -0,0 +1,205 @@
+//===--- TypePromotionInMathFnCheck.cpp - clang-tidy-----------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "TypePromotionInMathFnCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Lex/Preprocessor.h"
+#include "llvm/ADT/StringSet.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace performance {
+
+namespace {
+AST_MATCHER_P(Type, isBuiltinType, BuiltinType::Kind, Kind) {
+  if (const auto *BT = dyn_cast<BuiltinType>(&Node)) {
+    return BT->getKind() == Kind;
+  }
+  return false;
+}
+} // anonymous namespace
+
+TypePromotionInMathFnCheck::TypePromotionInMathFnCheck(
+    StringRef Name, ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      IncludeStyle(utils::IncludeSorter::parseIncludeStyle(
+          Options.get("IncludeStyle", "llvm"))) {}
+
+void TypePromotionInMathFnCheck::registerPPCallbacks(
+    CompilerInstance &Compiler) {
+  IncludeInserter = llvm::make_unique<utils::IncludeInserter>(
+      Compiler.getSourceManager(), Compiler.getLangOpts(), IncludeStyle);
+  Compiler.getPreprocessor().addPPCallbacks(
+      IncludeInserter->CreatePPCallbacks());
+}
+
+void TypePromotionInMathFnCheck::storeOptions(
+    ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "IncludeStyle",
+                utils::IncludeSorter::toString(IncludeStyle));
+}
+
+void TypePromotionInMathFnCheck::registerMatchers(MatchFinder *Finder) {
+  constexpr BuiltinType::Kind IntTy = BuiltinType::Int;
+  constexpr BuiltinType::Kind LongTy = BuiltinType::Long;
+  constexpr BuiltinType::Kind FloatTy = BuiltinType::Float;
+  constexpr BuiltinType::Kind DoubleTy = BuiltinType::Double;
+  constexpr BuiltinType::Kind LongDoubleTy = BuiltinType::LongDouble;
+
+  auto hasBuiltinTyParam = [](int Pos, BuiltinType::Kind Kind) {
+    return hasParameter(Pos, hasType(isBuiltinType(Kind)));
+  };
+  auto hasBuiltinTyArg = [](int Pos, BuiltinType::Kind Kind) {
+    return hasArgument(Pos, hasType(isBuiltinType(Kind)));
+  };
+
+  // Match calls to foo(double) with a float argument.
+  auto OneDoubleArgFns = hasAnyName(
+      "::acos", "::acosh", "::asin", "::asinh", "::atan", "::atanh", "::cbrt",
+      "::ceil", "::cos", "::cosh", "::erf", "::erfc", "::exp", "::exp2",
+      "::expm1", "::fabs", "::floor", "::ilogb", "::lgamma", "::llrint",
+      "::log", "::log10", "::log1p", "::log2", "::logb", "::lrint", "::modf",
+      "::nearbyint", "::rint", "::round", "::sin", "::sinh", "::sqrt", "::tan",
+      "::tanh", "::tgamma", "::trunc", "::llround", "::lround");
+  Finder->addMatcher(
+      callExpr(callee(functionDecl(OneDoubleArgFns, parameterCountIs(1),
+                                   hasBuiltinTyParam(0, DoubleTy))),
+               hasBuiltinTyArg(0, FloatTy))
+          .bind("call"),
+      this);
+
+  // Match calls to foo(double, double) where both args are floats.
+  auto TwoDoubleArgFns = hasAnyName("::atan2", "::copysign", "::fdim", "::fmax",
+                                    "::fmin", "::fmod", "::hypot", "::ldexp",
+                                    "::nextafter", "::pow", "::remainder");
+  Finder->addMatcher(
+      callExpr(callee(functionDecl(TwoDoubleArgFns, parameterCountIs(2),
+                                   hasBuiltinTyParam(0, DoubleTy),
+                                   hasBuiltinTyParam(1, DoubleTy))),
+               hasBuiltinTyArg(0, FloatTy), hasBuiltinTyArg(1, FloatTy))
+          .bind("call"),
+      this);
+
+  // Match calls to fma(double, double, double) where all args are floats.
+  Finder->addMatcher(
+      callExpr(callee(functionDecl(hasName("::fma"), parameterCountIs(3),
+                                   hasBuiltinTyParam(0, DoubleTy),
+                                   hasBuiltinTyParam(1, DoubleTy),
+                                   hasBuiltinTyParam(2, DoubleTy))),
+               hasBuiltinTyArg(0, FloatTy), hasBuiltinTyArg(1, FloatTy),
+               hasBuiltinTyArg(2, FloatTy))
+          .bind("call"),
+      this);
+
+  // Match calls to frexp(double, int*) where the first arg is a float.
+  Finder->addMatcher(
+      callExpr(callee(functionDecl(
+                   hasName("::frexp"), parameterCountIs(2),
+                   hasBuiltinTyParam(0, DoubleTy),
+                   hasParameter(1, parmVarDecl(hasType(pointerType(
+                                       pointee(isBuiltinType(IntTy)))))))),
+               hasBuiltinTyArg(0, FloatTy))
+          .bind("call"),
+      this);
+
+  // Match calls to nexttoward(double, long double) where the first arg is a
+  // float.
+  Finder->addMatcher(
+      callExpr(callee(functionDecl(hasName("::nexttoward"), parameterCountIs(2),
+                                   hasBuiltinTyParam(0, DoubleTy),
+                                   hasBuiltinTyParam(1, LongDoubleTy))),
+               hasBuiltinTyArg(0, FloatTy))
+          .bind("call"),
+      this);
+
+  // Match calls to remquo(double, double, int*) where the first two args are
+  // floats.
+  Finder->addMatcher(
+      callExpr(
+          callee(functionDecl(
+              hasName("::remquo"), parameterCountIs(3),
+              hasBuiltinTyParam(0, DoubleTy), hasBuiltinTyParam(1, DoubleTy),
+              hasParameter(2, parmVarDecl(hasType(pointerType(
+                                  pointee(isBuiltinType(IntTy)))))))),
+          hasBuiltinTyArg(0, FloatTy), hasBuiltinTyArg(1, FloatTy))
+          .bind("call"),
+      this);
+
+  // Match calls to scalbln(double, long) where the first arg is a float.
+  Finder->addMatcher(
+      callExpr(callee(functionDecl(hasName("::scalbln"), parameterCountIs(2),
+                                   hasBuiltinTyParam(0, DoubleTy),
+                                   hasBuiltinTyParam(1, LongTy))),
+               hasBuiltinTyArg(0, FloatTy))
+          .bind("call"),
+      this);
+
+  // Match calls to scalbn(double, int) where the first arg is a float.
+  Finder->addMatcher(
+      callExpr(callee(functionDecl(hasName("::scalbn"), parameterCountIs(2),
+                                   hasBuiltinTyParam(0, DoubleTy),
+                                   hasBuiltinTyParam(1, IntTy))),
+               hasBuiltinTyArg(0, FloatTy))
+          .bind("call"),
+      this);
+
+  // modf(double, double*) is omitted because the second parameter forces the
+  // type -- there's no conversion from float* to double*.
+}
+
+void TypePromotionInMathFnCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *Call = Result.Nodes.getNodeAs<CallExpr>("call");
+  assert(Call != nullptr);
+
+  StringRef OldFnName = Call->getDirectCallee()->getName();
+
+  // In C++ mode, we prefer std::foo to ::foof.  But some of these suggestions
+  // are only valid in C++11 and newer.
+  static llvm::StringSet<> Cpp11OnlyFns = {
+      "acosh",     "asinh",      "atanh",     "cbrt",   "copysign", "erf",
+      "erfc",      "exp2",       "expm1",     "fdim",   "fma",      "fmax",
+      "fmin",      "hypot",      "ilogb",     "lgamma", "llrint",   "llround",
+      "log1p",     "log2",       "logb",      "lrint",  "lround",   "nearbyint",
+      "nextafter", "nexttoward", "remainder", "remquo", "rint",     "round",
+      "scalbln",   "scalbn",     "tgamma",    "trunc"};
+  bool StdFnRequiresCpp11 = Cpp11OnlyFns.count(OldFnName);
+
+  std::string NewFnName;
+  bool FnInCmath = false;
+  if (getLangOpts().CPlusPlus &&
+      (!StdFnRequiresCpp11 || getLangOpts().CPlusPlus11)) {
+    NewFnName = ("std::" + OldFnName).str();
+    FnInCmath = true;
+  } else {
+    NewFnName = (OldFnName + "f").str();
+  }
+
+  auto Diag = diag(Call->getExprLoc(), "call to '%0' promotes float to double")
+              << OldFnName
+              << FixItHint::CreateReplacement(
+                     Call->getCallee()->getSourceRange(), NewFnName);
+
+  // Suggest including <cmath> if the function we're suggesting is declared in
+  // <cmath> and it's not already included.  We never have to suggest including
+  // <math.h>, because the functions we're suggesting moving away from are all
+  // declared in <math.h>.
+  if (FnInCmath)
+    if (auto IncludeFixit = IncludeInserter->CreateIncludeInsertion(
+            Result.Context->getSourceManager().getFileID(Call->getLocStart()),
+            "cmath", /*IsAngled=*/true))
+      Diag << *IncludeFixit;
+}
+
+} // namespace performance
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/performance/TypePromotionInMathFnCheck.h b/clang-tidy/performance/TypePromotionInMathFnCheck.h
new file mode 100644 (file)
index 0000000..2242957
--- /dev/null
@@ -0,0 +1,47 @@
+//===--- TypePromotionInMathFnCheck.h - clang-tidy---------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_TYPE_PROMOTION_IN_MATH_FN_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_TYPE_PROMOTION_IN_MATH_FN_H
+
+#include "../ClangTidy.h"
+#include "../utils/IncludeInserter.h"
+
+namespace clang {
+namespace tidy {
+namespace performance {
+
+/// Finds calls to C math library functions with implicit float to double
+/// promotions.
+///
+/// For example, warns on ::sin(0.f), because this funciton's parameter is a
+/// double.  You probably meant to call std::sin(0.f) (in C++), or sinf(0.f) (in
+/// C).
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/performance-type-promotion-in-math-fn.html
+class TypePromotionInMathFnCheck : public ClangTidyCheck {
+public:
+  TypePromotionInMathFnCheck(StringRef Name, ClangTidyContext *Context);
+
+  void registerPPCallbacks(CompilerInstance &Compiler) override;
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+  std::unique_ptr<utils::IncludeInserter> IncludeInserter;
+  const utils::IncludeSorter::IncludeStyle IncludeStyle;
+};
+
+} // namespace performance
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_TYPE_PROMOTION_IN_MATH_FN_H
diff --git a/clang-tidy/performance/UnnecessaryCopyInitialization.cpp b/clang-tidy/performance/UnnecessaryCopyInitialization.cpp
new file mode 100644 (file)
index 0000000..177497c
--- /dev/null
@@ -0,0 +1,149 @@
+//===--- UnnecessaryCopyInitialization.cpp - clang-tidy--------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "UnnecessaryCopyInitialization.h"
+
+#include "../utils/DeclRefExprUtils.h"
+#include "../utils/FixItHintUtils.h"
+#include "../utils/Matchers.h"
+
+namespace clang {
+namespace tidy {
+namespace performance {
+namespace {
+
+void recordFixes(const VarDecl &Var, ASTContext &Context,
+                 DiagnosticBuilder &Diagnostic) {
+  Diagnostic << utils::fixit::changeVarDeclToReference(Var, Context);
+  if (!Var.getType().isLocalConstQualified())
+    Diagnostic << utils::fixit::changeVarDeclToConst(Var);
+}
+
+} // namespace
+
+using namespace ::clang::ast_matchers;
+using utils::decl_ref_expr::isOnlyUsedAsConst;
+
+void UnnecessaryCopyInitialization::registerMatchers(MatchFinder *Finder) {
+  auto ConstReference = referenceType(pointee(qualType(isConstQualified())));
+  auto ConstOrConstReference =
+      allOf(anyOf(ConstReference, isConstQualified()),
+            unless(allOf(pointerType(), unless(pointerType(pointee(
+                                            qualType(isConstQualified())))))));
+
+  // Match method call expressions where the `this` argument is only used as
+  // const, this will be checked in `check()` part. This returned const
+  // reference is highly likely to outlive the local const reference of the
+  // variable being declared. The assumption is that the const reference being
+  // returned either points to a global static variable or to a member of the
+  // called object.
+  auto ConstRefReturningMethodCall =
+      cxxMemberCallExpr(callee(cxxMethodDecl(returns(ConstReference))),
+                        on(declRefExpr(to(varDecl().bind("objectArg")))));
+  auto ConstRefReturningFunctionCall =
+      callExpr(callee(functionDecl(returns(ConstReference))),
+               unless(callee(cxxMethodDecl())));
+
+  auto localVarCopiedFrom = [](const internal::Matcher<Expr> &CopyCtorArg) {
+    return compoundStmt(
+               forEachDescendant(
+                   declStmt(
+                       has(varDecl(hasLocalStorage(),
+                                   hasType(matchers::isExpensiveToCopy()),
+                                   unless(isImplicit()),
+                                   hasInitializer(
+                                       cxxConstructExpr(
+                                           hasDeclaration(cxxConstructorDecl(
+                                               isCopyConstructor())),
+                                           hasArgument(0, CopyCtorArg))
+                                           .bind("ctorCall")))
+                               .bind("newVarDecl")))
+                       .bind("declStmt")))
+        .bind("blockStmt");
+  };
+
+  Finder->addMatcher(localVarCopiedFrom(anyOf(ConstRefReturningFunctionCall,
+                                              ConstRefReturningMethodCall)),
+                     this);
+
+  Finder->addMatcher(localVarCopiedFrom(declRefExpr(
+                         to(varDecl(hasLocalStorage()).bind("oldVarDecl")))),
+                     this);
+}
+
+void UnnecessaryCopyInitialization::check(
+    const MatchFinder::MatchResult &Result) {
+  const auto *NewVar = Result.Nodes.getNodeAs<VarDecl>("newVarDecl");
+  const auto *OldVar = Result.Nodes.getNodeAs<VarDecl>("oldVarDecl");
+  const auto *ObjectArg = Result.Nodes.getNodeAs<VarDecl>("objectArg");
+  const auto *BlockStmt = Result.Nodes.getNodeAs<Stmt>("blockStmt");
+  const auto *CtorCall = Result.Nodes.getNodeAs<CXXConstructExpr>("ctorCall");
+  // Do not propose fixes if the DeclStmt has multiple VarDecls or in macros
+  // since we cannot place them correctly.
+  bool IssueFix =
+      Result.Nodes.getNodeAs<DeclStmt>("declStmt")->isSingleDecl() &&
+      !NewVar->getLocation().isMacroID();
+
+  // A constructor that looks like T(const T& t, bool arg = false) counts as a
+  // copy only when it is called with default arguments for the arguments after
+  // the first.
+  for (unsigned int i = 1; i < CtorCall->getNumArgs(); ++i)
+    if (!CtorCall->getArg(i)->isDefaultArgument())
+      return;
+
+  if (OldVar == nullptr) {
+    handleCopyFromMethodReturn(*NewVar, *BlockStmt, IssueFix, ObjectArg,
+                               *Result.Context);
+  } else {
+    handleCopyFromLocalVar(*NewVar, *OldVar, *BlockStmt, IssueFix,
+                           *Result.Context);
+  }
+}
+
+void UnnecessaryCopyInitialization::handleCopyFromMethodReturn(
+    const VarDecl &Var, const Stmt &BlockStmt, bool IssueFix,
+    const VarDecl *ObjectArg, ASTContext &Context) {
+  bool IsConstQualified = Var.getType().isConstQualified();
+  if (!IsConstQualified && !isOnlyUsedAsConst(Var, BlockStmt, Context))
+    return;
+  if (ObjectArg != nullptr &&
+      !isOnlyUsedAsConst(*ObjectArg, BlockStmt, Context))
+    return;
+
+  auto Diagnostic =
+      diag(Var.getLocation(),
+           IsConstQualified ? "the const qualified variable %0 is "
+                              "copy-constructed from a const reference; "
+                              "consider making it a const reference"
+                            : "the variable %0 is copy-constructed from a "
+                              "const reference but is only used as const "
+                              "reference; consider making it a const reference")
+      << &Var;
+  if (IssueFix)
+    recordFixes(Var, Context, Diagnostic);
+}
+
+void UnnecessaryCopyInitialization::handleCopyFromLocalVar(
+    const VarDecl &NewVar, const VarDecl &OldVar, const Stmt &BlockStmt,
+    bool IssueFix, ASTContext &Context) {
+  if (!isOnlyUsedAsConst(NewVar, BlockStmt, Context) ||
+      !isOnlyUsedAsConst(OldVar, BlockStmt, Context))
+    return;
+
+  auto Diagnostic = diag(NewVar.getLocation(),
+                         "local copy %0 of the variable %1 is never modified; "
+                         "consider avoiding the copy")
+                    << &NewVar << &OldVar;
+  if (IssueFix)
+    recordFixes(NewVar, Context, Diagnostic);
+}
+
+} // namespace performance
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/performance/UnnecessaryCopyInitialization.h b/clang-tidy/performance/UnnecessaryCopyInitialization.h
new file mode 100644 (file)
index 0000000..8ed4142
--- /dev/null
@@ -0,0 +1,47 @@
+//===--- UnnecessaryCopyInitialization.h - clang-tidy------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_UNNECESSARY_COPY_INITIALIZATION_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_UNNECESSARY_COPY_INITIALIZATION_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace performance {
+
+// The check detects local variable declarations that are copy initialized with
+// the const reference of a function call or the const reference of a method
+// call whose object is guaranteed to outlive the variable's scope and suggests
+// to use a const reference.
+//
+// The check currently only understands a subset of variables that are
+// guaranteed to outlive the const reference returned, namely: const variables,
+// const references, and const pointers to const.
+class UnnecessaryCopyInitialization : public ClangTidyCheck {
+public:
+  UnnecessaryCopyInitialization(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+  void handleCopyFromMethodReturn(const VarDecl &Var, const Stmt &BlockStmt,
+                                  bool IssueFix, const VarDecl *ObjectArg,
+                                  ASTContext &Context);
+  void handleCopyFromLocalVar(const VarDecl &NewVar, const VarDecl &OldVar,
+                              const Stmt &BlockStmt, bool IssueFix,
+                              ASTContext &Context);
+};
+
+} // namespace performance
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_UNNECESSARY_COPY_INITIALIZATION_H
diff --git a/clang-tidy/performance/UnnecessaryValueParamCheck.cpp b/clang-tidy/performance/UnnecessaryValueParamCheck.cpp
new file mode 100644 (file)
index 0000000..a34f707
--- /dev/null
@@ -0,0 +1,189 @@
+//===--- UnnecessaryValueParamCheck.cpp - clang-tidy-----------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "UnnecessaryValueParamCheck.h"
+
+#include "../utils/DeclRefExprUtils.h"
+#include "../utils/FixItHintUtils.h"
+#include "../utils/Matchers.h"
+#include "../utils/TypeTraits.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Lex/Preprocessor.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace performance {
+
+namespace {
+
+std::string paramNameOrIndex(StringRef Name, size_t Index) {
+  return (Name.empty() ? llvm::Twine('#') + llvm::Twine(Index + 1)
+                       : llvm::Twine('\'') + Name + llvm::Twine('\''))
+      .str();
+}
+
+template <typename S>
+bool isSubset(const S &SubsetCandidate, const S &SupersetCandidate) {
+  for (const auto &E : SubsetCandidate)
+    if (SupersetCandidate.count(E) == 0)
+      return false;
+  return true;
+}
+
+bool isReferencedOutsideOfCallExpr(const FunctionDecl &Function,
+                                   ASTContext &Context) {
+  auto Matches = match(declRefExpr(to(functionDecl(equalsNode(&Function))),
+                                   unless(hasAncestor(callExpr()))),
+                       Context);
+  return !Matches.empty();
+}
+
+bool hasLoopStmtAncestor(const DeclRefExpr &DeclRef, const Decl &Decl,
+                         ASTContext &Context) {
+  auto Matches =
+      match(decl(forEachDescendant(declRefExpr(
+                equalsNode(&DeclRef),
+                unless(hasAncestor(stmt(anyOf(forStmt(), cxxForRangeStmt(),
+                                              whileStmt(), doStmt()))))))),
+            Decl, Context);
+  return Matches.empty();
+}
+
+} // namespace
+
+UnnecessaryValueParamCheck::UnnecessaryValueParamCheck(
+    StringRef Name, ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      IncludeStyle(utils::IncludeSorter::parseIncludeStyle(
+          Options.get("IncludeStyle", "llvm"))) {}
+
+void UnnecessaryValueParamCheck::registerMatchers(MatchFinder *Finder) {
+  const auto ExpensiveValueParamDecl =
+      parmVarDecl(hasType(hasCanonicalType(allOf(
+                      unless(referenceType()), matchers::isExpensiveToCopy()))),
+                  decl().bind("param"));
+  Finder->addMatcher(
+      functionDecl(hasBody(stmt()), isDefinition(), unless(isImplicit()),
+                   unless(cxxMethodDecl(anyOf(isOverride(), isFinal()))),
+                   has(typeLoc(forEach(ExpensiveValueParamDecl))),
+                   unless(isInstantiated()), decl().bind("functionDecl")),
+      this);
+}
+
+void UnnecessaryValueParamCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *Param = Result.Nodes.getNodeAs<ParmVarDecl>("param");
+  const auto *Function = Result.Nodes.getNodeAs<FunctionDecl>("functionDecl");
+  const size_t Index = std::find(Function->parameters().begin(),
+                                 Function->parameters().end(), Param) -
+                       Function->parameters().begin();
+  bool IsConstQualified =
+      Param->getType().getCanonicalType().isConstQualified();
+
+  auto AllDeclRefExprs = utils::decl_ref_expr::allDeclRefExprs(
+      *Param, *Function, *Result.Context);
+  auto ConstDeclRefExprs = utils::decl_ref_expr::constReferenceDeclRefExprs(
+      *Param, *Function, *Result.Context);
+
+  // Do not trigger on non-const value parameters when they are not only used as
+  // const.
+  if (!isSubset(AllDeclRefExprs, ConstDeclRefExprs))
+    return;
+
+  // If the parameter is non-const, check if it has a move constructor and is
+  // only referenced once to copy-construct another object or whether it has a
+  // move assignment operator and is only referenced once when copy-assigned.
+  // In this case wrap DeclRefExpr with std::move() to avoid the unnecessary
+  // copy.
+  if (!IsConstQualified && AllDeclRefExprs.size() == 1) {
+    auto CanonicalType = Param->getType().getCanonicalType();
+    const auto &DeclRefExpr  = **AllDeclRefExprs.begin();
+
+    if (!hasLoopStmtAncestor(DeclRefExpr, *Function, *Result.Context) &&
+        ((utils::type_traits::hasNonTrivialMoveConstructor(CanonicalType) &&
+          utils::decl_ref_expr::isCopyConstructorArgument(
+              DeclRefExpr, *Function, *Result.Context)) ||
+         (utils::type_traits::hasNonTrivialMoveAssignment(CanonicalType) &&
+          utils::decl_ref_expr::isCopyAssignmentArgument(
+              DeclRefExpr, *Function, *Result.Context)))) {
+      handleMoveFix(*Param, DeclRefExpr, *Result.Context);
+      return;
+    }
+  }
+
+  auto Diag =
+      diag(Param->getLocation(),
+           IsConstQualified ? "the const qualified parameter %0 is "
+                              "copied for each invocation; consider "
+                              "making it a reference"
+                            : "the parameter %0 is copied for each "
+                              "invocation but only used as a const reference; "
+                              "consider making it a const reference")
+      << paramNameOrIndex(Param->getName(), Index);
+  // Do not propose fixes when:
+  // 1. the ParmVarDecl is in a macro, since we cannot place them correctly
+  // 2. the function is virtual as it might break overrides
+  // 3. the function is referenced outside of a call expression within the
+  //    compilation unit as the signature change could introduce build errors.
+  const auto *Method = llvm::dyn_cast<CXXMethodDecl>(Function);
+  if (Param->getLocStart().isMacroID() || (Method && Method->isVirtual()) ||
+      isReferencedOutsideOfCallExpr(*Function, *Result.Context))
+    return;
+  for (const auto *FunctionDecl = Function; FunctionDecl != nullptr;
+       FunctionDecl = FunctionDecl->getPreviousDecl()) {
+    const auto &CurrentParam = *FunctionDecl->getParamDecl(Index);
+    Diag << utils::fixit::changeVarDeclToReference(CurrentParam,
+                                                   *Result.Context);
+    // The parameter of each declaration needs to be checked individually as to
+    // whether it is const or not as constness can differ between definition and
+    // declaration.
+    if (!CurrentParam.getType().getCanonicalType().isConstQualified())
+      Diag << utils::fixit::changeVarDeclToConst(CurrentParam);
+  }
+}
+
+void UnnecessaryValueParamCheck::registerPPCallbacks(
+    CompilerInstance &Compiler) {
+  Inserter.reset(new utils::IncludeInserter(
+      Compiler.getSourceManager(), Compiler.getLangOpts(), IncludeStyle));
+  Compiler.getPreprocessor().addPPCallbacks(Inserter->CreatePPCallbacks());
+}
+
+void UnnecessaryValueParamCheck::storeOptions(
+    ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "IncludeStyle",
+                utils::IncludeSorter::toString(IncludeStyle));
+}
+
+void UnnecessaryValueParamCheck::handleMoveFix(const ParmVarDecl &Var,
+                                               const DeclRefExpr &CopyArgument,
+                                               const ASTContext &Context) {
+  auto Diag = diag(CopyArgument.getLocStart(),
+                   "parameter %0 is passed by value and only copied once; "
+                   "consider moving it to avoid unnecessary copies")
+              << &Var;
+  // Do not propose fixes in macros since we cannot place them correctly.
+  if (CopyArgument.getLocStart().isMacroID())
+    return;
+  const auto &SM = Context.getSourceManager();
+  auto EndLoc = Lexer::getLocForEndOfToken(CopyArgument.getLocation(), 0, SM,
+                                           Context.getLangOpts());
+  Diag << FixItHint::CreateInsertion(CopyArgument.getLocStart(), "std::move(")
+       << FixItHint::CreateInsertion(EndLoc, ")");
+  if (auto IncludeFixit = Inserter->CreateIncludeInsertion(
+          SM.getFileID(CopyArgument.getLocStart()), "utility",
+          /*IsAngled=*/true))
+    Diag << *IncludeFixit;
+}
+
+} // namespace performance
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/performance/UnnecessaryValueParamCheck.h b/clang-tidy/performance/UnnecessaryValueParamCheck.h
new file mode 100644 (file)
index 0000000..cbf0a3b
--- /dev/null
@@ -0,0 +1,45 @@
+//===--- UnnecessaryValueParamCheck.h - clang-tidy---------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_UNNECESSARY_VALUE_PARAM_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_UNNECESSARY_VALUE_PARAM_H
+
+#include "../ClangTidy.h"
+#include "../utils/IncludeInserter.h"
+
+namespace clang {
+namespace tidy {
+namespace performance {
+
+/// \brief A check that flags value parameters of expensive to copy types that
+/// can safely be converted to const references.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/performance-unnecessary-value-param.html
+class UnnecessaryValueParamCheck : public ClangTidyCheck {
+public:
+  UnnecessaryValueParamCheck(StringRef Name, ClangTidyContext *Context);
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+  void registerPPCallbacks(CompilerInstance &Compiler) override;
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+
+private:
+  void handleMoveFix(const ParmVarDecl &Var, const DeclRefExpr &CopyArgument,
+                     const ASTContext &Context);
+
+  std::unique_ptr<utils::IncludeInserter> Inserter;
+  const utils::IncludeSorter::IncludeStyle IncludeStyle;
+};
+
+} // namespace performance
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_UNNECESSARY_VALUE_PARAM_H
diff --git a/clang-tidy/plugin/CMakeLists.txt b/clang-tidy/plugin/CMakeLists.txt
new file mode 100644 (file)
index 0000000..5c88832
--- /dev/null
@@ -0,0 +1,23 @@
+add_clang_library(clangTidyPlugin
+  ClangTidyPlugin.cpp
+
+  LINK_LIBS
+  clangAST
+  clangASTMatchers
+  clangBasic
+  clangFrontend
+  clangSema
+  clangTidy
+  clangTidyAndroidModule
+  clangTidyBoostModule
+  clangTidyCERTModule
+  clangTidyCppCoreGuidelinesModule
+  clangTidyGoogleModule
+  clangTidyLLVMModule
+  clangTidyMiscModule
+  clangTidyModernizeModule
+  clangTidyMPIModule
+  clangTidyPerformanceModule
+  clangTidyReadabilityModule
+  clangTooling
+  )
diff --git a/clang-tidy/plugin/ClangTidyPlugin.cpp b/clang-tidy/plugin/ClangTidyPlugin.cpp
new file mode 100644 (file)
index 0000000..1e6346c
--- /dev/null
@@ -0,0 +1,127 @@
+//===- ClangTidyPlugin.cpp - clang-tidy as a clang plugin -----------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "../ClangTidy.h"
+#include "../ClangTidyModule.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendPluginRegistry.h"
+#include "clang/Frontend/MultiplexConsumer.h"
+
+namespace clang {
+namespace tidy {
+
+/// The core clang tidy plugin action. This just provides the AST consumer and
+/// command line flag parsing for using clang-tidy as a clang plugin.
+class ClangTidyPluginAction : public PluginASTAction {
+  /// Wrapper to grant the context the same lifetime as the action. We use
+  /// MultiplexConsumer to avoid writing out all the forwarding methods.
+  class WrapConsumer : public MultiplexConsumer {
+    std::unique_ptr<ClangTidyContext> Context;
+
+  public:
+    WrapConsumer(std::unique_ptr<ClangTidyContext> Context,
+                 std::vector<std::unique_ptr<ASTConsumer>> Consumer)
+        : MultiplexConsumer(std::move(Consumer)), Context(std::move(Context)) {}
+  };
+
+public:
+  std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler,
+                                                 StringRef File) override {
+    // Insert the current diagnostics engine.
+    Context->setDiagnosticsEngine(&Compiler.getDiagnostics());
+
+    // Create the AST consumer.
+    ClangTidyASTConsumerFactory Factory(*Context);
+    std::vector<std::unique_ptr<ASTConsumer>> Vec;
+    Vec.push_back(Factory.CreateASTConsumer(Compiler, File));
+
+    return llvm::make_unique<WrapConsumer>(std::move(Context), std::move(Vec));
+  }
+
+  bool ParseArgs(const CompilerInstance &,
+                 const std::vector<std::string> &Args) override {
+    ClangTidyGlobalOptions GlobalOptions;
+    ClangTidyOptions DefaultOptions;
+    ClangTidyOptions OverrideOptions;
+
+    // Parse the extra command line args.
+    // FIXME: This is very limited at the moment.
+    for (StringRef Arg : Args)
+      if (Arg.startswith("-checks="))
+        OverrideOptions.Checks = Arg.substr(strlen("-checks="));
+
+    auto Options = llvm::make_unique<FileOptionsProvider>(
+        GlobalOptions, DefaultOptions, OverrideOptions);
+    Context = llvm::make_unique<ClangTidyContext>(std::move(Options));
+    return true;
+  }
+
+private:
+  std::unique_ptr<ClangTidyContext> Context;
+};
+} // namespace tidy
+} // namespace clang
+
+// This anchor is used to force the linker to link in the generated object file
+// and thus register the clang-tidy plugin.
+volatile int ClangTidyPluginAnchorSource = 0;
+
+static clang::FrontendPluginRegistry::Add<clang::tidy::ClangTidyPluginAction>
+    X("clang-tidy", "clang-tidy");
+
+namespace clang {
+namespace tidy {
+
+// This anchor is used to force the linker to link the CERTModule.
+extern volatile int CERTModuleAnchorSource;
+static int LLVM_ATTRIBUTE_UNUSED CERTModuleAnchorDestination =
+    CERTModuleAnchorSource;
+
+// This anchor is used to force the linker to link the LLVMModule.
+extern volatile int LLVMModuleAnchorSource;
+static int LLVM_ATTRIBUTE_UNUSED LLVMModuleAnchorDestination =
+    LLVMModuleAnchorSource;
+
+// This anchor is used to force the linker to link the CppCoreGuidelinesModule.
+extern volatile int CppCoreGuidelinesModuleAnchorSource;
+static int LLVM_ATTRIBUTE_UNUSED CppCoreGuidelinesModuleAnchorDestination =
+    CppCoreGuidelinesModuleAnchorSource;
+
+// This anchor is used to force the linker to link the GoogleModule.
+extern volatile int GoogleModuleAnchorSource;
+static int LLVM_ATTRIBUTE_UNUSED GoogleModuleAnchorDestination =
+    GoogleModuleAnchorSource;
+
+// This anchor is used to force the linker to link the MiscModule.
+extern volatile int MiscModuleAnchorSource;
+static int LLVM_ATTRIBUTE_UNUSED MiscModuleAnchorDestination =
+    MiscModuleAnchorSource;
+
+// This anchor is used to force the linker to link the ModernizeModule.
+extern volatile int ModernizeModuleAnchorSource;
+static int LLVM_ATTRIBUTE_UNUSED ModernizeModuleAnchorDestination =
+    ModernizeModuleAnchorSource;
+
+// This anchor is used to force the linker to link the MPIModule.
+extern volatile int MPIModuleAnchorSource;
+static int LLVM_ATTRIBUTE_UNUSED MPIModuleAnchorDestination =
+  MPIModuleAnchorSource;
+
+// This anchor is used to force the linker to link the PerformanceModule.
+extern volatile int PerformanceModuleAnchorSource;
+static int LLVM_ATTRIBUTE_UNUSED PerformanceModuleAnchorDestination =
+    PerformanceModuleAnchorSource;
+
+// This anchor is used to force the linker to link the ReadabilityModule.
+extern volatile int ReadabilityModuleAnchorSource;
+static int LLVM_ATTRIBUTE_UNUSED ReadabilityModuleAnchorDestination =
+    ReadabilityModuleAnchorSource;
+
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/readability/AvoidConstParamsInDecls.cpp b/clang-tidy/readability/AvoidConstParamsInDecls.cpp
new file mode 100644 (file)
index 0000000..70329e8
--- /dev/null
@@ -0,0 +1,122 @@
+//===--- AvoidConstParamsInDecls.cpp - clang-tidy--------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "AvoidConstParamsInDecls.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Lex/Lexer.h"
+#include "llvm/ADT/Optional.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace readability {
+namespace {
+
+SourceRange getTypeRange(const ParmVarDecl &Param) {
+  if (Param.getIdentifier() != nullptr)
+    return SourceRange(Param.getLocStart(),
+                       Param.getLocEnd().getLocWithOffset(-1));
+  return Param.getSourceRange();
+}
+
+} // namespace
+
+void AvoidConstParamsInDecls::registerMatchers(MatchFinder *Finder) {
+  const auto ConstParamDecl =
+      parmVarDecl(hasType(qualType(isConstQualified()))).bind("param");
+  Finder->addMatcher(
+      functionDecl(unless(isDefinition()),
+                   // Lambdas are always their own definition, but they
+                   // generate a non-definition FunctionDecl too. Ignore those.
+                   // Class template instantiations have a non-definition
+                   // CXXMethodDecl for methods that aren't used in this
+                   // translation unit. Ignore those, as the template will have
+                   // already been checked.
+                   unless(cxxMethodDecl(ofClass(cxxRecordDecl(anyOf(
+                       isLambda(), ast_matchers::isTemplateInstantiation()))))),
+                   has(typeLoc(forEach(ConstParamDecl))))
+          .bind("func"),
+      this);
+}
+
+// Re-lex the tokens to get precise location of last 'const'
+static llvm::Optional<Token> ConstTok(CharSourceRange Range,
+                                      const MatchFinder::MatchResult &Result) {
+  const SourceManager &Sources = *Result.SourceManager;
+  std::pair<FileID, unsigned> LocInfo =
+      Sources.getDecomposedLoc(Range.getBegin());
+  StringRef File = Sources.getBufferData(LocInfo.first);
+  const char *TokenBegin = File.data() + LocInfo.second;
+  Lexer RawLexer(Sources.getLocForStartOfFile(LocInfo.first),
+                 Result.Context->getLangOpts(), File.begin(), TokenBegin,
+                 File.end());
+  Token Tok;
+  llvm::Optional<Token> ConstTok;
+  while (!RawLexer.LexFromRawLexer(Tok)) {
+    if (Sources.isBeforeInTranslationUnit(Range.getEnd(), Tok.getLocation()))
+      break;
+    if (Tok.is(tok::raw_identifier)) {
+      IdentifierInfo &Info = Result.Context->Idents.get(StringRef(
+          Sources.getCharacterData(Tok.getLocation()), Tok.getLength()));
+      Tok.setIdentifierInfo(&Info);
+      Tok.setKind(Info.getTokenID());
+    }
+    if (Tok.is(tok::kw_const))
+      ConstTok = Tok;
+  }
+  return ConstTok;
+}
+
+void AvoidConstParamsInDecls::check(const MatchFinder::MatchResult &Result) {
+  const auto *Func = Result.Nodes.getNodeAs<FunctionDecl>("func");
+  const auto *Param = Result.Nodes.getNodeAs<ParmVarDecl>("param");
+
+  if (!Param->getType().isLocalConstQualified())
+    return;
+
+  auto Diag = diag(Param->getLocStart(),
+                   "parameter %0 is const-qualified in the function "
+                   "declaration; const-qualification of parameters only has an "
+                   "effect in function definitions");
+  if (Param->getName().empty()) {
+    for (unsigned int i = 0; i < Func->getNumParams(); ++i) {
+      if (Param == Func->getParamDecl(i)) {
+        Diag << (i + 1);
+        break;
+      }
+    }
+  } else {
+    Diag << Param;
+  }
+
+  if (Param->getLocStart().isMacroID() != Param->getLocEnd().isMacroID()) {
+    // Do not offer a suggestion if the part of the variable declaration comes
+    // from a macro.
+    return;
+  }
+
+  CharSourceRange FileRange = Lexer::makeFileCharRange(
+      CharSourceRange::getTokenRange(getTypeRange(*Param)),
+      *Result.SourceManager, getLangOpts());
+
+  if (!FileRange.isValid())
+    return;
+
+  auto Tok = ConstTok(FileRange, Result);
+  if (!Tok)
+    return;
+  Diag << FixItHint::CreateRemoval(
+      CharSourceRange::getTokenRange(Tok->getLocation(), Tok->getLocation()));
+}
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/readability/AvoidConstParamsInDecls.h b/clang-tidy/readability/AvoidConstParamsInDecls.h
new file mode 100644 (file)
index 0000000..f2d91e8
--- /dev/null
@@ -0,0 +1,34 @@
+//===--- AvoidConstParamsInDecls.h - clang-tidy----------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_AVOID_CONST_PARAMS_IN_DECLS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_AVOID_CONST_PARAMS_IN_DECLS_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+// Detect function declarations that have const value parameters and discourage
+// them.
+class AvoidConstParamsInDecls : public ClangTidyCheck {
+public:
+  AvoidConstParamsInDecls(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_AVOID_CONST_PARAMS_IN_DECLS_H
diff --git a/clang-tidy/readability/BracesAroundStatementsCheck.cpp b/clang-tidy/readability/BracesAroundStatementsCheck.cpp
new file mode 100644 (file)
index 0000000..1bff668
--- /dev/null
@@ -0,0 +1,278 @@
+//===--- BracesAroundStatementsCheck.cpp - clang-tidy ---------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "BracesAroundStatementsCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace readability {
+namespace {
+
+tok::TokenKind getTokenKind(SourceLocation Loc, const SourceManager &SM,
+                            const ASTContext *Context) {
+  Token Tok;
+  SourceLocation Beginning =
+      Lexer::GetBeginningOfToken(Loc, SM, Context->getLangOpts());
+  const bool Invalid =
+      Lexer::getRawToken(Beginning, Tok, SM, Context->getLangOpts());
+  assert(!Invalid && "Expected a valid token.");
+
+  if (Invalid)
+    return tok::NUM_TOKENS;
+
+  return Tok.getKind();
+}
+
+SourceLocation forwardSkipWhitespaceAndComments(SourceLocation Loc,
+                                                const SourceManager &SM,
+                                                const ASTContext *Context) {
+  assert(Loc.isValid());
+  for (;;) {
+    while (isWhitespace(*SM.getCharacterData(Loc)))
+      Loc = Loc.getLocWithOffset(1);
+
+    tok::TokenKind TokKind = getTokenKind(Loc, SM, Context);
+    if (TokKind == tok::NUM_TOKENS || TokKind != tok::comment)
+      return Loc;
+
+    // Fast-forward current token.
+    Loc = Lexer::getLocForEndOfToken(Loc, 0, SM, Context->getLangOpts());
+  }
+}
+
+SourceLocation findEndLocation(SourceLocation LastTokenLoc,
+                               const SourceManager &SM,
+                               const ASTContext *Context) {
+  SourceLocation Loc =
+      Lexer::GetBeginningOfToken(LastTokenLoc, SM, Context->getLangOpts());
+  // Loc points to the beginning of the last (non-comment non-ws) token
+  // before end or ';'.
+  assert(Loc.isValid());
+  bool SkipEndWhitespaceAndComments = true;
+  tok::TokenKind TokKind = getTokenKind(Loc, SM, Context);
+  if (TokKind == tok::NUM_TOKENS || TokKind == tok::semi ||
+      TokKind == tok::r_brace) {
+    // If we are at ";" or "}", we found the last token. We could use as well
+    // `if (isa<NullStmt>(S))`, but it wouldn't work for nested statements.
+    SkipEndWhitespaceAndComments = false;
+  }
+
+  Loc = Lexer::getLocForEndOfToken(Loc, 0, SM, Context->getLangOpts());
+  // Loc points past the last token before end or after ';'.
+  if (SkipEndWhitespaceAndComments) {
+    Loc = forwardSkipWhitespaceAndComments(Loc, SM, Context);
+    tok::TokenKind TokKind = getTokenKind(Loc, SM, Context);
+    if (TokKind == tok::semi)
+      Loc = Lexer::getLocForEndOfToken(Loc, 0, SM, Context->getLangOpts());
+  }
+
+  for (;;) {
+    assert(Loc.isValid());
+    while (isHorizontalWhitespace(*SM.getCharacterData(Loc))) {
+      Loc = Loc.getLocWithOffset(1);
+    }
+
+    if (isVerticalWhitespace(*SM.getCharacterData(Loc))) {
+      // EOL, insert brace before.
+      break;
+    }
+    tok::TokenKind TokKind = getTokenKind(Loc, SM, Context);
+    if (TokKind != tok::comment) {
+      // Non-comment token, insert brace before.
+      break;
+    }
+
+    SourceLocation TokEndLoc =
+        Lexer::getLocForEndOfToken(Loc, 0, SM, Context->getLangOpts());
+    SourceRange TokRange(Loc, TokEndLoc);
+    StringRef Comment = Lexer::getSourceText(
+        CharSourceRange::getTokenRange(TokRange), SM, Context->getLangOpts());
+    if (Comment.startswith("/*") && Comment.find('\n') != StringRef::npos) {
+      // Multi-line block comment, insert brace before.
+      break;
+    }
+    // else: Trailing comment, insert brace after the newline.
+
+    // Fast-forward current token.
+    Loc = TokEndLoc;
+  }
+  return Loc;
+}
+
+} // namespace
+
+BracesAroundStatementsCheck::BracesAroundStatementsCheck(
+    StringRef Name, ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      // Always add braces by default.
+      ShortStatementLines(Options.get("ShortStatementLines", 0U)) {}
+
+void BracesAroundStatementsCheck::storeOptions(
+    ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "ShortStatementLines", ShortStatementLines);
+}
+
+void BracesAroundStatementsCheck::registerMatchers(MatchFinder *Finder) {
+  Finder->addMatcher(ifStmt().bind("if"), this);
+  Finder->addMatcher(whileStmt().bind("while"), this);
+  Finder->addMatcher(doStmt().bind("do"), this);
+  Finder->addMatcher(forStmt().bind("for"), this);
+  Finder->addMatcher(cxxForRangeStmt().bind("for-range"), this);
+}
+
+void BracesAroundStatementsCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  const SourceManager &SM = *Result.SourceManager;
+  const ASTContext *Context = Result.Context;
+
+  // Get location of closing parenthesis or 'do' to insert opening brace.
+  if (auto S = Result.Nodes.getNodeAs<ForStmt>("for")) {
+    checkStmt(Result, S->getBody(), S->getRParenLoc());
+  } else if (auto S = Result.Nodes.getNodeAs<CXXForRangeStmt>("for-range")) {
+    checkStmt(Result, S->getBody(), S->getRParenLoc());
+  } else if (auto S = Result.Nodes.getNodeAs<DoStmt>("do")) {
+    checkStmt(Result, S->getBody(), S->getDoLoc(), S->getWhileLoc());
+  } else if (auto S = Result.Nodes.getNodeAs<WhileStmt>("while")) {
+    SourceLocation StartLoc = findRParenLoc(S, SM, Context);
+    if (StartLoc.isInvalid())
+      return;
+    checkStmt(Result, S->getBody(), StartLoc);
+  } else if (auto S = Result.Nodes.getNodeAs<IfStmt>("if")) {
+    SourceLocation StartLoc = findRParenLoc(S, SM, Context);
+    if (StartLoc.isInvalid())
+      return;
+    if (ForceBracesStmts.erase(S))
+      ForceBracesStmts.insert(S->getThen());
+    bool BracedIf = checkStmt(Result, S->getThen(), StartLoc, S->getElseLoc());
+    const Stmt *Else = S->getElse();
+    if (Else && BracedIf)
+      ForceBracesStmts.insert(Else);
+    if (Else && !isa<IfStmt>(Else)) {
+      // Omit 'else if' statements here, they will be handled directly.
+      checkStmt(Result, Else, S->getElseLoc());
+    }
+  } else {
+    llvm_unreachable("Invalid match");
+  }
+}
+
+/// Find location of right parenthesis closing condition.
+template <typename IfOrWhileStmt>
+SourceLocation
+BracesAroundStatementsCheck::findRParenLoc(const IfOrWhileStmt *S,
+                                           const SourceManager &SM,
+                                           const ASTContext *Context) {
+  // Skip macros.
+  if (S->getLocStart().isMacroID())
+    return SourceLocation();
+
+  SourceLocation CondEndLoc = S->getCond()->getLocEnd();
+  if (const DeclStmt *CondVar = S->getConditionVariableDeclStmt())
+    CondEndLoc = CondVar->getLocEnd();
+
+  if (!CondEndLoc.isValid()) {
+    return SourceLocation();
+  }
+
+  SourceLocation PastCondEndLoc =
+      Lexer::getLocForEndOfToken(CondEndLoc, 0, SM, Context->getLangOpts());
+  if (PastCondEndLoc.isInvalid())
+    return SourceLocation();
+  SourceLocation RParenLoc =
+      forwardSkipWhitespaceAndComments(PastCondEndLoc, SM, Context);
+  if (RParenLoc.isInvalid())
+    return SourceLocation();
+  tok::TokenKind TokKind = getTokenKind(RParenLoc, SM, Context);
+  if (TokKind != tok::r_paren)
+    return SourceLocation();
+  return RParenLoc;
+}
+
+/// Determine if the statement needs braces around it, and add them if it does.
+/// Returns true if braces where added.
+bool BracesAroundStatementsCheck::checkStmt(
+    const MatchFinder::MatchResult &Result, const Stmt *S,
+    SourceLocation InitialLoc, SourceLocation EndLocHint) {
+  // 1) If there's a corresponding "else" or "while", the check inserts "} "
+  // right before that token.
+  // 2) If there's a multi-line block comment starting on the same line after
+  // the location we're inserting the closing brace at, or there's a non-comment
+  // token, the check inserts "\n}" right before that token.
+  // 3) Otherwise the check finds the end of line (possibly after some block or
+  // line comments) and inserts "\n}" right before that EOL.
+  if (!S || isa<CompoundStmt>(S)) {
+    // Already inside braces.
+    return false;
+  }
+
+  if (!InitialLoc.isValid())
+    return false;
+  const SourceManager &SM = *Result.SourceManager;
+  const ASTContext *Context = Result.Context;
+
+  // Treat macros.
+  CharSourceRange FileRange = Lexer::makeFileCharRange(
+      CharSourceRange::getTokenRange(S->getSourceRange()), SM,
+      Context->getLangOpts());
+  if (FileRange.isInvalid())
+    return false;
+
+  // Convert InitialLoc to file location, if it's on the same macro expansion
+  // level as the start of the statement. We also need file locations for
+  // Lexer::getLocForEndOfToken working properly.
+  InitialLoc = Lexer::makeFileCharRange(
+                   CharSourceRange::getCharRange(InitialLoc, S->getLocStart()),
+                   SM, Context->getLangOpts())
+                   .getBegin();
+  if (InitialLoc.isInvalid())
+    return false;
+  SourceLocation StartLoc =
+      Lexer::getLocForEndOfToken(InitialLoc, 0, SM, Context->getLangOpts());
+
+  // StartLoc points at the location of the opening brace to be inserted.
+  SourceLocation EndLoc;
+  std::string ClosingInsertion;
+  if (EndLocHint.isValid()) {
+    EndLoc = EndLocHint;
+    ClosingInsertion = "} ";
+  } else {
+    const auto FREnd = FileRange.getEnd().getLocWithOffset(-1);
+    EndLoc = findEndLocation(FREnd, SM, Context);
+    ClosingInsertion = "\n}";
+  }
+
+  assert(StartLoc.isValid());
+  assert(EndLoc.isValid());
+  // Don't require braces for statements spanning less than certain number of
+  // lines.
+  if (ShortStatementLines && !ForceBracesStmts.erase(S)) {
+    unsigned StartLine = SM.getSpellingLineNumber(StartLoc);
+    unsigned EndLine = SM.getSpellingLineNumber(EndLoc);
+    if (EndLine - StartLine < ShortStatementLines)
+      return false;
+  }
+
+  auto Diag = diag(StartLoc, "statement should be inside braces");
+  Diag << FixItHint::CreateInsertion(StartLoc, " {")
+       << FixItHint::CreateInsertion(EndLoc, ClosingInsertion);
+  return true;
+}
+
+void BracesAroundStatementsCheck::onEndOfTranslationUnit() {
+  ForceBracesStmts.clear();
+}
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/readability/BracesAroundStatementsCheck.h b/clang-tidy/readability/BracesAroundStatementsCheck.h
new file mode 100644 (file)
index 0000000..919ca46
--- /dev/null
@@ -0,0 +1,69 @@
+//===--- BracesAroundStatementsCheck.h - clang-tidy -------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_BRACESAROUNDSTATEMENTSCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_BRACESAROUNDSTATEMENTSCHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+/// Checks that bodies of `if` statements and loops (`for`, `range-for`,
+/// `do-while`, and `while`) are inside braces
+///
+/// Before:
+///
+/// \code
+///   if (condition)
+///     statement;
+/// \endcode
+///
+/// After:
+///
+/// \code
+///   if (condition) {
+///     statement;
+///   }
+/// \endcode
+///
+/// Additionally, one can define an option `ShortStatementLines` defining the
+/// minimal number of lines that the statement should have in order to trigger
+/// this check.
+///
+/// The number of lines is counted from the end of condition or initial keyword
+/// (`do`/`else`) until the last line of the inner statement.  Default value 0
+/// means that braces will be added to all statements (not having them already).
+class BracesAroundStatementsCheck : public ClangTidyCheck {
+public:
+  BracesAroundStatementsCheck(StringRef Name, ClangTidyContext *Context);
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+  void onEndOfTranslationUnit() override;
+
+private:
+  bool checkStmt(const ast_matchers::MatchFinder::MatchResult &Result,
+                 const Stmt *S, SourceLocation StartLoc,
+                 SourceLocation EndLocHint = SourceLocation());
+  template <typename IfOrWhileStmt>
+  SourceLocation findRParenLoc(const IfOrWhileStmt *S, const SourceManager &SM,
+                               const ASTContext *Context);
+
+private:
+  std::set<const Stmt *> ForceBracesStmts;
+  const unsigned ShortStatementLines;
+};
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_BRACESAROUNDSTATEMENTSCHECK_H
diff --git a/clang-tidy/readability/CMakeLists.txt b/clang-tidy/readability/CMakeLists.txt
new file mode 100644 (file)
index 0000000..bed7287
--- /dev/null
@@ -0,0 +1,39 @@
+set(LLVM_LINK_COMPONENTS support)
+
+add_clang_library(clangTidyReadabilityModule
+  AvoidConstParamsInDecls.cpp
+  BracesAroundStatementsCheck.cpp
+  ContainerSizeEmptyCheck.cpp
+  DeleteNullPointerCheck.cpp
+  DeletedDefaultCheck.cpp
+  ElseAfterReturnCheck.cpp
+  FunctionSizeCheck.cpp
+  IdentifierNamingCheck.cpp
+  ImplicitBoolCastCheck.cpp
+  InconsistentDeclarationParameterNameCheck.cpp
+  MisleadingIndentationCheck.cpp
+  MisplacedArrayIndexCheck.cpp
+  NamedParameterCheck.cpp
+  NamespaceCommentCheck.cpp
+  NonConstParameterCheck.cpp
+  ReadabilityTidyModule.cpp
+  RedundantControlFlowCheck.cpp
+  RedundantDeclarationCheck.cpp
+  RedundantFunctionPtrDereferenceCheck.cpp
+  RedundantMemberInitCheck.cpp
+  RedundantStringCStrCheck.cpp
+  RedundantSmartptrGetCheck.cpp
+  RedundantStringInitCheck.cpp
+  SimplifyBooleanExprCheck.cpp
+  StaticDefinitionInAnonymousNamespaceCheck.cpp
+  UniqueptrDeleteReleaseCheck.cpp
+
+  LINK_LIBS
+  clangAST
+  clangASTMatchers
+  clangBasic
+  clangLex
+  clangTidy
+  clangTidyUtils
+  clangTooling
+  )
diff --git a/clang-tidy/readability/ContainerSizeEmptyCheck.cpp b/clang-tidy/readability/ContainerSizeEmptyCheck.cpp
new file mode 100644 (file)
index 0000000..24680eb
--- /dev/null
@@ -0,0 +1,223 @@
+//===--- ContainerSizeEmptyCheck.cpp - clang-tidy -------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+#include "ContainerSizeEmptyCheck.h"
+#include "../utils/ASTUtils.h"
+#include "../utils/Matchers.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Lex/Lexer.h"
+#include "llvm/ADT/StringRef.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+using utils::IsBinaryOrTernary;
+
+ContainerSizeEmptyCheck::ContainerSizeEmptyCheck(StringRef Name,
+                                                 ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context) {}
+
+void ContainerSizeEmptyCheck::registerMatchers(MatchFinder *Finder) {
+  // Only register the matchers for C++; the functionality currently does not
+  // provide any benefit to other languages, despite being benign.
+  if (!getLangOpts().CPlusPlus)
+    return;
+
+  const auto ValidContainer = cxxRecordDecl(isSameOrDerivedFrom(
+      namedDecl(
+          has(cxxMethodDecl(
+                  isConst(), parameterCountIs(0), isPublic(), hasName("size"),
+                  returns(qualType(isInteger(), unless(booleanType()))))
+                  .bind("size")),
+          has(cxxMethodDecl(isConst(), parameterCountIs(0), isPublic(),
+                            hasName("empty"), returns(booleanType()))
+                  .bind("empty")))
+          .bind("container")));
+
+  const auto WrongUse = anyOf(
+      hasParent(binaryOperator(
+                    matchers::isComparisonOperator(),
+                    hasEitherOperand(ignoringImpCasts(anyOf(
+                        integerLiteral(equals(1)), integerLiteral(equals(0))))))
+                    .bind("SizeBinaryOp")),
+      hasParent(implicitCastExpr(
+          hasImplicitDestinationType(booleanType()),
+          anyOf(
+              hasParent(unaryOperator(hasOperatorName("!")).bind("NegOnSize")),
+              anything()))),
+      hasParent(explicitCastExpr(hasDestinationType(booleanType()))));
+
+  Finder->addMatcher(
+      cxxMemberCallExpr(on(expr(anyOf(hasType(ValidContainer),
+                                      hasType(pointsTo(ValidContainer)),
+                                      hasType(references(ValidContainer))))),
+                        callee(cxxMethodDecl(hasName("size"))), WrongUse,
+                        unless(hasAncestor(cxxMethodDecl(
+                            ofClass(equalsBoundNode("container"))))))
+          .bind("SizeCallExpr"),
+      this);
+
+  // Empty constructor matcher.
+  const auto DefaultConstructor = cxxConstructExpr(
+          hasDeclaration(cxxConstructorDecl(isDefaultConstructor())));
+  // Comparison to empty string or empty constructor.
+  const auto WrongComparend = anyOf(
+      ignoringImpCasts(stringLiteral(hasSize(0))),
+      ignoringImpCasts(cxxBindTemporaryExpr(has(DefaultConstructor))),
+      ignoringImplicit(DefaultConstructor),
+      cxxConstructExpr(
+          hasDeclaration(cxxConstructorDecl(isCopyConstructor())),
+          has(expr(ignoringImpCasts(DefaultConstructor)))),
+      cxxConstructExpr(
+          hasDeclaration(cxxConstructorDecl(isMoveConstructor())),
+          has(expr(ignoringImpCasts(DefaultConstructor)))));
+  // Match the object being compared.
+  const auto STLArg =
+      anyOf(unaryOperator(
+                hasOperatorName("*"),
+                hasUnaryOperand(
+                    expr(hasType(pointsTo(ValidContainer))).bind("Pointee"))),
+            expr(hasType(ValidContainer)).bind("STLObject"));
+  Finder->addMatcher(
+      cxxOperatorCallExpr(
+          anyOf(hasOverloadedOperatorName("=="),
+                hasOverloadedOperatorName("!=")),
+          anyOf(allOf(hasArgument(0, WrongComparend), hasArgument(1, STLArg)),
+                allOf(hasArgument(0, STLArg), hasArgument(1, WrongComparend))),
+          unless(hasAncestor(
+              cxxMethodDecl(ofClass(equalsBoundNode("container"))))))
+          .bind("BinCmp"),
+      this);
+}
+
+void ContainerSizeEmptyCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *MemberCall =
+      Result.Nodes.getNodeAs<CXXMemberCallExpr>("SizeCallExpr");
+  const auto *BinCmp = Result.Nodes.getNodeAs<CXXOperatorCallExpr>("BinCmp");
+  const auto *BinaryOp = Result.Nodes.getNodeAs<BinaryOperator>("SizeBinaryOp");
+  const auto *Pointee = Result.Nodes.getNodeAs<Expr>("Pointee");
+  const auto *E =
+      MemberCall
+          ? MemberCall->getImplicitObjectArgument()
+          : (Pointee ? Pointee : Result.Nodes.getNodeAs<Expr>("STLObject"));
+  FixItHint Hint;
+  std::string ReplacementText =
+      Lexer::getSourceText(CharSourceRange::getTokenRange(E->getSourceRange()),
+                           *Result.SourceManager, getLangOpts());
+  if (BinCmp && IsBinaryOrTernary(E)) {
+    // Not just a DeclRefExpr, so parenthesize to be on the safe side.
+    ReplacementText = "(" + ReplacementText + ")";
+  }
+  if (E->getType()->isPointerType())
+    ReplacementText += "->empty()";
+  else
+    ReplacementText += ".empty()";
+
+  if (BinCmp) {
+    if (BinCmp->getOperator() == OO_ExclaimEqual) {
+      ReplacementText = "!" + ReplacementText;
+    }
+    Hint =
+        FixItHint::CreateReplacement(BinCmp->getSourceRange(), ReplacementText);
+  } else if (BinaryOp) {  // Determine the correct transformation.
+    bool Negation = false;
+    const bool ContainerIsLHS =
+        !llvm::isa<IntegerLiteral>(BinaryOp->getLHS()->IgnoreImpCasts());
+    const auto OpCode = BinaryOp->getOpcode();
+    uint64_t Value = 0;
+    if (ContainerIsLHS) {
+      if (const auto *Literal = llvm::dyn_cast<IntegerLiteral>(
+              BinaryOp->getRHS()->IgnoreImpCasts()))
+        Value = Literal->getValue().getLimitedValue();
+      else
+        return;
+    } else {
+      Value =
+          llvm::dyn_cast<IntegerLiteral>(BinaryOp->getLHS()->IgnoreImpCasts())
+              ->getValue()
+              .getLimitedValue();
+    }
+
+    // Constant that is not handled.
+    if (Value > 1)
+      return;
+
+    if (Value == 1 && (OpCode == BinaryOperatorKind::BO_EQ ||
+                       OpCode == BinaryOperatorKind::BO_NE))
+      return;
+
+    // Always true, no warnings for that.
+    if ((OpCode == BinaryOperatorKind::BO_GE && Value == 0 && ContainerIsLHS) ||
+        (OpCode == BinaryOperatorKind::BO_LE && Value == 0 && !ContainerIsLHS))
+      return;
+
+    // Do not warn for size > 1, 1 < size, size <= 1, 1 >= size.
+    if (Value == 1) {
+      if ((OpCode == BinaryOperatorKind::BO_GT && ContainerIsLHS) ||
+          (OpCode == BinaryOperatorKind::BO_LT && !ContainerIsLHS))
+        return;
+      if ((OpCode == BinaryOperatorKind::BO_LE && ContainerIsLHS) ||
+          (OpCode == BinaryOperatorKind::BO_GE && !ContainerIsLHS))
+        return;
+    }
+
+    if (OpCode == BinaryOperatorKind::BO_NE && Value == 0)
+      Negation = true;
+    if ((OpCode == BinaryOperatorKind::BO_GT ||
+         OpCode == BinaryOperatorKind::BO_GE) &&
+        ContainerIsLHS)
+      Negation = true;
+    if ((OpCode == BinaryOperatorKind::BO_LT ||
+         OpCode == BinaryOperatorKind::BO_LE) &&
+        !ContainerIsLHS)
+      Negation = true;
+
+    if (Negation)
+      ReplacementText = "!" + ReplacementText;
+    Hint = FixItHint::CreateReplacement(BinaryOp->getSourceRange(),
+                                        ReplacementText);
+
+  } else {
+    // If there is a conversion above the size call to bool, it is safe to just
+    // replace size with empty.
+    if (const auto *UnaryOp =
+            Result.Nodes.getNodeAs<UnaryOperator>("NegOnSize"))
+      Hint = FixItHint::CreateReplacement(UnaryOp->getSourceRange(),
+                                          ReplacementText);
+    else
+      Hint = FixItHint::CreateReplacement(MemberCall->getSourceRange(),
+                                          "!" + ReplacementText);
+  }
+
+  if (MemberCall) {
+    diag(MemberCall->getLocStart(),
+         "the 'empty' method should be used to check "
+         "for emptiness instead of 'size'")
+        << Hint;
+  } else {
+    diag(BinCmp->getLocStart(),
+         "the 'empty' method should be used to check "
+         "for emptiness instead of comparing to an empty object")
+        << Hint;
+  }
+
+  const auto *Container = Result.Nodes.getNodeAs<NamedDecl>("container");
+  const auto *Empty = Result.Nodes.getNodeAs<FunctionDecl>("empty");
+
+  diag(Empty->getLocation(), "method %0::empty() defined here",
+       DiagnosticIDs::Note)
+      << Container;
+}
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/readability/ContainerSizeEmptyCheck.h b/clang-tidy/readability/ContainerSizeEmptyCheck.h
new file mode 100644 (file)
index 0000000..bde83f8
--- /dev/null
@@ -0,0 +1,40 @@
+//===--- ContainerSizeEmptyCheck.h - clang-tidy -----------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_CONTAINERSIZEEMPTYCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_CONTAINERSIZEEMPTYCHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+/// Checks whether a call to the `size()` method can be replaced with a call to
+/// `empty()`.
+///
+/// The emptiness of a container should be checked using the `empty()` method
+/// instead of the `size()` method. It is not guaranteed that `size()` is a
+/// constant-time function, and it is generally more efficient and also shows
+/// clearer intent to use `empty()`. Furthermore some containers may implement
+/// the `empty()` method but not implement the `size()` method. Using `empty()`
+/// whenever possible makes it easier to switch to another container in the
+/// future.
+class ContainerSizeEmptyCheck : public ClangTidyCheck {
+public:
+  ContainerSizeEmptyCheck(StringRef Name, ClangTidyContext *Context);
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_CONTAINERSIZEEMPTYCHECK_H
diff --git a/clang-tidy/readability/DeleteNullPointerCheck.cpp b/clang-tidy/readability/DeleteNullPointerCheck.cpp
new file mode 100644 (file)
index 0000000..766dfda
--- /dev/null
@@ -0,0 +1,79 @@
+//===--- DeleteNullPointerCheck.cpp - clang-tidy---------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "DeleteNullPointerCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+void DeleteNullPointerCheck::registerMatchers(MatchFinder *Finder) {
+  const auto DeleteExpr =
+      cxxDeleteExpr(has(castExpr(has(declRefExpr(
+                        to(decl(equalsBoundNode("deletedPointer"))))))))
+          .bind("deleteExpr");
+
+  const auto DeleteMemberExpr =
+      cxxDeleteExpr(has(castExpr(has(memberExpr(hasDeclaration(
+                        fieldDecl(equalsBoundNode("deletedMemberPointer"))))))))
+          .bind("deleteMemberExpr");
+
+  const auto PointerExpr = ignoringImpCasts(anyOf(
+      declRefExpr(to(decl().bind("deletedPointer"))),
+      memberExpr(hasDeclaration(fieldDecl().bind("deletedMemberPointer")))));
+
+  const auto PointerCondition = castExpr(hasCastKind(CK_PointerToBoolean),
+                                         hasSourceExpression(PointerExpr));
+  const auto BinaryPointerCheckCondition =
+      binaryOperator(hasEitherOperand(castExpr(hasCastKind(CK_NullToPointer))),
+                     hasEitherOperand(PointerExpr));
+
+  Finder->addMatcher(
+      ifStmt(hasCondition(anyOf(PointerCondition, BinaryPointerCheckCondition)),
+             hasThen(anyOf(
+                 DeleteExpr, DeleteMemberExpr,
+                 compoundStmt(anyOf(has(DeleteExpr), has(DeleteMemberExpr)),
+                              statementCountIs(1))
+                     .bind("compound"))))
+          .bind("ifWithDelete"),
+      this);
+}
+
+void DeleteNullPointerCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *IfWithDelete = Result.Nodes.getNodeAs<IfStmt>("ifWithDelete");
+  const auto *Compound = Result.Nodes.getNodeAs<CompoundStmt>("compound");
+
+  auto Diag = diag(
+      IfWithDelete->getLocStart(),
+      "'if' statement is unnecessary; deleting null pointer has no effect");
+  if (IfWithDelete->getElse())
+    return;
+  // FIXME: generate fixit for this case.
+
+  Diag << FixItHint::CreateRemoval(CharSourceRange::getTokenRange(
+      IfWithDelete->getLocStart(),
+      Lexer::getLocForEndOfToken(IfWithDelete->getCond()->getLocEnd(), 0,
+                                 *Result.SourceManager,
+                                 Result.Context->getLangOpts())));
+  if (Compound) {
+    Diag << FixItHint::CreateRemoval(
+        CharSourceRange::getTokenRange(Compound->getLBracLoc()));
+    Diag << FixItHint::CreateRemoval(
+        CharSourceRange::getTokenRange(Compound->getRBracLoc()));
+  }
+}
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/readability/DeleteNullPointerCheck.h b/clang-tidy/readability/DeleteNullPointerCheck.h
new file mode 100644 (file)
index 0000000..501f6f7
--- /dev/null
@@ -0,0 +1,36 @@
+//===--- DeleteNullPointerCheck.h - clang-tidy-------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_DELETE_NULL_POINTER_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_DELETE_NULL_POINTER_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+/// Check whether the 'if' statement is unnecessary before calling 'delete' on a
+/// pointer.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability-delete-null-pointer.html
+class DeleteNullPointerCheck : public ClangTidyCheck {
+public:
+  DeleteNullPointerCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_DELETE_NULL_POINTER_H
diff --git a/clang-tidy/readability/DeletedDefaultCheck.cpp b/clang-tidy/readability/DeletedDefaultCheck.cpp
new file mode 100644 (file)
index 0000000..a197e8d
--- /dev/null
@@ -0,0 +1,69 @@
+//===--- DeletedDefaultCheck.cpp - clang-tidy------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "DeletedDefaultCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+void DeletedDefaultCheck::registerMatchers(MatchFinder *Finder) {
+  // We match constructors/assignment operators that are:
+  //   - explicitly marked '= default'
+  //   - actually deleted
+  //   - not in template instantiation.
+  // We bind the declaration to "method-decl" and also to "constructor" when
+  // it is a constructor.
+
+  Finder->addMatcher(
+      cxxMethodDecl(anyOf(cxxConstructorDecl().bind("constructor"),
+                          isCopyAssignmentOperator(),
+                          isMoveAssignmentOperator()),
+                    isDefaulted(), unless(isImplicit()), isDeleted(),
+                    unless(isInstantiated()))
+          .bind("method-decl"),
+      this);
+}
+
+void DeletedDefaultCheck::check(const MatchFinder::MatchResult &Result) {
+  const StringRef Message = "%0 is explicitly defaulted but implicitly "
+                            "deleted, probably because %1; definition can "
+                            "either be removed or explicitly deleted";
+  if (const auto *Constructor =
+          Result.Nodes.getNodeAs<CXXConstructorDecl>("constructor")) {
+    auto Diag = diag(Constructor->getLocStart(), Message);
+    if (Constructor->isDefaultConstructor()) {
+      Diag << "default constructor"
+           << "a non-static data member or a base class is lacking a default "
+              "constructor";
+    } else if (Constructor->isCopyConstructor()) {
+      Diag << "copy constructor"
+           << "a non-static data member or a base class is not copyable";
+    } else if (Constructor->isMoveConstructor()) {
+      Diag << "move constructor"
+           << "a non-static data member or a base class is neither copyable "
+              "nor movable";
+    }
+  } else if (const auto *Assignment =
+                 Result.Nodes.getNodeAs<CXXMethodDecl>("method-decl")) {
+    diag(Assignment->getLocStart(), Message)
+        << (Assignment->isCopyAssignmentOperator() ? "copy assignment operator"
+                                                   : "move assignment operator")
+        << "a base class or a non-static data member is not assignable, e.g. "
+           "because the latter is marked 'const'";
+  }
+}
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/readability/DeletedDefaultCheck.h b/clang-tidy/readability/DeletedDefaultCheck.h
new file mode 100644 (file)
index 0000000..0608b07
--- /dev/null
@@ -0,0 +1,36 @@
+//===--- DeletedDefaultCheck.h - clang-tidy----------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_DELETED_DEFAULT_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_DELETED_DEFAULT_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+/// Checks when a constructor or an assignment operator is marked as '= default'
+/// but is actually deleted by the compiler.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability-deleted-default.html
+class DeletedDefaultCheck : public ClangTidyCheck {
+public:
+  DeletedDefaultCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_DELETED_DEFAULT_H
diff --git a/clang-tidy/readability/ElseAfterReturnCheck.cpp b/clang-tidy/readability/ElseAfterReturnCheck.cpp
new file mode 100644 (file)
index 0000000..6c67663
--- /dev/null
@@ -0,0 +1,56 @@
+//===--- ElseAfterReturnCheck.cpp - clang-tidy-----------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ElseAfterReturnCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Tooling/FixIt.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+void ElseAfterReturnCheck::registerMatchers(MatchFinder *Finder) {
+  const auto ControlFlowInterruptorMatcher =
+      stmt(anyOf(returnStmt().bind("return"), continueStmt().bind("continue"),
+                 breakStmt().bind("break"), cxxThrowExpr().bind("throw")));
+  Finder->addMatcher(
+      compoundStmt(forEach(
+          ifStmt(hasThen(stmt(
+                     anyOf(ControlFlowInterruptorMatcher,
+                           compoundStmt(has(ControlFlowInterruptorMatcher))))),
+                 hasElse(stmt().bind("else")))
+              .bind("if"))),
+      this);
+}
+
+void ElseAfterReturnCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *If = Result.Nodes.getNodeAs<IfStmt>("if");
+  SourceLocation ElseLoc = If->getElseLoc();
+  std::string ControlFlowInterruptor;
+  for (const auto *BindingName : {"return", "continue", "break", "throw"})
+    if (Result.Nodes.getNodeAs<Stmt>(BindingName))
+      ControlFlowInterruptor = BindingName;
+
+  DiagnosticBuilder Diag = diag(ElseLoc, "do not use 'else' after '%0'")
+                           << ControlFlowInterruptor;
+  Diag << tooling::fixit::createRemoval(ElseLoc);
+
+  // FIXME: Removing the braces isn't always safe. Do a more careful analysis.
+  // FIXME: Change clang-format to correctly un-indent the code.
+  if (const auto *CS = Result.Nodes.getNodeAs<CompoundStmt>("else"))
+    Diag << tooling::fixit::createRemoval(CS->getLBracLoc())
+         << tooling::fixit::createRemoval(CS->getRBracLoc());
+}
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/readability/ElseAfterReturnCheck.h b/clang-tidy/readability/ElseAfterReturnCheck.h
new file mode 100644 (file)
index 0000000..8479ab5
--- /dev/null
@@ -0,0 +1,34 @@
+//===--- ElseAfterReturnCheck.h - clang-tidy---------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_ELSEAFTERRETURNCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_ELSEAFTERRETURNCHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+/// Flags the usages of `else` after `return`.
+///
+/// http://llvm.org/docs/CodingStandards.html#don-t-use-else-after-a-return
+class ElseAfterReturnCheck : public ClangTidyCheck {
+public:
+  ElseAfterReturnCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_ELSEAFTERRETURNCHECK_H
diff --git a/clang-tidy/readability/FunctionSizeCheck.cpp b/clang-tidy/readability/FunctionSizeCheck.cpp
new file mode 100644 (file)
index 0000000..ea5f750
--- /dev/null
@@ -0,0 +1,172 @@
+//===--- FunctionSize.cpp - clang-tidy ------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "FunctionSizeCheck.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+class FunctionASTVisitor : public RecursiveASTVisitor<FunctionASTVisitor> {
+  using Base = RecursiveASTVisitor<FunctionASTVisitor>;
+
+public:
+  bool TraverseStmt(Stmt *Node) {
+    if (!Node)
+      return Base::TraverseStmt(Node);
+
+    if (TrackedParent.back() && !isa<CompoundStmt>(Node))
+      ++Info.Statements;
+
+    switch (Node->getStmtClass()) {
+    case Stmt::IfStmtClass:
+    case Stmt::WhileStmtClass:
+    case Stmt::DoStmtClass:
+    case Stmt::CXXForRangeStmtClass:
+    case Stmt::ForStmtClass:
+    case Stmt::SwitchStmtClass:
+      ++Info.Branches;
+      LLVM_FALLTHROUGH;
+    case Stmt::CompoundStmtClass:
+      TrackedParent.push_back(true);
+      break;
+    default:
+      TrackedParent.push_back(false);
+      break;
+    }
+
+    Base::TraverseStmt(Node);
+
+    TrackedParent.pop_back();
+
+    return true;
+  }
+
+  bool TraverseCompoundStmt(CompoundStmt *Node) {
+    // If this new compound statement is located in a compound statement, which
+    // is already nested NestingThreshold levels deep, record the start location
+    // of this new compound statement.
+    if (CurrentNestingLevel == Info.NestingThreshold)
+      Info.NestingThresholders.push_back(Node->getLocStart());
+
+    ++CurrentNestingLevel;
+    Base::TraverseCompoundStmt(Node);
+    --CurrentNestingLevel;
+
+    return true;
+  }
+
+  bool TraverseDecl(Decl *Node) {
+    TrackedParent.push_back(false);
+    Base::TraverseDecl(Node);
+    TrackedParent.pop_back();
+    return true;
+  }
+
+  struct FunctionInfo {
+    unsigned Lines = 0;
+    unsigned Statements = 0;
+    unsigned Branches = 0;
+    unsigned NestingThreshold = 0;
+    std::vector<SourceLocation> NestingThresholders;
+  };
+  FunctionInfo Info;
+  std::vector<bool> TrackedParent;
+  unsigned CurrentNestingLevel = 0;
+};
+
+FunctionSizeCheck::FunctionSizeCheck(StringRef Name, ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      LineThreshold(Options.get("LineThreshold", -1U)),
+      StatementThreshold(Options.get("StatementThreshold", 800U)),
+      BranchThreshold(Options.get("BranchThreshold", -1U)),
+      ParameterThreshold(Options.get("ParameterThreshold", -1U)),
+      NestingThreshold(Options.get("NestingThreshold", -1U)) {}
+
+void FunctionSizeCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "LineThreshold", LineThreshold);
+  Options.store(Opts, "StatementThreshold", StatementThreshold);
+  Options.store(Opts, "BranchThreshold", BranchThreshold);
+  Options.store(Opts, "ParameterThreshold", ParameterThreshold);
+  Options.store(Opts, "NestingThreshold", NestingThreshold);
+}
+
+void FunctionSizeCheck::registerMatchers(MatchFinder *Finder) {
+  Finder->addMatcher(functionDecl(unless(isInstantiated())).bind("func"), this);
+}
+
+void FunctionSizeCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *Func = Result.Nodes.getNodeAs<FunctionDecl>("func");
+
+  FunctionASTVisitor Visitor;
+  Visitor.Info.NestingThreshold = NestingThreshold;
+  Visitor.TraverseDecl(const_cast<FunctionDecl *>(Func));
+  auto &FI = Visitor.Info;
+
+  if (FI.Statements == 0)
+    return;
+
+  // Count the lines including whitespace and comments. Really simple.
+  if (const Stmt *Body = Func->getBody()) {
+    SourceManager *SM = Result.SourceManager;
+    if (SM->isWrittenInSameFile(Body->getLocStart(), Body->getLocEnd())) {
+      FI.Lines = SM->getSpellingLineNumber(Body->getLocEnd()) -
+                 SM->getSpellingLineNumber(Body->getLocStart());
+    }
+  }
+
+  unsigned ActualNumberParameters = Func->getNumParams();
+
+  if (FI.Lines > LineThreshold || FI.Statements > StatementThreshold ||
+      FI.Branches > BranchThreshold ||
+      ActualNumberParameters > ParameterThreshold ||
+      !FI.NestingThresholders.empty()) {
+    diag(Func->getLocation(),
+         "function %0 exceeds recommended size/complexity thresholds")
+        << Func;
+  }
+
+  if (FI.Lines > LineThreshold) {
+    diag(Func->getLocation(),
+         "%0 lines including whitespace and comments (threshold %1)",
+         DiagnosticIDs::Note)
+        << FI.Lines << LineThreshold;
+  }
+
+  if (FI.Statements > StatementThreshold) {
+    diag(Func->getLocation(), "%0 statements (threshold %1)",
+         DiagnosticIDs::Note)
+        << FI.Statements << StatementThreshold;
+  }
+
+  if (FI.Branches > BranchThreshold) {
+    diag(Func->getLocation(), "%0 branches (threshold %1)", DiagnosticIDs::Note)
+        << FI.Branches << BranchThreshold;
+  }
+
+  if (ActualNumberParameters > ParameterThreshold) {
+    diag(Func->getLocation(), "%0 parameters (threshold %1)",
+         DiagnosticIDs::Note)
+        << ActualNumberParameters << ParameterThreshold;
+  }
+
+  for (const auto &CSPos : FI.NestingThresholders) {
+    diag(CSPos, "nesting level %0 starts here (threshold %1)",
+         DiagnosticIDs::Note)
+        << NestingThreshold + 1 << NestingThreshold;
+  }
+}
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/readability/FunctionSizeCheck.h b/clang-tidy/readability/FunctionSizeCheck.h
new file mode 100644 (file)
index 0000000..3986b95
--- /dev/null
@@ -0,0 +1,56 @@
+//===--- FunctionSizeCheck.h - clang-tidy -----------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_FUNCTIONSIZECHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_FUNCTIONSIZECHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+/// Checks for large functions based on various metrics.
+///
+/// These options are supported:
+///
+///   * `LineThreshold` - flag functions exceeding this number of lines. The
+///     default is `-1` (ignore the number of lines).
+///   * `StatementThreshold` - flag functions exceeding this number of
+///     statements. This may differ significantly from the number of lines for
+///     macro-heavy code. The default is `800`.
+///   * `BranchThreshold` - flag functions exceeding this number of control
+///     statements. The default is `-1` (ignore the number of branches).
+///   * `ParameterThreshold` - flag functions having a high number of
+///     parameters. The default is `-1` (ignore the number of parameters).
+///   * `NestingThreshold` - flag compound statements which create next nesting
+///     level after `NestingThreshold`. This may differ significantly from the
+///     expected value for macro-heavy code. The default is `-1` (ignore the
+///     nesting level).
+class FunctionSizeCheck : public ClangTidyCheck {
+public:
+  FunctionSizeCheck(StringRef Name, ClangTidyContext *Context);
+
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+  const unsigned LineThreshold;
+  const unsigned StatementThreshold;
+  const unsigned BranchThreshold;
+  const unsigned ParameterThreshold;
+  const unsigned NestingThreshold;
+};
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_FUNCTIONSIZECHECK_H
diff --git a/clang-tidy/readability/IdentifierNamingCheck.cpp b/clang-tidy/readability/IdentifierNamingCheck.cpp
new file mode 100644 (file)
index 0000000..175feea
--- /dev/null
@@ -0,0 +1,940 @@
+//===--- IdentifierNamingCheck.cpp - clang-tidy ---------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "IdentifierNamingCheck.h"
+
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Lex/PPCallbacks.h"
+#include "clang/Lex/Preprocessor.h"
+#include "llvm/ADT/DenseMapInfo.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/Format.h"
+
+#define DEBUG_TYPE "clang-tidy"
+
+using namespace clang::ast_matchers;
+
+namespace llvm {
+/// Specialisation of DenseMapInfo to allow NamingCheckId objects in DenseMaps
+template <>
+struct DenseMapInfo<
+    clang::tidy::readability::IdentifierNamingCheck::NamingCheckId> {
+  using NamingCheckId =
+      clang::tidy::readability::IdentifierNamingCheck::NamingCheckId;
+
+  static inline NamingCheckId getEmptyKey() {
+    return NamingCheckId(
+        clang::SourceLocation::getFromRawEncoding(static_cast<unsigned>(-1)),
+        "EMPTY");
+  }
+
+  static inline NamingCheckId getTombstoneKey() {
+    return NamingCheckId(
+        clang::SourceLocation::getFromRawEncoding(static_cast<unsigned>(-2)),
+        "TOMBSTONE");
+  }
+
+  static unsigned getHashValue(NamingCheckId Val) {
+    assert(Val != getEmptyKey() && "Cannot hash the empty key!");
+    assert(Val != getTombstoneKey() && "Cannot hash the tombstone key!");
+
+    std::hash<NamingCheckId::second_type> SecondHash;
+    return Val.first.getRawEncoding() + SecondHash(Val.second);
+  }
+
+  static bool isEqual(const NamingCheckId &LHS, const NamingCheckId &RHS) {
+    if (RHS == getEmptyKey())
+      return LHS == getEmptyKey();
+    if (RHS == getTombstoneKey())
+      return LHS == getTombstoneKey();
+    return LHS == RHS;
+  }
+};
+} // namespace llvm
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+// clang-format off
+#define NAMING_KEYS(m) \
+    m(Namespace) \
+    m(InlineNamespace) \
+    m(EnumConstant) \
+    m(ConstexprVariable) \
+    m(ConstantMember) \
+    m(PrivateMember) \
+    m(ProtectedMember) \
+    m(PublicMember) \
+    m(Member) \
+    m(ClassConstant) \
+    m(ClassMember) \
+    m(GlobalConstant) \
+    m(GlobalVariable) \
+    m(LocalConstant) \
+    m(LocalVariable) \
+    m(StaticConstant) \
+    m(StaticVariable) \
+    m(Constant) \
+    m(Variable) \
+    m(ConstantParameter) \
+    m(ParameterPack) \
+    m(Parameter) \
+    m(AbstractClass) \
+    m(Struct) \
+    m(Class) \
+    m(Union) \
+    m(Enum) \
+    m(GlobalFunction) \
+    m(ConstexprFunction) \
+    m(Function) \
+    m(ConstexprMethod) \
+    m(VirtualMethod) \
+    m(ClassMethod) \
+    m(PrivateMethod) \
+    m(ProtectedMethod) \
+    m(PublicMethod) \
+    m(Method) \
+    m(Typedef) \
+    m(TypeTemplateParameter) \
+    m(ValueTemplateParameter) \
+    m(TemplateTemplateParameter) \
+    m(TemplateParameter) \
+    m(TypeAlias) \
+    m(MacroDefinition) \
+
+enum StyleKind {
+#define ENUMERATE(v) SK_ ## v,
+  NAMING_KEYS(ENUMERATE)
+#undef ENUMERATE
+  SK_Count,
+  SK_Invalid
+};
+
+static StringRef const StyleNames[] = {
+#define STRINGIZE(v) #v,
+  NAMING_KEYS(STRINGIZE)
+#undef STRINGIZE
+};
+
+#undef NAMING_KEYS
+// clang-format on
+
+namespace {
+/// Callback supplies macros to IdentifierNamingCheck::checkMacro
+class IdentifierNamingCheckPPCallbacks : public PPCallbacks {
+public:
+  IdentifierNamingCheckPPCallbacks(Preprocessor *PP,
+                                   IdentifierNamingCheck *Check)
+      : PP(PP), Check(Check) {}
+
+  /// MacroDefined calls checkMacro for macros in the main file
+  void MacroDefined(const Token &MacroNameTok,
+                    const MacroDirective *MD) override {
+    Check->checkMacro(PP->getSourceManager(), MacroNameTok, MD->getMacroInfo());
+  }
+
+  /// MacroExpands calls expandMacro for macros in the main file
+  void MacroExpands(const Token &MacroNameTok, const MacroDefinition &MD,
+                    SourceRange /*Range*/,
+                    const MacroArgs * /*Args*/) override {
+    Check->expandMacro(MacroNameTok, MD.getMacroInfo());
+  }
+
+private:
+  Preprocessor *PP;
+  IdentifierNamingCheck *Check;
+};
+} // namespace
+
+IdentifierNamingCheck::IdentifierNamingCheck(StringRef Name,
+                                             ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context) {
+  auto const fromString = [](StringRef Str) {
+    return llvm::StringSwitch<llvm::Optional<CaseType>>(Str)
+        .Case("aNy_CasE", CT_AnyCase)
+        .Case("lower_case", CT_LowerCase)
+        .Case("UPPER_CASE", CT_UpperCase)
+        .Case("camelBack", CT_CamelBack)
+        .Case("CamelCase", CT_CamelCase)
+        .Case("Camel_Snake_Case", CT_CamelSnakeCase)
+        .Case("camel_Snake_Back", CT_CamelSnakeBack)
+        .Default(llvm::None);
+  };
+
+  for (auto const &Name : StyleNames) {
+    auto const caseOptional =
+        fromString(Options.get((Name + "Case").str(), ""));
+    auto prefix = Options.get((Name + "Prefix").str(), "");
+    auto postfix = Options.get((Name + "Suffix").str(), "");
+
+    if (caseOptional || !prefix.empty() || !postfix.empty()) {
+      NamingStyles.push_back(NamingStyle(caseOptional, prefix, postfix));
+    } else {
+      NamingStyles.push_back(llvm::None);
+    }
+  }
+
+  IgnoreFailedSplit = Options.get("IgnoreFailedSplit", 0);
+}
+
+void IdentifierNamingCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+  auto const toString = [](CaseType Type) {
+    switch (Type) {
+    case CT_AnyCase:
+      return "aNy_CasE";
+    case CT_LowerCase:
+      return "lower_case";
+    case CT_CamelBack:
+      return "camelBack";
+    case CT_UpperCase:
+      return "UPPER_CASE";
+    case CT_CamelCase:
+      return "CamelCase";
+    case CT_CamelSnakeCase:
+      return "Camel_Snake_Case";
+    case CT_CamelSnakeBack:
+      return "camel_Snake_Back";
+    }
+
+    llvm_unreachable("Unknown Case Type");
+  };
+
+  for (size_t i = 0; i < SK_Count; ++i) {
+    if (NamingStyles[i]) {
+      if (NamingStyles[i]->Case) {
+        Options.store(Opts, (StyleNames[i] + "Case").str(),
+                      toString(*NamingStyles[i]->Case));
+      }
+      Options.store(Opts, (StyleNames[i] + "Prefix").str(),
+                    NamingStyles[i]->Prefix);
+      Options.store(Opts, (StyleNames[i] + "Suffix").str(),
+                    NamingStyles[i]->Suffix);
+    }
+  }
+
+  Options.store(Opts, "IgnoreFailedSplit", IgnoreFailedSplit);
+}
+
+void IdentifierNamingCheck::registerMatchers(MatchFinder *Finder) {
+  Finder->addMatcher(namedDecl().bind("decl"), this);
+  Finder->addMatcher(usingDecl().bind("using"), this);
+  Finder->addMatcher(declRefExpr().bind("declRef"), this);
+  Finder->addMatcher(cxxConstructorDecl().bind("classRef"), this);
+  Finder->addMatcher(cxxDestructorDecl().bind("classRef"), this);
+  Finder->addMatcher(typeLoc().bind("typeLoc"), this);
+  Finder->addMatcher(nestedNameSpecifierLoc().bind("nestedNameLoc"), this);
+}
+
+void IdentifierNamingCheck::registerPPCallbacks(CompilerInstance &Compiler) {
+  Compiler.getPreprocessor().addPPCallbacks(
+      llvm::make_unique<IdentifierNamingCheckPPCallbacks>(
+          &Compiler.getPreprocessor(), this));
+}
+
+static bool matchesStyle(StringRef Name,
+                         IdentifierNamingCheck::NamingStyle Style) {
+  static llvm::Regex Matchers[] = {
+      llvm::Regex("^.*$"),
+      llvm::Regex("^[a-z][a-z0-9_]*$"),
+      llvm::Regex("^[a-z][a-zA-Z0-9]*$"),
+      llvm::Regex("^[A-Z][A-Z0-9_]*$"),
+      llvm::Regex("^[A-Z][a-zA-Z0-9]*$"),
+      llvm::Regex("^[A-Z]([a-z0-9]*(_[A-Z])?)*"),
+      llvm::Regex("^[a-z]([a-z0-9]*(_[A-Z])?)*"),
+  };
+
+  bool Matches = true;
+  if (Name.startswith(Style.Prefix))
+    Name = Name.drop_front(Style.Prefix.size());
+  else
+    Matches = false;
+
+  if (Name.endswith(Style.Suffix))
+    Name = Name.drop_back(Style.Suffix.size());
+  else
+    Matches = false;
+
+  // Ensure the name doesn't have any extra underscores beyond those specified
+  // in the prefix and suffix.
+  if (Name.startswith("_") || Name.endswith("_"))
+    Matches = false;
+
+  if (Style.Case && !Matchers[static_cast<size_t>(*Style.Case)].match(Name))
+    Matches = false;
+
+  return Matches;
+}
+
+static std::string fixupWithCase(StringRef Name,
+                                 IdentifierNamingCheck::CaseType Case) {
+  static llvm::Regex Splitter(
+      "([a-z0-9A-Z]*)(_+)|([A-Z]?[a-z0-9]+)([A-Z]|$)|([A-Z]+)([A-Z]|$)");
+
+  SmallVector<StringRef, 8> Substrs;
+  Name.split(Substrs, "_", -1, false);
+
+  SmallVector<StringRef, 8> Words;
+  for (auto Substr : Substrs) {
+    while (!Substr.empty()) {
+      SmallVector<StringRef, 8> Groups;
+      if (!Splitter.match(Substr, &Groups))
+        break;
+
+      if (Groups[2].size() > 0) {
+        Words.push_back(Groups[1]);
+        Substr = Substr.substr(Groups[0].size());
+      } else if (Groups[3].size() > 0) {
+        Words.push_back(Groups[3]);
+        Substr = Substr.substr(Groups[0].size() - Groups[4].size());
+      } else if (Groups[5].size() > 0) {
+        Words.push_back(Groups[5]);
+        Substr = Substr.substr(Groups[0].size() - Groups[6].size());
+      }
+    }
+  }
+
+  if (Words.empty())
+    return Name;
+
+  std::string Fixup;
+  switch (Case) {
+  case IdentifierNamingCheck::CT_AnyCase:
+    Fixup += Name;
+    break;
+
+  case IdentifierNamingCheck::CT_LowerCase:
+    for (auto const &Word : Words) {
+      if (&Word != &Words.front())
+        Fixup += "_";
+      Fixup += Word.lower();
+    }
+    break;
+
+  case IdentifierNamingCheck::CT_UpperCase:
+    for (auto const &Word : Words) {
+      if (&Word != &Words.front())
+        Fixup += "_";
+      Fixup += Word.upper();
+    }
+    break;
+
+  case IdentifierNamingCheck::CT_CamelCase:
+    for (auto const &Word : Words) {
+      Fixup += Word.substr(0, 1).upper();
+      Fixup += Word.substr(1).lower();
+    }
+    break;
+
+  case IdentifierNamingCheck::CT_CamelBack:
+    for (auto const &Word : Words) {
+      if (&Word == &Words.front()) {
+        Fixup += Word.lower();
+      } else {
+        Fixup += Word.substr(0, 1).upper();
+        Fixup += Word.substr(1).lower();
+      }
+    }
+    break;
+
+  case IdentifierNamingCheck::CT_CamelSnakeCase:
+    for (auto const &Word : Words) {
+      if (&Word != &Words.front())
+        Fixup += "_";
+      Fixup += Word.substr(0, 1).upper();
+      Fixup += Word.substr(1).lower();
+    }
+    break;
+
+  case IdentifierNamingCheck::CT_CamelSnakeBack:
+    for (auto const &Word : Words) {
+      if (&Word != &Words.front()) {
+        Fixup += "_";
+        Fixup += Word.substr(0, 1).upper();
+      } else {
+        Fixup += Word.substr(0, 1).lower();
+      }
+      Fixup += Word.substr(1).lower();
+    }
+    break;
+  }
+
+  return Fixup;
+}
+
+static std::string
+fixupWithStyle(StringRef Name,
+               const IdentifierNamingCheck::NamingStyle &Style) {
+  const std::string Fixed = fixupWithCase(
+      Name, Style.Case.getValueOr(IdentifierNamingCheck::CaseType::CT_AnyCase));
+  StringRef Mid = StringRef(Fixed).trim("_");
+  if (Mid.empty())
+    Mid = "_";
+  return (Style.Prefix + Mid + Style.Suffix).str();
+}
+
+static StyleKind findStyleKind(
+    const NamedDecl *D,
+    const std::vector<llvm::Optional<IdentifierNamingCheck::NamingStyle>>
+        &NamingStyles) {
+  if (isa<TypedefDecl>(D) && NamingStyles[SK_Typedef])
+    return SK_Typedef;
+
+  if (isa<TypeAliasDecl>(D) && NamingStyles[SK_TypeAlias])
+    return SK_TypeAlias;
+
+  if (const auto *Decl = dyn_cast<NamespaceDecl>(D)) {
+    if (Decl->isAnonymousNamespace())
+      return SK_Invalid;
+
+    if (Decl->isInline() && NamingStyles[SK_InlineNamespace])
+      return SK_InlineNamespace;
+
+    if (NamingStyles[SK_Namespace])
+      return SK_Namespace;
+  }
+
+  if (isa<EnumDecl>(D) && NamingStyles[SK_Enum])
+    return SK_Enum;
+
+  if (isa<EnumConstantDecl>(D)) {
+    if (NamingStyles[SK_EnumConstant])
+      return SK_EnumConstant;
+
+    if (NamingStyles[SK_Constant])
+      return SK_Constant;
+
+    return SK_Invalid;
+  }
+
+  if (const auto *Decl = dyn_cast<CXXRecordDecl>(D)) {
+    if (Decl->isAnonymousStructOrUnion())
+      return SK_Invalid;
+
+    if (!Decl->getCanonicalDecl()->isThisDeclarationADefinition())
+      return SK_Invalid;
+
+    if (Decl->hasDefinition() && Decl->isAbstract() &&
+        NamingStyles[SK_AbstractClass])
+      return SK_AbstractClass;
+
+    if (Decl->isStruct() && NamingStyles[SK_Struct])
+      return SK_Struct;
+
+    if (Decl->isStruct() && NamingStyles[SK_Class])
+      return SK_Class;
+
+    if (Decl->isClass() && NamingStyles[SK_Class])
+      return SK_Class;
+
+    if (Decl->isClass() && NamingStyles[SK_Struct])
+      return SK_Struct;
+
+    if (Decl->isUnion() && NamingStyles[SK_Union])
+      return SK_Union;
+
+    if (Decl->isEnum() && NamingStyles[SK_Enum])
+      return SK_Enum;
+
+    return SK_Invalid;
+  }
+
+  if (const auto *Decl = dyn_cast<FieldDecl>(D)) {
+    QualType Type = Decl->getType();
+
+    if (!Type.isNull() && Type.isLocalConstQualified() &&
+        NamingStyles[SK_ConstantMember])
+      return SK_ConstantMember;
+
+    if (!Type.isNull() && Type.isLocalConstQualified() &&
+        NamingStyles[SK_Constant])
+      return SK_Constant;
+
+    if (Decl->getAccess() == AS_private && NamingStyles[SK_PrivateMember])
+      return SK_PrivateMember;
+
+    if (Decl->getAccess() == AS_protected && NamingStyles[SK_ProtectedMember])
+      return SK_ProtectedMember;
+
+    if (Decl->getAccess() == AS_public && NamingStyles[SK_PublicMember])
+      return SK_PublicMember;
+
+    if (NamingStyles[SK_Member])
+      return SK_Member;
+
+    return SK_Invalid;
+  }
+
+  if (const auto *Decl = dyn_cast<ParmVarDecl>(D)) {
+    QualType Type = Decl->getType();
+
+    if (Decl->isConstexpr() && NamingStyles[SK_ConstexprVariable])
+      return SK_ConstexprVariable;
+
+    if (!Type.isNull() && Type.isLocalConstQualified() &&
+        NamingStyles[SK_ConstantParameter])
+      return SK_ConstantParameter;
+
+    if (!Type.isNull() && Type.isLocalConstQualified() &&
+        NamingStyles[SK_Constant])
+      return SK_Constant;
+
+    if (Decl->isParameterPack() && NamingStyles[SK_ParameterPack])
+      return SK_ParameterPack;
+
+    if (NamingStyles[SK_Parameter])
+      return SK_Parameter;
+
+    return SK_Invalid;
+  }
+
+  if (const auto *Decl = dyn_cast<VarDecl>(D)) {
+    QualType Type = Decl->getType();
+
+    if (Decl->isConstexpr() && NamingStyles[SK_ConstexprVariable])
+      return SK_ConstexprVariable;
+
+    if (!Type.isNull() && Type.isLocalConstQualified() &&
+        Decl->isStaticDataMember() && NamingStyles[SK_ClassConstant])
+      return SK_ClassConstant;
+
+    if (!Type.isNull() && Type.isLocalConstQualified() &&
+        Decl->isFileVarDecl() && NamingStyles[SK_GlobalConstant])
+      return SK_GlobalConstant;
+
+    if (!Type.isNull() && Type.isLocalConstQualified() &&
+        Decl->isStaticLocal() && NamingStyles[SK_StaticConstant])
+      return SK_StaticConstant;
+
+    if (!Type.isNull() && Type.isLocalConstQualified() &&
+        Decl->isLocalVarDecl() && NamingStyles[SK_LocalConstant])
+      return SK_LocalConstant;
+
+    if (!Type.isNull() && Type.isLocalConstQualified() &&
+        Decl->isFunctionOrMethodVarDecl() && NamingStyles[SK_LocalConstant])
+      return SK_LocalConstant;
+
+    if (!Type.isNull() && Type.isLocalConstQualified() &&
+        NamingStyles[SK_Constant])
+      return SK_Constant;
+
+    if (Decl->isStaticDataMember() && NamingStyles[SK_ClassMember])
+      return SK_ClassMember;
+
+    if (Decl->isFileVarDecl() && NamingStyles[SK_GlobalVariable])
+      return SK_GlobalVariable;
+
+    if (Decl->isStaticLocal() && NamingStyles[SK_StaticVariable])
+      return SK_StaticVariable;
+
+    if (Decl->isLocalVarDecl() && NamingStyles[SK_LocalVariable])
+      return SK_LocalVariable;
+
+    if (Decl->isFunctionOrMethodVarDecl() && NamingStyles[SK_LocalVariable])
+      return SK_LocalVariable;
+
+    if (NamingStyles[SK_Variable])
+      return SK_Variable;
+
+    return SK_Invalid;
+  }
+
+  if (const auto *Decl = dyn_cast<CXXMethodDecl>(D)) {
+    if (Decl->isMain() || !Decl->isUserProvided() ||
+        Decl->isUsualDeallocationFunction() ||
+        Decl->isCopyAssignmentOperator() || Decl->isMoveAssignmentOperator() ||
+        Decl->size_overridden_methods() > 0)
+      return SK_Invalid;
+
+    if (Decl->isConstexpr() && NamingStyles[SK_ConstexprMethod])
+      return SK_ConstexprMethod;
+
+    if (Decl->isConstexpr() && NamingStyles[SK_ConstexprFunction])
+      return SK_ConstexprFunction;
+
+    if (Decl->isStatic() && NamingStyles[SK_ClassMethod])
+      return SK_ClassMethod;
+
+    if (Decl->isVirtual() && NamingStyles[SK_VirtualMethod])
+      return SK_VirtualMethod;
+
+    if (Decl->getAccess() == AS_private && NamingStyles[SK_PrivateMethod])
+      return SK_PrivateMethod;
+
+    if (Decl->getAccess() == AS_protected && NamingStyles[SK_ProtectedMethod])
+      return SK_ProtectedMethod;
+
+    if (Decl->getAccess() == AS_public && NamingStyles[SK_PublicMethod])
+      return SK_PublicMethod;
+
+    if (NamingStyles[SK_Method])
+      return SK_Method;
+
+    if (NamingStyles[SK_Function])
+      return SK_Function;
+
+    return SK_Invalid;
+  }
+
+  if (const auto *Decl = dyn_cast<FunctionDecl>(D)) {
+    if (Decl->isMain())
+      return SK_Invalid;
+
+    if (Decl->isConstexpr() && NamingStyles[SK_ConstexprFunction])
+      return SK_ConstexprFunction;
+
+    if (Decl->isGlobal() && NamingStyles[SK_GlobalFunction])
+      return SK_GlobalFunction;
+
+    if (NamingStyles[SK_Function])
+      return SK_Function;
+  }
+
+  if (isa<TemplateTypeParmDecl>(D)) {
+    if (NamingStyles[SK_TypeTemplateParameter])
+      return SK_TypeTemplateParameter;
+
+    if (NamingStyles[SK_TemplateParameter])
+      return SK_TemplateParameter;
+
+    return SK_Invalid;
+  }
+
+  if (isa<NonTypeTemplateParmDecl>(D)) {
+    if (NamingStyles[SK_ValueTemplateParameter])
+      return SK_ValueTemplateParameter;
+
+    if (NamingStyles[SK_TemplateParameter])
+      return SK_TemplateParameter;
+
+    return SK_Invalid;
+  }
+
+  if (isa<TemplateTemplateParmDecl>(D)) {
+    if (NamingStyles[SK_TemplateTemplateParameter])
+      return SK_TemplateTemplateParameter;
+
+    if (NamingStyles[SK_TemplateParameter])
+      return SK_TemplateParameter;
+
+    return SK_Invalid;
+  }
+
+  return SK_Invalid;
+}
+
+static void addUsage(IdentifierNamingCheck::NamingCheckFailureMap &Failures,
+                     const IdentifierNamingCheck::NamingCheckId &Decl,
+                     SourceRange Range, SourceManager *SourceMgr = nullptr) {
+  // Do nothing if the provided range is invalid.
+  if (Range.getBegin().isInvalid() || Range.getEnd().isInvalid())
+    return;
+
+  // If we have a source manager, use it to convert to the spelling location for
+  // performing the fix. This is necessary because macros can map the same
+  // spelling location to different source locations, and we only want to fix
+  // the token once, before it is expanded by the macro.
+  SourceLocation FixLocation = Range.getBegin();
+  if (SourceMgr)
+    FixLocation = SourceMgr->getSpellingLoc(FixLocation);
+  if (FixLocation.isInvalid())
+    return;
+
+  // Try to insert the identifier location in the Usages map, and bail out if it
+  // is already in there
+  auto &Failure = Failures[Decl];
+  if (!Failure.RawUsageLocs.insert(FixLocation.getRawEncoding()).second)
+    return;
+
+  if (!Failure.ShouldFix)
+    return;
+
+  // Check if the range is entirely contained within a macro argument.
+  SourceLocation MacroArgExpansionStartForRangeBegin;
+  SourceLocation MacroArgExpansionStartForRangeEnd;
+  bool RangeIsEntirelyWithinMacroArgument =
+      SourceMgr &&
+      SourceMgr->isMacroArgExpansion(Range.getBegin(),
+                                     &MacroArgExpansionStartForRangeBegin) &&
+      SourceMgr->isMacroArgExpansion(Range.getEnd(),
+                                     &MacroArgExpansionStartForRangeEnd) &&
+      MacroArgExpansionStartForRangeBegin == MacroArgExpansionStartForRangeEnd;
+
+  // Check if the range contains any locations from a macro expansion.
+  bool RangeContainsMacroExpansion = RangeIsEntirelyWithinMacroArgument ||
+                                     Range.getBegin().isMacroID() ||
+                                     Range.getEnd().isMacroID();
+
+  bool RangeCanBeFixed =
+      RangeIsEntirelyWithinMacroArgument || !RangeContainsMacroExpansion;
+  Failure.ShouldFix = RangeCanBeFixed;
+}
+
+/// Convenience method when the usage to be added is a NamedDecl
+static void addUsage(IdentifierNamingCheck::NamingCheckFailureMap &Failures,
+                     const NamedDecl *Decl, SourceRange Range,
+                     SourceManager *SourceMgr = nullptr) {
+  return addUsage(Failures, IdentifierNamingCheck::NamingCheckId(
+                                Decl->getLocation(), Decl->getNameAsString()),
+                  Range, SourceMgr);
+}
+
+void IdentifierNamingCheck::check(const MatchFinder::MatchResult &Result) {
+  if (const auto *Decl =
+          Result.Nodes.getNodeAs<CXXConstructorDecl>("classRef")) {
+    if (Decl->isImplicit())
+      return;
+
+    addUsage(NamingCheckFailures, Decl->getParent(),
+             Decl->getNameInfo().getSourceRange());
+
+    for (const auto *Init : Decl->inits()) {
+      if (!Init->isWritten() || Init->isInClassMemberInitializer())
+        continue;
+      if (const auto *FD = Init->getAnyMember())
+        addUsage(NamingCheckFailures, FD, SourceRange(Init->getMemberLocation()));
+      // Note: delegating constructors and base class initializers are handled
+      // via the "typeLoc" matcher.
+    }
+    return;
+  }
+
+  if (const auto *Decl =
+          Result.Nodes.getNodeAs<CXXDestructorDecl>("classRef")) {
+    if (Decl->isImplicit())
+      return;
+
+    SourceRange Range = Decl->getNameInfo().getSourceRange();
+    if (Range.getBegin().isInvalid())
+      return;
+    // The first token that will be found is the ~ (or the equivalent trigraph),
+    // we want instead to replace the next token, that will be the identifier.
+    Range.setBegin(CharSourceRange::getTokenRange(Range).getEnd());
+
+    addUsage(NamingCheckFailures, Decl->getParent(), Range);
+    return;
+  }
+
+  if (const auto *Loc = Result.Nodes.getNodeAs<TypeLoc>("typeLoc")) {
+    NamedDecl *Decl = nullptr;
+    if (const auto &Ref = Loc->getAs<TagTypeLoc>()) {
+      Decl = Ref.getDecl();
+    } else if (const auto &Ref = Loc->getAs<InjectedClassNameTypeLoc>()) {
+      Decl = Ref.getDecl();
+    } else if (const auto &Ref = Loc->getAs<UnresolvedUsingTypeLoc>()) {
+      Decl = Ref.getDecl();
+    } else if (const auto &Ref = Loc->getAs<TemplateTypeParmTypeLoc>()) {
+      Decl = Ref.getDecl();
+    }
+
+    if (Decl) {
+      addUsage(NamingCheckFailures, Decl, Loc->getSourceRange());
+      return;
+    }
+
+    if (const auto &Ref = Loc->getAs<TemplateSpecializationTypeLoc>()) {
+      const auto *Decl =
+          Ref.getTypePtr()->getTemplateName().getAsTemplateDecl();
+
+      SourceRange Range(Ref.getTemplateNameLoc(), Ref.getTemplateNameLoc());
+      if (const auto *ClassDecl = dyn_cast<TemplateDecl>(Decl)) {
+        if (const auto *TemplDecl = ClassDecl->getTemplatedDecl())
+          addUsage(NamingCheckFailures, TemplDecl, Range);
+        return;
+      }
+    }
+
+    if (const auto &Ref =
+            Loc->getAs<DependentTemplateSpecializationTypeLoc>()) {
+      if (const auto *Decl = Ref.getTypePtr()->getAsTagDecl())
+        addUsage(NamingCheckFailures, Decl, Loc->getSourceRange());
+      return;
+    }
+  }
+
+  if (const auto *Loc =
+          Result.Nodes.getNodeAs<NestedNameSpecifierLoc>("nestedNameLoc")) {
+    if (NestedNameSpecifier *Spec = Loc->getNestedNameSpecifier()) {
+      if (NamespaceDecl *Decl = Spec->getAsNamespace()) {
+        addUsage(NamingCheckFailures, Decl, Loc->getLocalSourceRange());
+        return;
+      }
+    }
+  }
+
+  if (const auto *Decl = Result.Nodes.getNodeAs<UsingDecl>("using")) {
+    for (const auto &Shadow : Decl->shadows()) {
+      addUsage(NamingCheckFailures, Shadow->getTargetDecl(),
+               Decl->getNameInfo().getSourceRange());
+    }
+    return;
+  }
+
+  if (const auto *DeclRef = Result.Nodes.getNodeAs<DeclRefExpr>("declRef")) {
+    SourceRange Range = DeclRef->getNameInfo().getSourceRange();
+    addUsage(NamingCheckFailures, DeclRef->getDecl(), Range,
+             Result.SourceManager);
+    return;
+  }
+
+  if (const auto *Decl = Result.Nodes.getNodeAs<NamedDecl>("decl")) {
+    if (!Decl->getIdentifier() || Decl->getName().empty() || Decl->isImplicit())
+      return;
+
+    // Fix type aliases in value declarations
+    if (const auto *Value = Result.Nodes.getNodeAs<ValueDecl>("decl")) {
+      if (const auto *Typedef =
+              Value->getType().getTypePtr()->getAs<TypedefType>()) {
+        addUsage(NamingCheckFailures, Typedef->getDecl(),
+                 Value->getSourceRange());
+      }
+    }
+
+    // Fix type aliases in function declarations
+    if (const auto *Value = Result.Nodes.getNodeAs<FunctionDecl>("decl")) {
+      if (const auto *Typedef =
+              Value->getReturnType().getTypePtr()->getAs<TypedefType>()) {
+        addUsage(NamingCheckFailures, Typedef->getDecl(),
+                 Value->getSourceRange());
+      }
+      for (unsigned i = 0; i < Value->getNumParams(); ++i) {
+        if (const auto *Typedef = Value->parameters()[i]
+                                      ->getType()
+                                      .getTypePtr()
+                                      ->getAs<TypedefType>()) {
+          addUsage(NamingCheckFailures, Typedef->getDecl(),
+                   Value->getSourceRange());
+        }
+      }
+    }
+
+    // Ignore ClassTemplateSpecializationDecl which are creating duplicate
+    // replacements with CXXRecordDecl
+    if (isa<ClassTemplateSpecializationDecl>(Decl))
+      return;
+
+    StyleKind SK = findStyleKind(Decl, NamingStyles);
+    if (SK == SK_Invalid)
+      return;
+
+    if (!NamingStyles[SK])
+      return;
+
+    const NamingStyle &Style = *NamingStyles[SK];
+    StringRef Name = Decl->getName();
+    if (matchesStyle(Name, Style))
+      return;
+
+    std::string KindName = fixupWithCase(StyleNames[SK], CT_LowerCase);
+    std::replace(KindName.begin(), KindName.end(), '_', ' ');
+
+    std::string Fixup = fixupWithStyle(Name, Style);
+    if (StringRef(Fixup).equals(Name)) {
+      if (!IgnoreFailedSplit) {
+        DEBUG(llvm::dbgs()
+              << Decl->getLocStart().printToString(*Result.SourceManager)
+              << llvm::format(": unable to split words for %s '%s'\n",
+                              KindName.c_str(), Name.str().c_str()));
+      }
+    } else {
+      NamingCheckFailure &Failure = NamingCheckFailures[NamingCheckId(
+          Decl->getLocation(), Decl->getNameAsString())];
+      SourceRange Range =
+          DeclarationNameInfo(Decl->getDeclName(), Decl->getLocation())
+              .getSourceRange();
+
+      Failure.Fixup = std::move(Fixup);
+      Failure.KindName = std::move(KindName);
+      addUsage(NamingCheckFailures, Decl, Range);
+    }
+  }
+}
+
+void IdentifierNamingCheck::checkMacro(SourceManager &SourceMgr,
+                                       const Token &MacroNameTok,
+                                       const MacroInfo *MI) {
+  if (!NamingStyles[SK_MacroDefinition])
+    return;
+
+  StringRef Name = MacroNameTok.getIdentifierInfo()->getName();
+  const NamingStyle &Style = *NamingStyles[SK_MacroDefinition];
+  if (matchesStyle(Name, Style))
+    return;
+
+  std::string KindName =
+      fixupWithCase(StyleNames[SK_MacroDefinition], CT_LowerCase);
+  std::replace(KindName.begin(), KindName.end(), '_', ' ');
+
+  std::string Fixup = fixupWithStyle(Name, Style);
+  if (StringRef(Fixup).equals(Name)) {
+    if (!IgnoreFailedSplit) {
+      DEBUG(
+          llvm::dbgs() << MacroNameTok.getLocation().printToString(SourceMgr)
+                       << llvm::format(": unable to split words for %s '%s'\n",
+                                       KindName.c_str(), Name.str().c_str()));
+    }
+  } else {
+    NamingCheckId ID(MI->getDefinitionLoc(), Name);
+    NamingCheckFailure &Failure = NamingCheckFailures[ID];
+    SourceRange Range(MacroNameTok.getLocation(), MacroNameTok.getEndLoc());
+
+    Failure.Fixup = std::move(Fixup);
+    Failure.KindName = std::move(KindName);
+    addUsage(NamingCheckFailures, ID, Range);
+  }
+}
+
+void IdentifierNamingCheck::expandMacro(const Token &MacroNameTok,
+                                        const MacroInfo *MI) {
+  StringRef Name = MacroNameTok.getIdentifierInfo()->getName();
+  NamingCheckId ID(MI->getDefinitionLoc(), Name);
+
+  auto Failure = NamingCheckFailures.find(ID);
+  if (Failure == NamingCheckFailures.end())
+    return;
+
+  SourceRange Range(MacroNameTok.getLocation(), MacroNameTok.getEndLoc());
+  addUsage(NamingCheckFailures, ID, Range);
+}
+
+void IdentifierNamingCheck::onEndOfTranslationUnit() {
+  for (const auto &Pair : NamingCheckFailures) {
+    const NamingCheckId &Decl = Pair.first;
+    const NamingCheckFailure &Failure = Pair.second;
+
+    if (Failure.KindName.empty())
+      continue;
+
+    if (Failure.ShouldFix) {
+      auto Diag = diag(Decl.first, "invalid case style for %0 '%1'")
+                  << Failure.KindName << Decl.second;
+
+      for (const auto &Loc : Failure.RawUsageLocs) {
+        // We assume that the identifier name is made of one token only. This is
+        // always the case as we ignore usages in macros that could build
+        // identifier names by combining multiple tokens.
+        //
+        // For destructors, we alread take care of it by remembering the
+        // location of the start of the identifier and not the start of the
+        // tilde.
+        //
+        // Other multi-token identifiers, such as operators are not checked at
+        // all.
+        Diag << FixItHint::CreateReplacement(
+            SourceRange(SourceLocation::getFromRawEncoding(Loc)),
+            Failure.Fixup);
+      }
+    }
+  }
+}
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/readability/IdentifierNamingCheck.h b/clang-tidy/readability/IdentifierNamingCheck.h
new file mode 100644 (file)
index 0000000..c236ad5
--- /dev/null
@@ -0,0 +1,109 @@
+//===--- IdentifierNamingCheck.h - clang-tidy -------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_IDENTIFIERNAMINGCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_IDENTIFIERNAMINGCHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+
+class MacroInfo;
+
+namespace tidy {
+namespace readability {
+
+/// Checks for identifiers naming style mismatch.
+///
+/// This check will try to enforce coding guidelines on the identifiers naming.
+/// It supports `lower_case`, `UPPER_CASE`, `camelBack` and `CamelCase` casing
+/// and tries to convert from one to another if a mismatch is detected.
+///
+/// It also supports a fixed prefix and suffix that will be prepended or
+/// appended to the identifiers, regardless of the casing.
+///
+/// Many configuration options are available, in order to be able to create
+/// different rules for different kind of identifier. In general, the
+/// rules are falling back to a more generic rule if the specific case is not
+/// configured.
+class IdentifierNamingCheck : public ClangTidyCheck {
+public:
+  IdentifierNamingCheck(StringRef Name, ClangTidyContext *Context);
+
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+  void registerPPCallbacks(CompilerInstance &Compiler) override;
+  void onEndOfTranslationUnit() override;
+
+  enum CaseType {
+    CT_AnyCase = 0,
+    CT_LowerCase,
+    CT_CamelBack,
+    CT_UpperCase,
+    CT_CamelCase,
+    CT_CamelSnakeCase,
+    CT_CamelSnakeBack
+  };
+
+  struct NamingStyle {
+    NamingStyle() = default;
+
+    NamingStyle(llvm::Optional<CaseType> Case, const std::string &Prefix,
+                const std::string &Suffix)
+        : Case(Case), Prefix(Prefix), Suffix(Suffix) {}
+
+    llvm::Optional<CaseType> Case;
+    std::string Prefix;
+    std::string Suffix;
+  };
+
+  /// \brief Holds an identifier name check failure, tracking the kind of the
+  /// identifer, its possible fixup and the starting locations of all the
+  /// identifier usages.
+  struct NamingCheckFailure {
+    std::string KindName;
+    std::string Fixup;
+
+    /// \brief Whether the failure should be fixed or not.
+    ///
+    /// ie: if the identifier was used or declared within a macro we won't offer
+    /// a fixup for safety reasons.
+    bool ShouldFix;
+
+    /// \brief A set of all the identifier usages starting SourceLocation, in
+    /// their encoded form.
+    llvm::DenseSet<unsigned> RawUsageLocs;
+
+    NamingCheckFailure() : ShouldFix(true) {}
+  };
+
+  typedef std::pair<SourceLocation, std::string> NamingCheckId;
+
+  typedef llvm::DenseMap<NamingCheckId, NamingCheckFailure>
+      NamingCheckFailureMap;
+
+  /// Check Macros for style violations.
+  void checkMacro(SourceManager &sourceMgr, const Token &MacroNameTok,
+                  const MacroInfo *MI);
+
+  /// Add a usage of a macro if it already has a violation.
+  void expandMacro(const Token &MacroNameTok, const MacroInfo *MI);
+
+private:
+  std::vector<llvm::Optional<NamingStyle>> NamingStyles;
+  bool IgnoreFailedSplit;
+  NamingCheckFailureMap NamingCheckFailures;
+};
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_IDENTIFIERNAMINGCHECK_H
diff --git a/clang-tidy/readability/ImplicitBoolCastCheck.cpp b/clang-tidy/readability/ImplicitBoolCastCheck.cpp
new file mode 100644 (file)
index 0000000..996219c
--- /dev/null
@@ -0,0 +1,390 @@
+//===--- ImplicitBoolCastCheck.cpp - clang-tidy----------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ImplicitBoolCastCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Tooling/FixIt.h"
+#include <queue>
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+namespace {
+
+AST_MATCHER(Stmt, isMacroExpansion) {
+  SourceManager &SM = Finder->getASTContext().getSourceManager();
+  SourceLocation Loc = Node.getLocStart();
+  return SM.isMacroBodyExpansion(Loc) || SM.isMacroArgExpansion(Loc);
+}
+
+bool isNULLMacroExpansion(const Stmt *Statement, ASTContext &Context) {
+  SourceManager &SM = Context.getSourceManager();
+  const LangOptions &LO = Context.getLangOpts();
+  SourceLocation Loc = Statement->getLocStart();
+  return SM.isMacroBodyExpansion(Loc) &&
+         Lexer::getImmediateMacroName(Loc, SM, LO) == "NULL";
+}
+
+AST_MATCHER(Stmt, isNULLMacroExpansion) {
+  return isNULLMacroExpansion(&Node, Finder->getASTContext());
+}
+
+StringRef getZeroLiteralToCompareWithForType(CastKind CastExprKind,
+                                             QualType Type,
+                                             ASTContext &Context) {
+  switch (CastExprKind) {
+  case CK_IntegralToBoolean:
+    return Type->isUnsignedIntegerType() ? "0u" : "0";
+
+  case CK_FloatingToBoolean:
+    return Context.hasSameType(Type, Context.FloatTy) ? "0.0f" : "0.0";
+
+  case CK_PointerToBoolean:
+  case CK_MemberPointerToBoolean: // Fall-through on purpose.
+    return Context.getLangOpts().CPlusPlus11 ? "nullptr" : "0";
+
+  default:
+    llvm_unreachable("Unexpected cast kind");
+  }
+}
+
+bool isUnaryLogicalNotOperator(const Stmt *Statement) {
+  const auto *UnaryOperatorExpr = dyn_cast<UnaryOperator>(Statement);
+  return UnaryOperatorExpr && UnaryOperatorExpr->getOpcode() == UO_LNot;
+}
+
+bool areParensNeededForOverloadedOperator(OverloadedOperatorKind OperatorKind) {
+  switch (OperatorKind) {
+  case OO_New:
+  case OO_Delete: // Fall-through on purpose.
+  case OO_Array_New:
+  case OO_Array_Delete:
+  case OO_ArrowStar:
+  case OO_Arrow:
+  case OO_Call:
+  case OO_Subscript:
+    return false;
+
+  default:
+    return true;
+  }
+}
+
+bool areParensNeededForStatement(const Stmt *Statement) {
+  if (const auto *OperatorCall = dyn_cast<CXXOperatorCallExpr>(Statement)) {
+    return areParensNeededForOverloadedOperator(OperatorCall->getOperator());
+  }
+
+  return isa<BinaryOperator>(Statement) || isa<UnaryOperator>(Statement);
+}
+
+void fixGenericExprCastToBool(DiagnosticBuilder &Diag,
+                              const ImplicitCastExpr *Cast, const Stmt *Parent,
+                              ASTContext &Context) {
+  // In case of expressions like (! integer), we should remove the redundant not
+  // operator and use inverted comparison (integer == 0).
+  bool InvertComparison =
+      Parent != nullptr && isUnaryLogicalNotOperator(Parent);
+  if (InvertComparison) {
+    SourceLocation ParentStartLoc = Parent->getLocStart();
+    SourceLocation ParentEndLoc =
+        cast<UnaryOperator>(Parent)->getSubExpr()->getLocStart();
+    Diag << FixItHint::CreateRemoval(
+        CharSourceRange::getCharRange(ParentStartLoc, ParentEndLoc));
+
+    Parent = Context.getParents(*Parent)[0].get<Stmt>();
+  }
+
+  const Expr *SubExpr = Cast->getSubExpr();
+
+  bool NeedInnerParens = areParensNeededForStatement(SubExpr);
+  bool NeedOuterParens =
+      Parent != nullptr && areParensNeededForStatement(Parent);
+
+  std::string StartLocInsertion;
+
+  if (NeedOuterParens) {
+    StartLocInsertion += "(";
+  }
+  if (NeedInnerParens) {
+    StartLocInsertion += "(";
+  }
+
+  if (!StartLocInsertion.empty()) {
+    Diag << FixItHint::CreateInsertion(Cast->getLocStart(), StartLocInsertion);
+  }
+
+  std::string EndLocInsertion;
+
+  if (NeedInnerParens) {
+    EndLocInsertion += ")";
+  }
+
+  if (InvertComparison) {
+    EndLocInsertion += " == ";
+  } else {
+    EndLocInsertion += " != ";
+  }
+
+  EndLocInsertion += getZeroLiteralToCompareWithForType(
+      Cast->getCastKind(), SubExpr->getType(), Context);
+
+  if (NeedOuterParens) {
+    EndLocInsertion += ")";
+  }
+
+  SourceLocation EndLoc = Lexer::getLocForEndOfToken(
+      Cast->getLocEnd(), 0, Context.getSourceManager(), Context.getLangOpts());
+  Diag << FixItHint::CreateInsertion(EndLoc, EndLocInsertion);
+}
+
+StringRef getEquivalentBoolLiteralForExpr(const Expr *Expression,
+                                          ASTContext &Context) {
+  if (isNULLMacroExpansion(Expression, Context)) {
+    return "false";
+  }
+
+  if (const auto *IntLit = dyn_cast<IntegerLiteral>(Expression)) {
+    return (IntLit->getValue() == 0) ? "false" : "true";
+  }
+
+  if (const auto *FloatLit = dyn_cast<FloatingLiteral>(Expression)) {
+    llvm::APFloat FloatLitAbsValue = FloatLit->getValue();
+    FloatLitAbsValue.clearSign();
+    return (FloatLitAbsValue.bitcastToAPInt() == 0) ? "false" : "true";
+  }
+
+  if (const auto *CharLit = dyn_cast<CharacterLiteral>(Expression)) {
+    return (CharLit->getValue() == 0) ? "false" : "true";
+  }
+
+  if (isa<StringLiteral>(Expression->IgnoreCasts())) {
+    return "true";
+  }
+
+  return StringRef();
+}
+
+void fixGenericExprCastFromBool(DiagnosticBuilder &Diag,
+                                const ImplicitCastExpr *Cast,
+                                ASTContext &Context, StringRef OtherType) {
+  const Expr *SubExpr = Cast->getSubExpr();
+  bool NeedParens = !isa<ParenExpr>(SubExpr);
+
+  Diag << FixItHint::CreateInsertion(
+      Cast->getLocStart(),
+      (Twine("static_cast<") + OtherType + ">" + (NeedParens ? "(" : ""))
+          .str());
+
+  if (NeedParens) {
+    SourceLocation EndLoc = Lexer::getLocForEndOfToken(
+        Cast->getLocEnd(), 0, Context.getSourceManager(),
+        Context.getLangOpts());
+
+    Diag << FixItHint::CreateInsertion(EndLoc, ")");
+  }
+}
+
+StringRef getEquivalentForBoolLiteral(const CXXBoolLiteralExpr *BoolLiteral,
+                                      QualType DestType, ASTContext &Context) {
+  // Prior to C++11, false literal could be implicitly converted to pointer.
+  if (!Context.getLangOpts().CPlusPlus11 &&
+      (DestType->isPointerType() || DestType->isMemberPointerType()) &&
+      BoolLiteral->getValue() == false) {
+    return "0";
+  }
+
+  if (DestType->isFloatingType()) {
+    if (Context.hasSameType(DestType, Context.FloatTy)) {
+      return BoolLiteral->getValue() ? "1.0f" : "0.0f";
+    }
+    return BoolLiteral->getValue() ? "1.0" : "0.0";
+  }
+
+  if (DestType->isUnsignedIntegerType()) {
+    return BoolLiteral->getValue() ? "1u" : "0u";
+  }
+  return BoolLiteral->getValue() ? "1" : "0";
+}
+
+bool isAllowedConditionalCast(const ImplicitCastExpr *Cast,
+                              ASTContext &Context) {
+  std::queue<const Stmt *> Q;
+  Q.push(Cast);
+  while (!Q.empty()) {
+    for (const auto &N : Context.getParents(*Q.front())) {
+      const Stmt *S = N.get<Stmt>();
+      if (!S)
+        return false;
+      if (isa<IfStmt>(S) || isa<ConditionalOperator>(S) || isa<ForStmt>(S) ||
+          isa<WhileStmt>(S) || isa<BinaryConditionalOperator>(S))
+        return true;
+      if (isa<ParenExpr>(S) || isa<ImplicitCastExpr>(S) ||
+          isUnaryLogicalNotOperator(S) ||
+          (isa<BinaryOperator>(S) && cast<BinaryOperator>(S)->isLogicalOp())) {
+        Q.push(S);
+      } else {
+        return false;
+      }
+    }
+    Q.pop();
+  }
+  return false;
+}
+
+} // anonymous namespace
+
+ImplicitBoolCastCheck::ImplicitBoolCastCheck(StringRef Name,
+                                             ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      AllowConditionalIntegerCasts(
+          Options.get("AllowConditionalIntegerCasts", false)),
+      AllowConditionalPointerCasts(
+          Options.get("AllowConditionalPointerCasts", false)) {}
+
+void ImplicitBoolCastCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "AllowConditionalIntegerCasts",
+                AllowConditionalIntegerCasts);
+  Options.store(Opts, "AllowConditionalPointerCasts",
+                AllowConditionalPointerCasts);
+}
+
+void ImplicitBoolCastCheck::registerMatchers(MatchFinder *Finder) {
+  // This check doesn't make much sense if we run it on language without
+  // built-in bool support.
+  if (!getLangOpts().Bool) {
+    return;
+  }
+
+  auto exceptionCases =
+      expr(anyOf(allOf(isMacroExpansion(), unless(isNULLMacroExpansion())),
+                 hasParent(explicitCastExpr())));
+  auto implicitCastFromBool = implicitCastExpr(
+      anyOf(hasCastKind(CK_IntegralCast), hasCastKind(CK_IntegralToFloating),
+            // Prior to C++11 cast from bool literal to pointer was allowed.
+            allOf(anyOf(hasCastKind(CK_NullToPointer),
+                        hasCastKind(CK_NullToMemberPointer)),
+                  hasSourceExpression(cxxBoolLiteral()))),
+      hasSourceExpression(expr(hasType(booleanType()))),
+      unless(exceptionCases));
+  auto boolXor =
+      binaryOperator(hasOperatorName("^"), hasLHS(implicitCastFromBool),
+                     hasRHS(implicitCastFromBool));
+  Finder->addMatcher(
+      implicitCastExpr(
+          anyOf(hasCastKind(CK_IntegralToBoolean),
+                hasCastKind(CK_FloatingToBoolean),
+                hasCastKind(CK_PointerToBoolean),
+                hasCastKind(CK_MemberPointerToBoolean)),
+          // Exclude case of using if or while statements with variable
+          // declaration, e.g.:
+          //   if (int var = functionCall()) {}
+          unless(
+              hasParent(stmt(anyOf(ifStmt(), whileStmt()), has(declStmt())))),
+          // Exclude cases common to implicit cast to and from bool.
+          unless(exceptionCases), unless(has(boolXor)),
+          // Retrive also parent statement, to check if we need additional
+          // parens in replacement.
+          anyOf(hasParent(stmt().bind("parentStmt")), anything()),
+          unless(isInTemplateInstantiation()),
+          unless(hasAncestor(functionTemplateDecl())))
+          .bind("implicitCastToBool"),
+      this);
+
+  auto boolComparison = binaryOperator(
+      anyOf(hasOperatorName("=="), hasOperatorName("!=")),
+      hasLHS(implicitCastFromBool), hasRHS(implicitCastFromBool));
+  auto boolOpAssignment =
+      binaryOperator(anyOf(hasOperatorName("|="), hasOperatorName("&=")),
+                     hasLHS(expr(hasType(booleanType()))));
+  Finder->addMatcher(
+      implicitCastExpr(
+          implicitCastFromBool,
+          // Exclude comparisons of bools, as they are always cast to integers
+          // in such context:
+          //   bool_expr_a == bool_expr_b
+          //   bool_expr_a != bool_expr_b
+          unless(hasParent(binaryOperator(
+              anyOf(boolComparison, boolXor, boolOpAssignment)))),
+          // Check also for nested casts, for example: bool -> int -> float.
+          anyOf(hasParent(implicitCastExpr().bind("furtherImplicitCast")),
+                anything()),
+          unless(isInTemplateInstantiation()),
+          unless(hasAncestor(functionTemplateDecl())))
+          .bind("implicitCastFromBool"),
+      this);
+}
+
+void ImplicitBoolCastCheck::check(const MatchFinder::MatchResult &Result) {
+  if (const auto *CastToBool =
+          Result.Nodes.getNodeAs<ImplicitCastExpr>("implicitCastToBool")) {
+    const auto *Parent = Result.Nodes.getNodeAs<Stmt>("parentStmt");
+    return handleCastToBool(CastToBool, Parent, *Result.Context);
+  }
+
+  if (const auto *CastFromBool =
+          Result.Nodes.getNodeAs<ImplicitCastExpr>("implicitCastFromBool")) {
+    const auto *NextImplicitCast =
+        Result.Nodes.getNodeAs<ImplicitCastExpr>("furtherImplicitCast");
+    return handleCastFromBool(CastFromBool, NextImplicitCast, *Result.Context);
+  }
+}
+
+void ImplicitBoolCastCheck::handleCastToBool(const ImplicitCastExpr *Cast,
+                                             const Stmt *Parent,
+                                             ASTContext &Context) {
+  if (AllowConditionalPointerCasts &&
+      (Cast->getCastKind() == CK_PointerToBoolean ||
+       Cast->getCastKind() == CK_MemberPointerToBoolean) &&
+      isAllowedConditionalCast(Cast, Context)) {
+    return;
+  }
+
+  if (AllowConditionalIntegerCasts &&
+      Cast->getCastKind() == CK_IntegralToBoolean &&
+      isAllowedConditionalCast(Cast, Context)) {
+    return;
+  }
+
+  auto Diag = diag(Cast->getLocStart(), "implicit cast %0 -> bool")
+              << Cast->getSubExpr()->getType();
+
+  StringRef EquivalentLiteral =
+      getEquivalentBoolLiteralForExpr(Cast->getSubExpr(), Context);
+  if (!EquivalentLiteral.empty()) {
+    Diag << tooling::fixit::createReplacement(*Cast, EquivalentLiteral);
+  } else {
+    fixGenericExprCastToBool(Diag, Cast, Parent, Context);
+  }
+}
+
+void ImplicitBoolCastCheck::handleCastFromBool(
+    const ImplicitCastExpr *Cast, const ImplicitCastExpr *NextImplicitCast,
+    ASTContext &Context) {
+  QualType DestType =
+      NextImplicitCast ? NextImplicitCast->getType() : Cast->getType();
+  auto Diag = diag(Cast->getLocStart(), "implicit cast bool -> %0") << DestType;
+
+  if (const auto *BoolLiteral =
+          dyn_cast<CXXBoolLiteralExpr>(Cast->getSubExpr())) {
+    Diag << tooling::fixit::createReplacement(
+        *Cast, getEquivalentForBoolLiteral(BoolLiteral, DestType, Context));
+  } else {
+    fixGenericExprCastFromBool(Diag, Cast, Context, DestType.getAsString());
+  }
+}
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/readability/ImplicitBoolCastCheck.h b/clang-tidy/readability/ImplicitBoolCastCheck.h
new file mode 100644 (file)
index 0000000..cd8addf
--- /dev/null
@@ -0,0 +1,46 @@
+//===--- ImplicitBoolCastCheck.h - clang-tidy--------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_IMPLICIT_BOOL_CAST_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_IMPLICIT_BOOL_CAST_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+/// \brief Checks for use of implicit bool casts in expressions.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability-implicit-bool-cast.html
+class ImplicitBoolCastCheck : public ClangTidyCheck {
+public:
+  ImplicitBoolCastCheck(StringRef Name, ClangTidyContext *Context);
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+  void handleCastToBool(const ImplicitCastExpr *CastExpression,
+                        const Stmt *ParentStatement, ASTContext &Context);
+  void handleCastFromBool(const ImplicitCastExpr *CastExpression,
+                          const ImplicitCastExpr *FurtherImplicitCastExpression,
+                          ASTContext &Context);
+
+  bool AllowConditionalIntegerCasts;
+  bool AllowConditionalPointerCasts;
+};
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_IMPLICIT_BOOL_CAST_H
diff --git a/clang-tidy/readability/InconsistentDeclarationParameterNameCheck.cpp b/clang-tidy/readability/InconsistentDeclarationParameterNameCheck.cpp
new file mode 100644 (file)
index 0000000..d20416e
--- /dev/null
@@ -0,0 +1,339 @@
+//===--- InconsistentDeclarationParameterNameCheck.cpp - clang-tidy-------===//
+//
+//           The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "InconsistentDeclarationParameterNameCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+#include <algorithm>
+#include <functional>
+#include <sstream>
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+namespace {
+
+AST_MATCHER(FunctionDecl, hasOtherDeclarations) {
+  auto It = Node.redecls_begin();
+  auto EndIt = Node.redecls_end();
+
+  if (It == EndIt)
+    return false;
+
+  ++It;
+  return It != EndIt;
+}
+
+struct DifferingParamInfo {
+  DifferingParamInfo(StringRef SourceName, StringRef OtherName,
+                     SourceRange OtherNameRange, bool GenerateFixItHint)
+      : SourceName(SourceName), OtherName(OtherName),
+        OtherNameRange(OtherNameRange), GenerateFixItHint(GenerateFixItHint) {}
+
+  StringRef SourceName;
+  StringRef OtherName;
+  SourceRange OtherNameRange;
+  bool GenerateFixItHint;
+};
+
+using DifferingParamsContainer = llvm::SmallVector<DifferingParamInfo, 10>;
+
+struct InconsistentDeclarationInfo {
+  InconsistentDeclarationInfo(SourceLocation DeclarationLocation,
+                              DifferingParamsContainer &&DifferingParams)
+      : DeclarationLocation(DeclarationLocation),
+        DifferingParams(std::move(DifferingParams)) {}
+
+  SourceLocation DeclarationLocation;
+  DifferingParamsContainer DifferingParams;
+};
+
+using InconsistentDeclarationsContainer =
+    llvm::SmallVector<InconsistentDeclarationInfo, 2>;
+
+bool checkIfFixItHintIsApplicable(
+    const FunctionDecl *ParameterSourceDeclaration,
+    const ParmVarDecl *SourceParam, const FunctionDecl *OriginalDeclaration) {
+  // Assumptions with regard to function declarations/definition:
+  //  * If both function declaration and definition are seen, assume that
+  //    definition is most up-to-date, and use it to generate replacements.
+  //  * If only function declarations are seen, there is no easy way to tell
+  //    which is up-to-date and which is not, so don't do anything.
+  // TODO: This may be changed later, but for now it seems the reasonable
+  // solution.
+  if (!ParameterSourceDeclaration->isThisDeclarationADefinition())
+    return false;
+
+  // Assumption: if parameter is not referenced in function defintion body, it
+  // may indicate that it's outdated, so don't touch it.
+  if (!SourceParam->isReferenced())
+    return false;
+
+  // In case there is the primary template definition and (possibly several)
+  // template specializations (and each with possibly several redeclarations),
+  // it is not at all clear what to change.
+  if (OriginalDeclaration->getTemplatedKind() ==
+      FunctionDecl::TK_FunctionTemplateSpecialization)
+    return false;
+
+  // Other cases seem OK to allow replacements.
+  return true;
+}
+
+DifferingParamsContainer
+findDifferingParamsInDeclaration(const FunctionDecl *ParameterSourceDeclaration,
+                                 const FunctionDecl *OtherDeclaration,
+                                 const FunctionDecl *OriginalDeclaration) {
+  DifferingParamsContainer DifferingParams;
+
+  auto SourceParamIt = ParameterSourceDeclaration->param_begin();
+  auto OtherParamIt = OtherDeclaration->param_begin();
+
+  while (SourceParamIt != ParameterSourceDeclaration->param_end() &&
+         OtherParamIt != OtherDeclaration->param_end()) {
+    auto SourceParamName = (*SourceParamIt)->getName();
+    auto OtherParamName = (*OtherParamIt)->getName();
+
+    // FIXME: Provide a way to extract commented out parameter name from comment
+    // next to it.
+    if (!SourceParamName.empty() && !OtherParamName.empty() &&
+        SourceParamName != OtherParamName) {
+      SourceRange OtherParamNameRange =
+          DeclarationNameInfo((*OtherParamIt)->getDeclName(),
+                              (*OtherParamIt)->getLocation())
+              .getSourceRange();
+
+      bool GenerateFixItHint = checkIfFixItHintIsApplicable(
+          ParameterSourceDeclaration, *SourceParamIt, OriginalDeclaration);
+
+      DifferingParams.emplace_back(SourceParamName, OtherParamName,
+                                   OtherParamNameRange, GenerateFixItHint);
+    }
+
+    ++SourceParamIt;
+    ++OtherParamIt;
+  }
+
+  return DifferingParams;
+}
+
+InconsistentDeclarationsContainer
+findInconsitentDeclarations(const FunctionDecl *OriginalDeclaration,
+                            const FunctionDecl *ParameterSourceDeclaration,
+                            SourceManager &SM) {
+  InconsistentDeclarationsContainer InconsistentDeclarations;
+  SourceLocation ParameterSourceLocation =
+      ParameterSourceDeclaration->getLocation();
+
+  for (const FunctionDecl *OtherDeclaration : OriginalDeclaration->redecls()) {
+    SourceLocation OtherLocation = OtherDeclaration->getLocation();
+    if (OtherLocation != ParameterSourceLocation) { // Skip self.
+      DifferingParamsContainer DifferingParams =
+          findDifferingParamsInDeclaration(ParameterSourceDeclaration,
+                                           OtherDeclaration,
+                                           OriginalDeclaration);
+      if (!DifferingParams.empty()) {
+        InconsistentDeclarations.emplace_back(OtherDeclaration->getLocation(),
+                                              std::move(DifferingParams));
+      }
+    }
+  }
+
+  // Sort in order of appearance in translation unit to generate clear
+  // diagnostics.
+  std::sort(InconsistentDeclarations.begin(), InconsistentDeclarations.end(),
+            [&SM](const InconsistentDeclarationInfo &Info1,
+                  const InconsistentDeclarationInfo &Info2) {
+              return SM.isBeforeInTranslationUnit(Info1.DeclarationLocation,
+                                                  Info2.DeclarationLocation);
+            });
+  return InconsistentDeclarations;
+}
+
+const FunctionDecl *
+getParameterSourceDeclaration(const FunctionDecl *OriginalDeclaration) {
+  const FunctionTemplateDecl *PrimaryTemplate =
+      OriginalDeclaration->getPrimaryTemplate();
+  if (PrimaryTemplate != nullptr) {
+    // In case of template specializations, use primary template declaration as
+    // the source of parameter names.
+    return PrimaryTemplate->getTemplatedDecl();
+  }
+
+  // In other cases, try to change to function definition, if available.
+
+  if (OriginalDeclaration->isThisDeclarationADefinition())
+    return OriginalDeclaration;
+
+  for (const FunctionDecl *OtherDeclaration : OriginalDeclaration->redecls()) {
+    if (OtherDeclaration->isThisDeclarationADefinition()) {
+      return OtherDeclaration;
+    }
+  }
+
+  // No definition found, so return original declaration.
+  return OriginalDeclaration;
+}
+
+std::string joinParameterNames(
+    const DifferingParamsContainer &DifferingParams,
+    llvm::function_ref<StringRef(const DifferingParamInfo &)> ChooseParamName) {
+  llvm::SmallVector<char, 40> Buffer;
+  llvm::raw_svector_ostream Str(Buffer);
+  bool First = true;
+  for (const DifferingParamInfo &ParamInfo : DifferingParams) {
+    if (First)
+      First = false;
+    else
+      Str << ", ";
+
+    Str << "'" << ChooseParamName(ParamInfo).str() << "'";
+  }
+  return Str.str().str();
+}
+
+void formatDifferingParamsDiagnostic(
+    InconsistentDeclarationParameterNameCheck *Check, SourceLocation Location,
+    StringRef OtherDeclarationDescription,
+    const DifferingParamsContainer &DifferingParams) {
+  auto ChooseOtherName = [](const DifferingParamInfo &ParamInfo) {
+    return ParamInfo.OtherName;
+  };
+  auto ChooseSourceName = [](const DifferingParamInfo &ParamInfo) {
+    return ParamInfo.SourceName;
+  };
+
+  auto ParamDiag =
+      Check->diag(Location,
+                  "differing parameters are named here: (%0), in %1: (%2)",
+                  DiagnosticIDs::Level::Note)
+      << joinParameterNames(DifferingParams, ChooseOtherName)
+      << OtherDeclarationDescription
+      << joinParameterNames(DifferingParams, ChooseSourceName);
+
+  for (const DifferingParamInfo &ParamInfo : DifferingParams) {
+    if (ParamInfo.GenerateFixItHint) {
+      ParamDiag << FixItHint::CreateReplacement(
+          CharSourceRange::getTokenRange(ParamInfo.OtherNameRange),
+          ParamInfo.SourceName);
+    }
+  }
+}
+
+void formatDiagnosticsForDeclarations(
+    InconsistentDeclarationParameterNameCheck *Check,
+    const FunctionDecl *ParameterSourceDeclaration,
+    const FunctionDecl *OriginalDeclaration,
+    const InconsistentDeclarationsContainer &InconsistentDeclarations) {
+  Check->diag(
+      OriginalDeclaration->getLocation(),
+      "function %q0 has %1 other declaration%s1 with different parameter names")
+      << OriginalDeclaration
+      << static_cast<int>(InconsistentDeclarations.size());
+  int Count = 1;
+  for (const InconsistentDeclarationInfo &InconsistentDeclaration :
+       InconsistentDeclarations) {
+    Check->diag(InconsistentDeclaration.DeclarationLocation,
+                "the %ordinal0 inconsistent declaration seen here",
+                DiagnosticIDs::Level::Note)
+        << Count;
+
+    formatDifferingParamsDiagnostic(
+        Check, InconsistentDeclaration.DeclarationLocation,
+        "the other declaration", InconsistentDeclaration.DifferingParams);
+
+    ++Count;
+  }
+}
+
+void formatDiagnostics(
+    InconsistentDeclarationParameterNameCheck *Check,
+    const FunctionDecl *ParameterSourceDeclaration,
+    const FunctionDecl *OriginalDeclaration,
+    const InconsistentDeclarationsContainer &InconsistentDeclarations,
+    StringRef FunctionDescription, StringRef ParameterSourceDescription) {
+  for (const InconsistentDeclarationInfo &InconsistentDeclaration :
+       InconsistentDeclarations) {
+    Check->diag(InconsistentDeclaration.DeclarationLocation,
+                "%0 %q1 has a %2 with different parameter names")
+        << FunctionDescription << OriginalDeclaration
+        << ParameterSourceDescription;
+
+    Check->diag(ParameterSourceDeclaration->getLocation(), "the %0 seen here",
+                DiagnosticIDs::Level::Note)
+        << ParameterSourceDescription;
+
+    formatDifferingParamsDiagnostic(
+        Check, InconsistentDeclaration.DeclarationLocation,
+        ParameterSourceDescription, InconsistentDeclaration.DifferingParams);
+  }
+}
+
+} // anonymous namespace
+
+void InconsistentDeclarationParameterNameCheck::registerMatchers(
+    MatchFinder *Finder) {
+  Finder->addMatcher(functionDecl(unless(isImplicit()), hasOtherDeclarations())
+                         .bind("functionDecl"),
+                     this);
+}
+
+void InconsistentDeclarationParameterNameCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  const auto *OriginalDeclaration =
+      Result.Nodes.getNodeAs<FunctionDecl>("functionDecl");
+
+  if (VisitedDeclarations.count(OriginalDeclaration) > 0)
+    return; // Avoid multiple warnings.
+
+  const FunctionDecl *ParameterSourceDeclaration =
+      getParameterSourceDeclaration(OriginalDeclaration);
+
+  InconsistentDeclarationsContainer InconsistentDeclarations =
+      findInconsitentDeclarations(OriginalDeclaration,
+                                  ParameterSourceDeclaration,
+                                  *Result.SourceManager);
+  if (InconsistentDeclarations.empty()) {
+    // Avoid unnecessary further visits.
+    markRedeclarationsAsVisited(OriginalDeclaration);
+    return;
+  }
+
+  if (OriginalDeclaration->getTemplatedKind() ==
+      FunctionDecl::TK_FunctionTemplateSpecialization) {
+    formatDiagnostics(this, ParameterSourceDeclaration, OriginalDeclaration,
+                      InconsistentDeclarations,
+                      "function template specialization",
+                      "primary template declaration");
+  } else if (ParameterSourceDeclaration->isThisDeclarationADefinition()) {
+    formatDiagnostics(this, ParameterSourceDeclaration, OriginalDeclaration,
+                      InconsistentDeclarations, "function", "definition");
+  } else {
+    formatDiagnosticsForDeclarations(this, ParameterSourceDeclaration,
+                                     OriginalDeclaration,
+                                     InconsistentDeclarations);
+  }
+
+  markRedeclarationsAsVisited(OriginalDeclaration);
+}
+
+void InconsistentDeclarationParameterNameCheck::markRedeclarationsAsVisited(
+    const FunctionDecl *OriginalDeclaration) {
+  for (const FunctionDecl *Redecl : OriginalDeclaration->redecls()) {
+    VisitedDeclarations.insert(Redecl);
+  }
+}
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/readability/InconsistentDeclarationParameterNameCheck.h b/clang-tidy/readability/InconsistentDeclarationParameterNameCheck.h
new file mode 100644 (file)
index 0000000..5486031
--- /dev/null
@@ -0,0 +1,45 @@
+//===- InconsistentDeclarationParameterNameCheck.h - clang-tidy-*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_INCONSISTENT_DECLARATION_PARAMETER_NAME_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_INCONSISTENT_DECLARATION_PARAMETER_NAME_H
+
+#include "../ClangTidy.h"
+
+#include "llvm/ADT/DenseSet.h"
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+/// \brief Checks for declarations of functions which differ in parameter names.
+///
+/// For detailed documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability-inconsistent-declaration-parameter-name.html
+///
+class InconsistentDeclarationParameterNameCheck : public ClangTidyCheck {
+public:
+  InconsistentDeclarationParameterNameCheck(StringRef Name,
+                                            ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+  void markRedeclarationsAsVisited(const FunctionDecl *FunctionDeclaration);
+
+  llvm::DenseSet<const FunctionDecl *> VisitedDeclarations;
+};
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_INCONSISTENT_DECLARATION_PARAMETER_NAME_H
diff --git a/clang-tidy/readability/MisleadingIndentationCheck.cpp b/clang-tidy/readability/MisleadingIndentationCheck.cpp
new file mode 100644 (file)
index 0000000..8a6c8c4
--- /dev/null
@@ -0,0 +1,123 @@
+//===--- MisleadingIndentationCheck.cpp - clang-tidy-----------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "MisleadingIndentationCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+static const IfStmt *getPrecedingIf(const SourceManager &SM,
+                                    ASTContext *Context, const IfStmt *If) {
+  auto parents = Context->getParents(*If);
+  if (parents.size() != 1)
+    return nullptr;
+  if (const auto *PrecedingIf = parents[0].get<IfStmt>()) {
+    SourceLocation PreviousElseLoc = PrecedingIf->getElseLoc();
+    if (SM.getExpansionLineNumber(PreviousElseLoc) ==
+        SM.getExpansionLineNumber(If->getIfLoc()))
+      return PrecedingIf;
+  }
+  return nullptr;
+}
+
+void MisleadingIndentationCheck::danglingElseCheck(const SourceManager &SM,
+                                                   ASTContext *Context,
+                                                   const IfStmt *If) {
+  SourceLocation IfLoc = If->getIfLoc();
+  SourceLocation ElseLoc = If->getElseLoc();
+
+  if (IfLoc.isMacroID() || ElseLoc.isMacroID())
+    return;
+
+  if (SM.getExpansionLineNumber(If->getThen()->getLocEnd()) ==
+      SM.getExpansionLineNumber(ElseLoc))
+    return;
+
+  // Find location of first 'if' in a 'if else if' chain.
+  for (auto PrecedingIf = getPrecedingIf(SM, Context, If); PrecedingIf;
+       PrecedingIf = getPrecedingIf(SM, Context, PrecedingIf))
+    IfLoc = PrecedingIf->getIfLoc();
+
+  if (SM.getExpansionColumnNumber(IfLoc) !=
+      SM.getExpansionColumnNumber(ElseLoc))
+    diag(ElseLoc, "different indentation for 'if' and corresponding 'else'");
+}
+
+void MisleadingIndentationCheck::missingBracesCheck(const SourceManager &SM,
+                                                    const CompoundStmt *CStmt) {
+  const static StringRef StmtNames[] = {"if", "for", "while"};
+  for (unsigned int i = 0; i < CStmt->size() - 1; i++) {
+    const Stmt *CurrentStmt = CStmt->body_begin()[i];
+    const Stmt *Inner = nullptr;
+    int StmtKind = 0;
+
+    if (const auto *CurrentIf = dyn_cast<IfStmt>(CurrentStmt)) {
+      StmtKind = 0;
+      Inner =
+          CurrentIf->getElse() ? CurrentIf->getElse() : CurrentIf->getThen();
+    } else if (const auto *CurrentFor = dyn_cast<ForStmt>(CurrentStmt)) {
+      StmtKind = 1;
+      Inner = CurrentFor->getBody();
+    } else if (const auto *CurrentWhile = dyn_cast<WhileStmt>(CurrentStmt)) {
+      StmtKind = 2;
+      Inner = CurrentWhile->getBody();
+    } else {
+      continue;
+    }
+
+    if (isa<CompoundStmt>(Inner))
+      continue;
+
+    SourceLocation InnerLoc = Inner->getLocStart();
+    SourceLocation OuterLoc = CurrentStmt->getLocStart();
+
+    if (SM.getExpansionLineNumber(InnerLoc) ==
+        SM.getExpansionLineNumber(OuterLoc))
+      continue;
+
+    const Stmt *NextStmt = CStmt->body_begin()[i + 1];
+    SourceLocation NextLoc = NextStmt->getLocStart();
+
+    if (InnerLoc.isMacroID() || OuterLoc.isMacroID() || NextLoc.isMacroID())
+      continue;
+
+    if (SM.getExpansionColumnNumber(InnerLoc) ==
+        SM.getExpansionColumnNumber(NextLoc)) {
+      diag(NextLoc, "misleading indentation: statement is indented too deeply");
+      diag(OuterLoc, "did you mean this line to be inside this '%0'",
+           DiagnosticIDs::Note)
+          << StmtNames[StmtKind];
+    }
+  }
+}
+
+void MisleadingIndentationCheck::registerMatchers(MatchFinder *Finder) {
+  Finder->addMatcher(ifStmt(hasElse(stmt())).bind("if"), this);
+  Finder->addMatcher(
+      compoundStmt(has(stmt(anyOf(ifStmt(), forStmt(), whileStmt()))))
+          .bind("compound"),
+      this);
+}
+
+void MisleadingIndentationCheck::check(const MatchFinder::MatchResult &Result) {
+  if (const auto *If = Result.Nodes.getNodeAs<IfStmt>("if"))
+    danglingElseCheck(*Result.SourceManager, Result.Context, If);
+
+  if (const auto *CStmt = Result.Nodes.getNodeAs<CompoundStmt>("compound"))
+    missingBracesCheck(*Result.SourceManager, CStmt);
+}
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/readability/MisleadingIndentationCheck.h b/clang-tidy/readability/MisleadingIndentationCheck.h
new file mode 100644 (file)
index 0000000..0ca50bb
--- /dev/null
@@ -0,0 +1,42 @@
+//===--- MisleadingIndentationCheck.h - clang-tidy---------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_MISLEADING_INDENTATION_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_MISLEADING_INDENTATION_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+/// Checks the code for dangling else, and possible misleading indentations due
+/// to missing braces. Note that this check only works as expected when the tabs
+/// or spaces are used consistently and not mixed.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability-misleading-indentation.html
+class MisleadingIndentationCheck : public ClangTidyCheck {
+public:
+  MisleadingIndentationCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+  void danglingElseCheck(const SourceManager &SM, ASTContext *Context,
+                         const IfStmt *If);
+  void missingBracesCheck(const SourceManager &SM, const CompoundStmt *CStmt);
+};
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_MISLEADING_INDENTATION_H
diff --git a/clang-tidy/readability/MisplacedArrayIndexCheck.cpp b/clang-tidy/readability/MisplacedArrayIndexCheck.cpp
new file mode 100644 (file)
index 0000000..f5e09fa
--- /dev/null
@@ -0,0 +1,57 @@
+//===--- MisplacedArrayIndexCheck.cpp - clang-tidy-------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "MisplacedArrayIndexCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Tooling/FixIt.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+void MisplacedArrayIndexCheck::registerMatchers(MatchFinder *Finder) {
+  Finder->addMatcher(arraySubscriptExpr(hasLHS(hasType(isInteger())),
+                                        hasRHS(hasType(isAnyPointer())))
+                         .bind("expr"),
+                     this);
+}
+
+void MisplacedArrayIndexCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *ArraySubscriptE =
+      Result.Nodes.getNodeAs<ArraySubscriptExpr>("expr");
+
+  auto Diag = diag(ArraySubscriptE->getLocStart(), "confusing array subscript "
+                                                   "expression, usually the "
+                                                   "index is inside the []");
+
+  // Only try to fixit when LHS and RHS can be swapped directly without changing
+  // the logic.
+  const Expr *RHSE = ArraySubscriptE->getRHS()->IgnoreParenImpCasts();
+  if (!isa<StringLiteral>(RHSE) && !isa<DeclRefExpr>(RHSE) &&
+      !isa<MemberExpr>(RHSE))
+    return;
+
+  const StringRef LText = tooling::fixit::getText(
+      ArraySubscriptE->getLHS()->getSourceRange(), *Result.Context);
+  const StringRef RText = tooling::fixit::getText(
+      ArraySubscriptE->getRHS()->getSourceRange(), *Result.Context);
+
+  Diag << FixItHint::CreateReplacement(
+      ArraySubscriptE->getLHS()->getSourceRange(), RText);
+  Diag << FixItHint::CreateReplacement(
+      ArraySubscriptE->getRHS()->getSourceRange(), LText);
+}
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/readability/MisplacedArrayIndexCheck.h b/clang-tidy/readability/MisplacedArrayIndexCheck.h
new file mode 100644 (file)
index 0000000..e9a2231
--- /dev/null
@@ -0,0 +1,36 @@
+//===--- MisplacedArrayIndexCheck.h - clang-tidy-----------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_MISPLACED_ARRAY_INDEX_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_MISPLACED_ARRAY_INDEX_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+/// Warn about unusual array index syntax (`index[array]` instead of
+/// `array[index]`).
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability-misplaced-array-index.html
+class MisplacedArrayIndexCheck : public ClangTidyCheck {
+public:
+  MisplacedArrayIndexCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_MISPLACED_ARRAY_INDEX_H
diff --git a/clang-tidy/readability/NamedParameterCheck.cpp b/clang-tidy/readability/NamedParameterCheck.cpp
new file mode 100644 (file)
index 0000000..ffdc813
--- /dev/null
@@ -0,0 +1,127 @@
+//===--- NamedParameterCheck.cpp - clang-tidy -------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "NamedParameterCheck.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 readability {
+
+void NamedParameterCheck::registerMatchers(ast_matchers::MatchFinder *Finder) {
+  Finder->addMatcher(functionDecl(unless(isInstantiated())).bind("decl"), this);
+}
+
+void NamedParameterCheck::check(const MatchFinder::MatchResult &Result) {
+  const SourceManager &SM = *Result.SourceManager;
+  const auto *Function = Result.Nodes.getNodeAs<FunctionDecl>("decl");
+  SmallVector<std::pair<const FunctionDecl *, unsigned>, 4> UnnamedParams;
+
+  // Ignore implicitly generated members.
+  if (Function->isImplicit())
+    return;
+
+  // Ignore declarations without a definition if we're not dealing with an
+  // overriden method.
+  const FunctionDecl *Definition = nullptr;
+  if ((!Function->isDefined(Definition) || Function->isDefaulted() ||
+       Function->isDeleted()) &&
+      (!isa<CXXMethodDecl>(Function) ||
+       cast<CXXMethodDecl>(Function)->size_overridden_methods() == 0))
+    return;
+
+  // TODO: Handle overloads.
+  // TODO: We could check that all redeclarations use the same name for
+  //       arguments in the same position.
+  for (unsigned I = 0, E = Function->getNumParams(); I != E; ++I) {
+    const ParmVarDecl *Parm = Function->getParamDecl(I);
+    if (Parm->isImplicit())
+      continue;
+    // Look for unnamed parameters.
+    if (!Parm->getName().empty())
+      continue;
+
+    // Don't warn on the dummy argument on post-inc and post-dec operators.
+    if ((Function->getOverloadedOperator() == OO_PlusPlus ||
+         Function->getOverloadedOperator() == OO_MinusMinus) &&
+        Parm->getType()->isSpecificBuiltinType(BuiltinType::Int))
+      continue;
+
+    // Sanity check the source locations.
+    if (!Parm->getLocation().isValid() || Parm->getLocation().isMacroID() ||
+        !SM.isWrittenInSameFile(Parm->getLocStart(), Parm->getLocation()))
+      continue;
+
+    // Skip gmock testing::Unused parameters.
+    if (auto Typedef = Parm->getType()->getAs<clang::TypedefType>())
+      if (Typedef->getDecl()->getQualifiedNameAsString() == "testing::Unused")
+        continue;
+
+    // Skip std::nullptr_t.
+    if (Parm->getType().getCanonicalType()->isNullPtrType())
+      continue;
+
+    // Look for comments. We explicitly want to allow idioms like
+    // void foo(int /*unused*/)
+    const char *Begin = SM.getCharacterData(Parm->getLocStart());
+    const char *End = SM.getCharacterData(Parm->getLocation());
+    StringRef Data(Begin, End - Begin);
+    if (Data.find("/*") != StringRef::npos)
+      continue;
+
+    UnnamedParams.push_back(std::make_pair(Function, I));
+  }
+
+  // Emit only one warning per function but fixits for all unnamed parameters.
+  if (!UnnamedParams.empty()) {
+    const ParmVarDecl *FirstParm =
+        UnnamedParams.front().first->getParamDecl(UnnamedParams.front().second);
+    auto D = diag(FirstParm->getLocation(),
+                  "all parameters should be named in a function");
+
+    for (auto P : UnnamedParams) {
+      // Fallback to an unused marker.
+      StringRef NewName = "unused";
+
+      // If the method is overridden, try to copy the name from the base method
+      // into the overrider.
+      const auto *M = dyn_cast<CXXMethodDecl>(P.first);
+      if (M && M->size_overridden_methods() > 0) {
+        const ParmVarDecl *OtherParm =
+            (*M->begin_overridden_methods())->getParamDecl(P.second);
+        StringRef Name = OtherParm->getName();
+        if (!Name.empty())
+          NewName = Name;
+      }
+
+      // If the definition has a named parameter use that name.
+      if (Definition) {
+        const ParmVarDecl *DefParm = Definition->getParamDecl(P.second);
+        StringRef Name = DefParm->getName();
+        if (!Name.empty())
+          NewName = Name;
+      }
+
+      // Now insert the comment. Note that getLocation() points to the place
+      // where the name would be, this allows us to also get complex cases like
+      // function pointers right.
+      const ParmVarDecl *Parm = P.first->getParamDecl(P.second);
+      D << FixItHint::CreateInsertion(Parm->getLocation(),
+                                      " /*" + NewName.str() + "*/");
+    }
+  }
+}
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/readability/NamedParameterCheck.h b/clang-tidy/readability/NamedParameterCheck.h
new file mode 100644 (file)
index 0000000..bd38ad2
--- /dev/null
@@ -0,0 +1,42 @@
+//===--- NamedParameterCheck.h - clang-tidy ---------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_NAMEDPARAMETERCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_NAMEDPARAMETERCHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+/// Find functions with unnamed arguments.
+///
+/// The check implements the following rule originating in the Google C++ Style
+/// Guide:
+///
+/// https://google.github.io/styleguide/cppguide.html#Function_Declarations_and_Definitions
+///
+/// All parameters should be named, with identical names in the declaration and
+/// implementation.
+///
+/// Corresponding cpplint.py check name: 'readability/function'.
+class NamedParameterCheck : public ClangTidyCheck {
+public:
+  NamedParameterCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_NAMEDPARAMETERCHECK_H
diff --git a/clang-tidy/readability/NamespaceCommentCheck.cpp b/clang-tidy/readability/NamespaceCommentCheck.cpp
new file mode 100644 (file)
index 0000000..d9e7112
--- /dev/null
@@ -0,0 +1,147 @@
+//===--- NamespaceCommentCheck.cpp - clang-tidy ---------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "NamespaceCommentCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Lex/Lexer.h"
+#include "llvm/ADT/StringExtras.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+NamespaceCommentCheck::NamespaceCommentCheck(StringRef Name,
+                                             ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      NamespaceCommentPattern("^/[/*] *(end (of )?)? *(anonymous|unnamed)? *"
+                              "namespace( +([a-zA-Z0-9_]+))?\\.? *(\\*/)?$",
+                              llvm::Regex::IgnoreCase),
+      ShortNamespaceLines(Options.get("ShortNamespaceLines", 1u)),
+      SpacesBeforeComments(Options.get("SpacesBeforeComments", 1u)) {}
+
+void NamespaceCommentCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "ShortNamespaceLines", ShortNamespaceLines);
+  Options.store(Opts, "SpacesBeforeComments", SpacesBeforeComments);
+}
+
+void NamespaceCommentCheck::registerMatchers(MatchFinder *Finder) {
+  // Only register the matchers for C++; the functionality currently does not
+  // provide any benefit to other languages, despite being benign.
+  if (getLangOpts().CPlusPlus)
+    Finder->addMatcher(namespaceDecl().bind("namespace"), this);
+}
+
+static bool locationsInSameFile(const SourceManager &Sources,
+                                SourceLocation Loc1, SourceLocation Loc2) {
+  return Loc1.isFileID() && Loc2.isFileID() &&
+         Sources.getFileID(Loc1) == Sources.getFileID(Loc2);
+}
+
+static std::string getNamespaceComment(const NamespaceDecl *ND,
+                                       bool InsertLineBreak) {
+  std::string Fix = "// namespace";
+  if (!ND->isAnonymousNamespace())
+    Fix.append(" ").append(ND->getNameAsString());
+  if (InsertLineBreak)
+    Fix.append("\n");
+  return Fix;
+}
+
+void NamespaceCommentCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *ND = Result.Nodes.getNodeAs<NamespaceDecl>("namespace");
+  const SourceManager &Sources = *Result.SourceManager;
+
+  if (!locationsInSameFile(Sources, ND->getLocStart(), ND->getRBraceLoc()))
+    return;
+
+  // Don't require closing comments for namespaces spanning less than certain
+  // number of lines.
+  unsigned StartLine = Sources.getSpellingLineNumber(ND->getLocStart());
+  unsigned EndLine = Sources.getSpellingLineNumber(ND->getRBraceLoc());
+  if (EndLine - StartLine + 1 <= ShortNamespaceLines)
+    return;
+
+  // Find next token after the namespace closing brace.
+  SourceLocation AfterRBrace = ND->getRBraceLoc().getLocWithOffset(1);
+  SourceLocation Loc = AfterRBrace;
+  Token Tok;
+  // Skip whitespace until we find the next token.
+  while (Lexer::getRawToken(Loc, Tok, Sources, getLangOpts()) ||
+         Tok.is(tok::semi)) {
+    Loc = Loc.getLocWithOffset(1);
+  }
+  if (!locationsInSameFile(Sources, ND->getRBraceLoc(), Loc))
+    return;
+
+  bool NextTokenIsOnSameLine = Sources.getSpellingLineNumber(Loc) == EndLine;
+  // If we insert a line comment before the token in the same line, we need
+  // to insert a line break.
+  bool NeedLineBreak = NextTokenIsOnSameLine && Tok.isNot(tok::eof);
+
+  SourceRange OldCommentRange(AfterRBrace, AfterRBrace);
+  std::string Message = "%0 not terminated with a closing comment";
+
+  // Try to find existing namespace closing comment on the same line.
+  if (Tok.is(tok::comment) && NextTokenIsOnSameLine) {
+    StringRef Comment(Sources.getCharacterData(Loc), Tok.getLength());
+    SmallVector<StringRef, 7> Groups;
+    if (NamespaceCommentPattern.match(Comment, &Groups)) {
+      StringRef NamespaceNameInComment = Groups.size() > 5 ? Groups[5] : "";
+      StringRef Anonymous = Groups.size() > 3 ? Groups[3] : "";
+
+      // Check if the namespace in the comment is the same.
+      if ((ND->isAnonymousNamespace() && NamespaceNameInComment.empty()) ||
+          (ND->getNameAsString() == NamespaceNameInComment &&
+           Anonymous.empty())) {
+        // FIXME: Maybe we need a strict mode, where we always fix namespace
+        // comments with different format.
+        return;
+      }
+
+      // Otherwise we need to fix the comment.
+      NeedLineBreak = Comment.startswith("/*");
+      OldCommentRange =
+          SourceRange(AfterRBrace, Loc.getLocWithOffset(Tok.getLength()));
+      Message =
+          (llvm::Twine(
+               "%0 ends with a comment that refers to a wrong namespace '") +
+           NamespaceNameInComment + "'")
+              .str();
+    } else if (Comment.startswith("//")) {
+      // Assume that this is an unrecognized form of a namespace closing line
+      // comment. Replace it.
+      NeedLineBreak = false;
+      OldCommentRange =
+          SourceRange(AfterRBrace, Loc.getLocWithOffset(Tok.getLength()));
+      Message = "%0 ends with an unrecognized comment";
+    }
+    // If it's a block comment, just move it to the next line, as it can be
+    // multi-line or there may be other tokens behind it.
+  }
+
+  std::string NamespaceName =
+      ND->isAnonymousNamespace()
+          ? "anonymous namespace"
+          : ("namespace '" + ND->getNameAsString() + "'");
+
+  diag(AfterRBrace, Message)
+      << NamespaceName << FixItHint::CreateReplacement(
+                              CharSourceRange::getCharRange(OldCommentRange),
+                              std::string(SpacesBeforeComments, ' ') +
+                                  getNamespaceComment(ND, NeedLineBreak));
+  diag(ND->getLocation(), "%0 starts here", DiagnosticIDs::Note)
+      << NamespaceName;
+}
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/readability/NamespaceCommentCheck.h b/clang-tidy/readability/NamespaceCommentCheck.h
new file mode 100644 (file)
index 0000000..87d97d5
--- /dev/null
@@ -0,0 +1,43 @@
+//===--- NamespaceCommentCheck.h - clang-tidy -------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_NAMESPACECOMMENTCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_NAMESPACECOMMENTCHECK_H
+
+#include "../ClangTidy.h"
+#include "llvm/Support/Regex.h"
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+/// Checks that long namespaces have a closing comment.
+///
+/// http://llvm.org/docs/CodingStandards.html#namespace-indentation
+///
+/// https://google.github.io/styleguide/cppguide.html#Namespaces
+class NamespaceCommentCheck : public ClangTidyCheck {
+public:
+  NamespaceCommentCheck(StringRef Name, ClangTidyContext *Context);
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+  void storeOptions(ClangTidyOptions::OptionMap &Options) override;
+
+  llvm::Regex NamespaceCommentPattern;
+  const unsigned ShortNamespaceLines;
+  const unsigned SpacesBeforeComments;
+};
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_NAMESPACECOMMENTCHECK_H
diff --git a/clang-tidy/readability/NonConstParameterCheck.cpp b/clang-tidy/readability/NonConstParameterCheck.cpp
new file mode 100644 (file)
index 0000000..5ef9644
--- /dev/null
@@ -0,0 +1,214 @@
+//===--- NonConstParameterCheck.cpp - clang-tidy---------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "NonConstParameterCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+void NonConstParameterCheck::registerMatchers(MatchFinder *Finder) {
+  // Add parameters to Parameters.
+  Finder->addMatcher(parmVarDecl(unless(isInstantiated())).bind("Parm"), this);
+
+  // C++ constructor.
+  Finder->addMatcher(cxxConstructorDecl().bind("Ctor"), this);
+
+  // Track unused parameters, there is Wunused-parameter about unused
+  // parameters.
+  Finder->addMatcher(declRefExpr().bind("Ref"), this);
+
+  // Analyse parameter usage in function.
+  Finder->addMatcher(stmt(anyOf(unaryOperator(anyOf(hasOperatorName("++"),
+                                                    hasOperatorName("--"))),
+                                binaryOperator(), callExpr(), returnStmt(),
+                                cxxConstructExpr()))
+                         .bind("Mark"),
+                     this);
+  Finder->addMatcher(varDecl(hasInitializer(anything())).bind("Mark"), this);
+}
+
+void NonConstParameterCheck::check(const MatchFinder::MatchResult &Result) {
+  if (const auto *Parm = Result.Nodes.getNodeAs<ParmVarDecl>("Parm")) {
+    if (const DeclContext *D = Parm->getParentFunctionOrMethod()) {
+      if (const auto *M = dyn_cast<CXXMethodDecl>(D)) {
+        if (M->isVirtual() || M->size_overridden_methods() != 0)
+          return;
+      }
+    }
+    addParm(Parm);
+  } else if (const auto *Ctor =
+                 Result.Nodes.getNodeAs<CXXConstructorDecl>("Ctor")) {
+    for (const auto *Parm : Ctor->parameters())
+      addParm(Parm);
+    for (const auto *Init : Ctor->inits())
+      markCanNotBeConst(Init->getInit(), true);
+  } else if (const auto *Ref = Result.Nodes.getNodeAs<DeclRefExpr>("Ref")) {
+    setReferenced(Ref);
+  } else if (const auto *S = Result.Nodes.getNodeAs<Stmt>("Mark")) {
+    if (const auto *B = dyn_cast<BinaryOperator>(S)) {
+      if (B->isAssignmentOp())
+        markCanNotBeConst(B, false);
+    } else if (const auto *CE = dyn_cast<CallExpr>(S)) {
+      // Typically, if a parameter is const then it is fine to make the data
+      // const. But sometimes the data is written even though the parameter
+      // is const. Mark all data passed by address to the function.
+      for (const auto *Arg : CE->arguments()) {
+        markCanNotBeConst(Arg->IgnoreParenCasts(), true);
+      }
+
+      // Data passed by nonconst reference should not be made const.
+      if (const FunctionDecl *FD = CE->getDirectCallee()) {
+        unsigned ArgNr = 0U;
+        for (const auto *Par : FD->parameters()) {
+          if (ArgNr >= CE->getNumArgs())
+            break;
+          const Expr *Arg = CE->getArg(ArgNr++);
+          // Is this a non constant reference parameter?
+          const Type *ParType = Par->getType().getTypePtr();
+          if (!ParType->isReferenceType() || Par->getType().isConstQualified())
+            continue;
+          markCanNotBeConst(Arg->IgnoreParenCasts(), false);
+        }
+      }
+    } else if (const auto *CE = dyn_cast<CXXConstructExpr>(S)) {
+      for (const auto *Arg : CE->arguments()) {
+        markCanNotBeConst(Arg->IgnoreParenCasts(), true);
+      }
+    } else if (const auto *R = dyn_cast<ReturnStmt>(S)) {
+      markCanNotBeConst(R->getRetValue(), true);
+    } else if (const auto *U = dyn_cast<UnaryOperator>(S)) {
+      markCanNotBeConst(U, true);
+    }
+  } else if (const auto *VD = Result.Nodes.getNodeAs<VarDecl>("Mark")) {
+    const QualType T = VD->getType();
+    if ((T->isPointerType() && !T->getPointeeType().isConstQualified()) ||
+        T->isArrayType())
+      markCanNotBeConst(VD->getInit(), true);
+  }
+}
+
+void NonConstParameterCheck::addParm(const ParmVarDecl *Parm) {
+  // Only add nonconst integer/float pointer parameters.
+  const QualType T = Parm->getType();
+  if (!T->isPointerType() || T->getPointeeType().isConstQualified() ||
+      !(T->getPointeeType()->isIntegerType() ||
+        T->getPointeeType()->isFloatingType()))
+    return;
+
+  if (Parameters.find(Parm) != Parameters.end())
+    return;
+
+  ParmInfo PI;
+  PI.IsReferenced = false;
+  PI.CanBeConst = true;
+  Parameters[Parm] = PI;
+}
+
+void NonConstParameterCheck::setReferenced(const DeclRefExpr *Ref) {
+  auto It = Parameters.find(dyn_cast<ParmVarDecl>(Ref->getDecl()));
+  if (It != Parameters.end())
+    It->second.IsReferenced = true;
+}
+
+void NonConstParameterCheck::onEndOfTranslationUnit() {
+  diagnoseNonConstParameters();
+}
+
+void NonConstParameterCheck::diagnoseNonConstParameters() {
+  for (const auto &It : Parameters) {
+    const ParmVarDecl *Par = It.first;
+    const ParmInfo &ParamInfo = It.second;
+
+    // Unused parameter => there are other warnings about this.
+    if (!ParamInfo.IsReferenced)
+      continue;
+
+    // Parameter can't be const.
+    if (!ParamInfo.CanBeConst)
+      continue;
+
+    diag(Par->getLocation(), "pointer parameter '%0' can be pointer to const")
+        << Par->getName()
+        << FixItHint::CreateInsertion(Par->getLocStart(), "const ");
+  }
+}
+
+void NonConstParameterCheck::markCanNotBeConst(const Expr *E,
+                                               bool CanNotBeConst) {
+  if (!E)
+    return;
+
+  if (const auto *Cast = dyn_cast<ImplicitCastExpr>(E)) {
+    // If expression is const then ignore usage.
+    const QualType T = Cast->getType();
+    if (T->isPointerType() && T->getPointeeType().isConstQualified())
+      return;
+  }
+
+  E = E->IgnoreParenCasts();
+
+  if (const auto *B = dyn_cast<BinaryOperator>(E)) {
+    if (B->isAdditiveOp()) {
+      // p + 2
+      markCanNotBeConst(B->getLHS(), CanNotBeConst);
+      markCanNotBeConst(B->getRHS(), CanNotBeConst);
+    } else if (B->isAssignmentOp()) {
+      markCanNotBeConst(B->getLHS(), false);
+
+      // If LHS is not const then RHS can't be const.
+      const QualType T = B->getLHS()->getType();
+      if (T->isPointerType() && !T->getPointeeType().isConstQualified())
+        markCanNotBeConst(B->getRHS(), true);
+    }
+  } else if (const auto *C = dyn_cast<ConditionalOperator>(E)) {
+    markCanNotBeConst(C->getTrueExpr(), CanNotBeConst);
+    markCanNotBeConst(C->getFalseExpr(), CanNotBeConst);
+  } else if (const auto *U = dyn_cast<UnaryOperator>(E)) {
+    if (U->getOpcode() == UO_PreInc || U->getOpcode() == UO_PreDec ||
+        U->getOpcode() == UO_PostInc || U->getOpcode() == UO_PostDec) {
+      if (const auto *SubU =
+              dyn_cast<UnaryOperator>(U->getSubExpr()->IgnoreParenCasts()))
+        markCanNotBeConst(SubU->getSubExpr(), true);
+      markCanNotBeConst(U->getSubExpr(), CanNotBeConst);
+    } else if (U->getOpcode() == UO_Deref) {
+      if (!CanNotBeConst)
+        markCanNotBeConst(U->getSubExpr(), true);
+    } else {
+      markCanNotBeConst(U->getSubExpr(), CanNotBeConst);
+    }
+  } else if (const auto *A = dyn_cast<ArraySubscriptExpr>(E)) {
+    markCanNotBeConst(A->getBase(), true);
+  } else if (const auto *CLE = dyn_cast<CompoundLiteralExpr>(E)) {
+    markCanNotBeConst(CLE->getInitializer(), true);
+  } else if (const auto *Constr = dyn_cast<CXXConstructExpr>(E)) {
+    for (const auto *Arg : Constr->arguments()) {
+      if (const auto *M = dyn_cast<MaterializeTemporaryExpr>(Arg))
+        markCanNotBeConst(cast<Expr>(M->getTemporary()), CanNotBeConst);
+    }
+  } else if (const auto *ILE = dyn_cast<InitListExpr>(E)) {
+    for (unsigned I = 0U; I < ILE->getNumInits(); ++I)
+      markCanNotBeConst(ILE->getInit(I), true);
+  } else if (CanNotBeConst) {
+    // Referencing parameter.
+    if (const auto *D = dyn_cast<DeclRefExpr>(E)) {
+      auto It = Parameters.find(dyn_cast<ParmVarDecl>(D->getDecl()));
+      if (It != Parameters.end())
+        It->second.CanBeConst = false;
+    }
+  }
+}
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/readability/NonConstParameterCheck.h b/clang-tidy/readability/NonConstParameterCheck.h
new file mode 100644 (file)
index 0000000..3cc73e9
--- /dev/null
@@ -0,0 +1,64 @@
+//===--- NonConstParameterCheck.h - clang-tidy-------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_NON_CONST_PARAMETER_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_NON_CONST_PARAMETER_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+/// Warn when a pointer function parameter can be const.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability-non-const-parameter.html
+class NonConstParameterCheck : public ClangTidyCheck {
+public:
+  NonConstParameterCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+  void onEndOfTranslationUnit() override;
+
+private:
+  /// Parameter info.
+  struct ParmInfo {
+    /// Is function parameter referenced?
+    bool IsReferenced;
+
+    /// Can function parameter be const?
+    bool CanBeConst;
+  };
+
+  /// Track all nonconst integer/float parameters.
+  std::map<const ParmVarDecl *, ParmInfo> Parameters;
+
+  /// Add function parameter.
+  void addParm(const ParmVarDecl *Parm);
+
+  /// Set IsReferenced.
+  void setReferenced(const DeclRefExpr *Ref);
+
+  /// Set CanNotBeConst.
+  /// Visits sub expressions recursively. If a DeclRefExpr is found
+  /// and CanNotBeConst is true the Parameter is marked as not-const.
+  /// The CanNotBeConst is updated as sub expressions are visited.
+  void markCanNotBeConst(const Expr *E, bool CanNotBeConst);
+
+  /// Diagnose non const parameters.
+  void diagnoseNonConstParameters();
+};
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_NON_CONST_PARAMETER_H
diff --git a/clang-tidy/readability/ReadabilityTidyModule.cpp b/clang-tidy/readability/ReadabilityTidyModule.cpp
new file mode 100644 (file)
index 0000000..dacea0d
--- /dev/null
@@ -0,0 +1,107 @@
+//===--- ReadabilityTidyModule.cpp - clang-tidy ---------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "../ClangTidy.h"
+#include "../ClangTidyModule.h"
+#include "../ClangTidyModuleRegistry.h"
+#include "AvoidConstParamsInDecls.h"
+#include "BracesAroundStatementsCheck.h"
+#include "ContainerSizeEmptyCheck.h"
+#include "DeleteNullPointerCheck.h"
+#include "DeletedDefaultCheck.h"
+#include "ElseAfterReturnCheck.h"
+#include "FunctionSizeCheck.h"
+#include "IdentifierNamingCheck.h"
+#include "ImplicitBoolCastCheck.h"
+#include "InconsistentDeclarationParameterNameCheck.h"
+#include "MisleadingIndentationCheck.h"
+#include "MisplacedArrayIndexCheck.h"
+#include "NamedParameterCheck.h"
+#include "NonConstParameterCheck.h"
+#include "RedundantControlFlowCheck.h"
+#include "RedundantDeclarationCheck.h"
+#include "RedundantFunctionPtrDereferenceCheck.h"
+#include "RedundantMemberInitCheck.h"
+#include "RedundantSmartptrGetCheck.h"
+#include "RedundantStringCStrCheck.h"
+#include "RedundantStringInitCheck.h"
+#include "SimplifyBooleanExprCheck.h"
+#include "StaticDefinitionInAnonymousNamespaceCheck.h"
+#include "UniqueptrDeleteReleaseCheck.h"
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+class ReadabilityModule : public ClangTidyModule {
+public:
+  void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
+    CheckFactories.registerCheck<AvoidConstParamsInDecls>(
+        "readability-avoid-const-params-in-decls");
+    CheckFactories.registerCheck<BracesAroundStatementsCheck>(
+        "readability-braces-around-statements");
+    CheckFactories.registerCheck<ContainerSizeEmptyCheck>(
+        "readability-container-size-empty");
+    CheckFactories.registerCheck<DeleteNullPointerCheck>(
+        "readability-delete-null-pointer");
+    CheckFactories.registerCheck<DeletedDefaultCheck>(
+        "readability-deleted-default");
+    CheckFactories.registerCheck<ElseAfterReturnCheck>(
+        "readability-else-after-return");
+    CheckFactories.registerCheck<FunctionSizeCheck>(
+        "readability-function-size");
+    CheckFactories.registerCheck<IdentifierNamingCheck>(
+        "readability-identifier-naming");
+    CheckFactories.registerCheck<ImplicitBoolCastCheck>(
+        "readability-implicit-bool-cast");
+    CheckFactories.registerCheck<InconsistentDeclarationParameterNameCheck>(
+        "readability-inconsistent-declaration-parameter-name");
+    CheckFactories.registerCheck<MisleadingIndentationCheck>(
+        "readability-misleading-indentation");
+    CheckFactories.registerCheck<MisplacedArrayIndexCheck>(
+        "readability-misplaced-array-index");
+    CheckFactories.registerCheck<RedundantFunctionPtrDereferenceCheck>(
+        "readability-redundant-function-ptr-dereference");
+    CheckFactories.registerCheck<RedundantMemberInitCheck>(
+        "readability-redundant-member-init");
+    CheckFactories.registerCheck<StaticDefinitionInAnonymousNamespaceCheck>(
+        "readability-static-definition-in-anonymous-namespace");
+    CheckFactories.registerCheck<readability::NamedParameterCheck>(
+        "readability-named-parameter");
+    CheckFactories.registerCheck<NonConstParameterCheck>(
+        "readability-non-const-parameter");
+    CheckFactories.registerCheck<RedundantControlFlowCheck>(
+        "readability-redundant-control-flow");
+    CheckFactories.registerCheck<RedundantDeclarationCheck>(
+        "readability-redundant-declaration");
+    CheckFactories.registerCheck<RedundantSmartptrGetCheck>(
+        "readability-redundant-smartptr-get");
+    CheckFactories.registerCheck<RedundantStringCStrCheck>(
+        "readability-redundant-string-cstr");
+    CheckFactories.registerCheck<RedundantStringInitCheck>(
+        "readability-redundant-string-init");
+    CheckFactories.registerCheck<SimplifyBooleanExprCheck>(
+        "readability-simplify-boolean-expr");
+    CheckFactories.registerCheck<UniqueptrDeleteReleaseCheck>(
+        "readability-uniqueptr-delete-release");
+  }
+};
+
+// Register the ReadabilityModule using this statically initialized variable.
+static ClangTidyModuleRegistry::Add<ReadabilityModule>
+    X("readability-module", "Adds readability-related checks.");
+
+} // namespace readability
+
+// This anchor is used to force the linker to link in the generated object file
+// and thus register the ReadabilityModule.
+volatile int ReadabilityModuleAnchorSource = 0;
+
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/readability/RedundantControlFlowCheck.cpp b/clang-tidy/readability/RedundantControlFlowCheck.cpp
new file mode 100644 (file)
index 0000000..0788c42
--- /dev/null
@@ -0,0 +1,98 @@
+//===--- RedundantControlFlowCheck.cpp - clang-tidy------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "RedundantControlFlowCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+namespace {
+
+const char *const RedundantReturnDiag = "redundant return statement at the end "
+                                        "of a function with a void return type";
+const char *const RedundantContinueDiag = "redundant continue statement at the "
+                                          "end of loop statement";
+
+bool isLocationInMacroExpansion(const SourceManager &SM, SourceLocation Loc) {
+  return SM.isMacroBodyExpansion(Loc) || SM.isMacroArgExpansion(Loc);
+}
+
+} // namespace
+
+void RedundantControlFlowCheck::registerMatchers(MatchFinder *Finder) {
+  Finder->addMatcher(
+      functionDecl(
+          isDefinition(), returns(voidType()),
+          has(compoundStmt(hasAnySubstatement(returnStmt(unless(has(expr())))))
+                  .bind("return"))),
+      this);
+  auto CompoundContinue =
+      has(compoundStmt(hasAnySubstatement(continueStmt())).bind("continue"));
+  Finder->addMatcher(
+      stmt(anyOf(forStmt(), cxxForRangeStmt(), whileStmt(), doStmt()),
+           CompoundContinue),
+      this);
+}
+
+void RedundantControlFlowCheck::check(const MatchFinder::MatchResult &Result) {
+  if (const auto *Return = Result.Nodes.getNodeAs<CompoundStmt>("return"))
+    checkRedundantReturn(Result, Return);
+  else if (const auto *Continue =
+               Result.Nodes.getNodeAs<CompoundStmt>("continue"))
+    checkRedundantContinue(Result, Continue);
+}
+
+void RedundantControlFlowCheck::checkRedundantReturn(
+    const MatchFinder::MatchResult &Result, const CompoundStmt *Block) {
+  CompoundStmt::const_reverse_body_iterator last = Block->body_rbegin();
+  if (const auto *Return = dyn_cast<ReturnStmt>(*last))
+    issueDiagnostic(Result, Block, Return->getSourceRange(),
+                    RedundantReturnDiag);
+}
+
+void RedundantControlFlowCheck::checkRedundantContinue(
+    const MatchFinder::MatchResult &Result, const CompoundStmt *Block) {
+  CompoundStmt::const_reverse_body_iterator last = Block->body_rbegin();
+  if (const auto *Continue = dyn_cast<ContinueStmt>(*last))
+    issueDiagnostic(Result, Block, Continue->getSourceRange(),
+                    RedundantContinueDiag);
+}
+
+void RedundantControlFlowCheck::issueDiagnostic(
+    const MatchFinder::MatchResult &Result, const CompoundStmt *const Block,
+    const SourceRange &StmtRange, const char *const Diag) {
+  SourceManager &SM = *Result.SourceManager;
+  if (isLocationInMacroExpansion(SM, StmtRange.getBegin()))
+    return;
+
+  CompoundStmt::const_reverse_body_iterator Previous = ++Block->body_rbegin();
+  SourceLocation Start;
+  if (Previous != Block->body_rend())
+    Start = Lexer::findLocationAfterToken(
+        dyn_cast<Stmt>(*Previous)->getLocEnd(), tok::semi, SM, getLangOpts(),
+        /*SkipTrailingWhitespaceAndNewLine=*/true);
+  if (!Start.isValid())
+    Start = StmtRange.getBegin();
+  auto RemovedRange = CharSourceRange::getCharRange(
+      Start, Lexer::findLocationAfterToken(
+                 StmtRange.getEnd(), tok::semi, SM, getLangOpts(),
+                 /*SkipTrailingWhitespaceAndNewLine=*/true));
+
+  diag(StmtRange.getBegin(), Diag) << FixItHint::CreateRemoval(RemovedRange);
+}
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/readability/RedundantControlFlowCheck.h b/clang-tidy/readability/RedundantControlFlowCheck.h
new file mode 100644 (file)
index 0000000..4b8b6fb
--- /dev/null
@@ -0,0 +1,51 @@
+//===--- RedundantControlFlowCheck.h - clang-tidy----------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANT_CONTROL_FLOW_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANT_CONTROL_FLOW_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+/// Eliminates redundant `return` statements at the end of a function that
+/// returns `void`.
+///
+/// Eliminates redundant `continue` statements at the end of a loop body.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability-redundant-control-flow.html
+class RedundantControlFlowCheck : public ClangTidyCheck {
+public:
+  RedundantControlFlowCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+  void
+  checkRedundantReturn(const ast_matchers::MatchFinder::MatchResult &Result,
+                       const CompoundStmt *Block);
+
+  void
+  checkRedundantContinue(const ast_matchers::MatchFinder::MatchResult &Result,
+                         const CompoundStmt *Block);
+
+  void issueDiagnostic(const ast_matchers::MatchFinder::MatchResult &Result,
+                       const CompoundStmt *Block, const SourceRange &StmtRange,
+                       const char *Diag);
+};
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANT_CONTROL_FLOW_H
diff --git a/clang-tidy/readability/RedundantDeclarationCheck.cpp b/clang-tidy/readability/RedundantDeclarationCheck.cpp
new file mode 100644 (file)
index 0000000..29d43c7
--- /dev/null
@@ -0,0 +1,70 @@
+//===--- RedundantDeclarationCheck.cpp - clang-tidy------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "RedundantDeclarationCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+void RedundantDeclarationCheck::registerMatchers(MatchFinder *Finder) {
+  Finder->addMatcher(
+      namedDecl(
+          anyOf(varDecl(unless(isDefinition())),
+                functionDecl(unless(anyOf(isDefinition(), isDefaulted())))))
+          .bind("Decl"),
+      this);
+}
+
+void RedundantDeclarationCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *D = Result.Nodes.getNodeAs<NamedDecl>("Decl");
+  const auto *Prev = D->getPreviousDecl();
+  if (!Prev)
+    return;
+  if (!Prev->getLocation().isValid())
+    return;
+  if (Prev->getLocation() == D->getLocation())
+    return;
+
+  const SourceManager &SM = *Result.SourceManager;
+
+  const bool DifferentHeaders =
+      !SM.isInMainFile(D->getLocation()) &&
+      !SM.isWrittenInSameFile(Prev->getLocation(), D->getLocation());
+
+  bool MultiVar = false;
+  if (const auto *VD = dyn_cast<VarDecl>(D)) {
+    // Is this a multivariable declaration?
+    for (const auto Other : VD->getDeclContext()->decls()) {
+      if (Other != D && Other->getLocStart() == VD->getLocStart()) {
+        MultiVar = true;
+        break;
+      }
+    }
+  }
+
+  SourceLocation EndLoc = Lexer::getLocForEndOfToken(
+      D->getSourceRange().getEnd(), 0, SM, Result.Context->getLangOpts());
+  {
+    auto Diag = diag(D->getLocation(), "redundant %0 declaration") << D;
+    if (!MultiVar && !DifferentHeaders)
+      Diag << FixItHint::CreateRemoval(
+          SourceRange(D->getSourceRange().getBegin(), EndLoc));
+  }
+  diag(Prev->getLocation(), "previously declared here", DiagnosticIDs::Note);
+}
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/readability/RedundantDeclarationCheck.h b/clang-tidy/readability/RedundantDeclarationCheck.h
new file mode 100644 (file)
index 0000000..96c4830
--- /dev/null
@@ -0,0 +1,35 @@
+//===--- RedundantDeclarationCheck.h - clang-tidy----------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANT_DECLARATION_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANT_DECLARATION_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+/// Find redundant variable declarations.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability-redundant-declaration.html
+class RedundantDeclarationCheck : public ClangTidyCheck {
+public:
+  RedundantDeclarationCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANT_DECLARATION_H
diff --git a/clang-tidy/readability/RedundantFunctionPtrDereferenceCheck.cpp b/clang-tidy/readability/RedundantFunctionPtrDereferenceCheck.cpp
new file mode 100644 (file)
index 0000000..fa503c5
--- /dev/null
@@ -0,0 +1,37 @@
+//===--- RedundantFunctionPtrDereferenceCheck.cpp - clang-tidy-------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "RedundantFunctionPtrDereferenceCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+void RedundantFunctionPtrDereferenceCheck::registerMatchers(MatchFinder *Finder) {
+  Finder->addMatcher(unaryOperator(hasOperatorName("*"),
+                                   has(implicitCastExpr(
+                                       hasCastKind(CK_FunctionToPointerDecay))))
+                         .bind("op"),
+                     this);
+}
+
+void RedundantFunctionPtrDereferenceCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *Operator = Result.Nodes.getNodeAs<UnaryOperator>("op");
+  diag(Operator->getOperatorLoc(),
+       "redundant repeated dereference of function pointer")
+      << FixItHint::CreateRemoval(Operator->getOperatorLoc());
+}
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/readability/RedundantFunctionPtrDereferenceCheck.h b/clang-tidy/readability/RedundantFunctionPtrDereferenceCheck.h
new file mode 100644 (file)
index 0000000..4cf6d11
--- /dev/null
@@ -0,0 +1,35 @@
+//===--- RedundantFunctionPtrDereferenceCheck.h - clang-tidy-----*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANT_FUNCTION_PTR_DEREFERENCE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANT_FUNCTION_PTR_DEREFERENCE_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+/// Eliminate redundant dereferences of a function pointer.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability-redundant-function-ptr-dereference.html
+class RedundantFunctionPtrDereferenceCheck : public ClangTidyCheck {
+public:
+  RedundantFunctionPtrDereferenceCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANT_FUNCTION_PTR_DEREFERENCE_H
diff --git a/clang-tidy/readability/RedundantMemberInitCheck.cpp b/clang-tidy/readability/RedundantMemberInitCheck.cpp
new file mode 100644 (file)
index 0000000..6d36500
--- /dev/null
@@ -0,0 +1,68 @@
+//===--- RedundantMemberInitCheck.cpp - clang-tidy-------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "RedundantMemberInitCheck.h"
+#include "../utils/Matchers.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+#include <algorithm>
+
+using namespace clang::ast_matchers;
+using namespace clang::tidy::matchers;
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+void RedundantMemberInitCheck::registerMatchers(MatchFinder *Finder) {
+  if (!getLangOpts().CPlusPlus)
+    return;
+
+  auto Construct =
+      cxxConstructExpr(
+          hasDeclaration(cxxConstructorDecl(hasParent(
+              cxxRecordDecl(unless(isTriviallyDefaultConstructible()))))))
+          .bind("construct");
+
+  Finder->addMatcher(
+      cxxConstructorDecl(
+          unless(isDelegatingConstructor()),
+          ofClass(unless(
+              anyOf(isUnion(), ast_matchers::isTemplateInstantiation()))),
+          forEachConstructorInitializer(
+              cxxCtorInitializer(isWritten(),
+                                 withInitializer(ignoringImplicit(Construct)),
+                                 unless(forField(hasType(isConstQualified()))))
+                  .bind("init"))),
+      this);
+}
+
+void RedundantMemberInitCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *Init = Result.Nodes.getNodeAs<CXXCtorInitializer>("init");
+  const auto *Construct = Result.Nodes.getNodeAs<CXXConstructExpr>("construct");
+
+  if (Construct->getNumArgs() == 0 ||
+      Construct->getArg(0)->isDefaultArgument()) {
+    if (Init->isAnyMemberInitializer()) {
+      diag(Init->getSourceLocation(), "initializer for member %0 is redundant")
+          << Init->getMember()
+          << FixItHint::CreateRemoval(Init->getSourceRange());
+    } else {
+      diag(Init->getSourceLocation(),
+           "initializer for base class %0 is redundant")
+          << Construct->getType()
+          << FixItHint::CreateRemoval(Init->getSourceRange());
+    }
+  }
+}
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/readability/RedundantMemberInitCheck.h b/clang-tidy/readability/RedundantMemberInitCheck.h
new file mode 100644 (file)
index 0000000..13cc9d3
--- /dev/null
@@ -0,0 +1,36 @@
+//===--- RedundantMemberInitCheck.h - clang-tidy----------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANT_MEMBER_INIT_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANT_MEMBER_INIT_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+/// Finds member initializations that are unnecessary because the same default
+/// constructor would be called if they were not present.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability-redundant-member-init.html
+class RedundantMemberInitCheck : public ClangTidyCheck {
+public:
+  RedundantMemberInitCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANT_MEMBER_INIT_H
diff --git a/clang-tidy/readability/RedundantSmartptrGetCheck.cpp b/clang-tidy/readability/RedundantSmartptrGetCheck.cpp
new file mode 100644 (file)
index 0000000..dd6866f
--- /dev/null
@@ -0,0 +1,138 @@
+//===--- RedundantSmartptrGetCheck.cpp - clang-tidy -----------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "RedundantSmartptrGetCheck.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+namespace {
+internal::Matcher<Expr> callToGet(const internal::Matcher<Decl> &OnClass) {
+  return cxxMemberCallExpr(
+             on(expr(anyOf(hasType(OnClass),
+                           hasType(qualType(
+                               pointsTo(decl(OnClass).bind("ptr_to_ptr"))))))
+                    .bind("smart_pointer")),
+             unless(callee(memberExpr(hasObjectExpression(cxxThisExpr())))),
+             callee(cxxMethodDecl(
+                 hasName("get"),
+                 returns(qualType(pointsTo(type().bind("getType")))))))
+      .bind("redundant_get");
+}
+
+void registerMatchersForGetArrowStart(MatchFinder *Finder,
+                                      MatchFinder::MatchCallback *Callback) {
+  const auto QuacksLikeASmartptr = recordDecl(
+      recordDecl().bind("duck_typing"),
+      has(cxxMethodDecl(hasName("operator->"),
+                        returns(qualType(pointsTo(type().bind("op->Type")))))),
+      has(cxxMethodDecl(hasName("operator*"), returns(qualType(references(
+                                                  type().bind("op*Type")))))));
+
+  // Catch 'ptr.get()->Foo()'
+  Finder->addMatcher(memberExpr(expr().bind("memberExpr"), isArrow(),
+                                hasObjectExpression(ignoringImpCasts(
+                                    callToGet(QuacksLikeASmartptr)))),
+                     Callback);
+
+  // Catch '*ptr.get()' or '*ptr->get()'
+  Finder->addMatcher(
+      unaryOperator(hasOperatorName("*"),
+                    hasUnaryOperand(callToGet(QuacksLikeASmartptr))),
+      Callback);
+}
+
+void registerMatchersForGetEquals(MatchFinder *Finder,
+                                  MatchFinder::MatchCallback *Callback) {
+  // This one is harder to do with duck typing.
+  // The operator==/!= that we are looking for might be member or non-member,
+  // might be on global namespace or found by ADL, might be a template, etc.
+  // For now, lets keep a list of known standard types.
+
+  const auto IsAKnownSmartptr =
+      recordDecl(hasAnyName("::std::unique_ptr", "::std::shared_ptr"));
+
+  // Matches against nullptr.
+  Finder->addMatcher(
+      binaryOperator(anyOf(hasOperatorName("=="), hasOperatorName("!=")),
+                     hasEitherOperand(ignoringImpCasts(
+                         anyOf(cxxNullPtrLiteralExpr(), gnuNullExpr(),
+                               integerLiteral(equals(0))))),
+                     hasEitherOperand(callToGet(IsAKnownSmartptr))),
+      Callback);
+
+  // Matches against if(ptr.get())
+  Finder->addMatcher(
+      ifStmt(hasCondition(ignoringImpCasts(callToGet(IsAKnownSmartptr)))),
+      Callback);
+
+  // FIXME: Match and fix if (l.get() == r.get()).
+}
+
+} // namespace
+
+void RedundantSmartptrGetCheck::registerMatchers(MatchFinder *Finder) {
+  // Only register the matchers for C++; the functionality currently does not
+  // provide any benefit to other languages, despite being benign.
+  if (!getLangOpts().CPlusPlus)
+    return;
+
+  registerMatchersForGetArrowStart(Finder, this);
+  registerMatchersForGetEquals(Finder, this);
+}
+
+namespace {
+bool allReturnTypesMatch(const MatchFinder::MatchResult &Result) {
+  if (Result.Nodes.getNodeAs<Decl>("duck_typing") == nullptr)
+    return true;
+  // Verify that the types match.
+  // We can't do this on the matcher because the type nodes can be different,
+  // even though they represent the same type. This difference comes from how
+  // the type is referenced (eg. through a typedef, a type trait, etc).
+  const Type *OpArrowType =
+      Result.Nodes.getNodeAs<Type>("op->Type")->getUnqualifiedDesugaredType();
+  const Type *OpStarType =
+      Result.Nodes.getNodeAs<Type>("op*Type")->getUnqualifiedDesugaredType();
+  const Type *GetType =
+      Result.Nodes.getNodeAs<Type>("getType")->getUnqualifiedDesugaredType();
+  return OpArrowType == OpStarType && OpArrowType == GetType;
+}
+} // namespace
+
+void RedundantSmartptrGetCheck::check(const MatchFinder::MatchResult &Result) {
+  if (!allReturnTypesMatch(Result))
+    return;
+
+  bool IsPtrToPtr = Result.Nodes.getNodeAs<Decl>("ptr_to_ptr") != nullptr;
+  bool IsMemberExpr = Result.Nodes.getNodeAs<Expr>("memberExpr") != nullptr;
+  const auto *GetCall = Result.Nodes.getNodeAs<Expr>("redundant_get");
+  const auto *Smartptr = Result.Nodes.getNodeAs<Expr>("smart_pointer");
+
+  if (IsPtrToPtr && IsMemberExpr) {
+    // Ignore this case (eg. Foo->get()->DoSomething());
+    return;
+  }
+
+  StringRef SmartptrText = Lexer::getSourceText(
+      CharSourceRange::getTokenRange(Smartptr->getSourceRange()),
+      *Result.SourceManager, getLangOpts());
+  // Replace foo->get() with *foo, and foo.get() with foo.
+  std::string Replacement = Twine(IsPtrToPtr ? "*" : "", SmartptrText).str();
+  diag(GetCall->getLocStart(), "redundant get() call on smart pointer")
+      << FixItHint::CreateReplacement(GetCall->getSourceRange(), Replacement);
+}
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/readability/RedundantSmartptrGetCheck.h b/clang-tidy/readability/RedundantSmartptrGetCheck.h
new file mode 100644 (file)
index 0000000..1da6195
--- /dev/null
@@ -0,0 +1,40 @@
+//===--- RedundantSmartptrGetCheck.h - clang-tidy ---------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANTSMARTPTRGETCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANTSMARTPTRGETCHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+/// Find and remove redundant calls to smart pointer's `.get()` method.
+///
+/// Examples:
+///
+/// \code
+///   ptr.get()->Foo()  ==>  ptr->Foo()
+///   *ptr.get()  ==>  *ptr
+///   *ptr->get()  ==>  **ptr
+/// \endcode
+class RedundantSmartptrGetCheck : public ClangTidyCheck {
+public:
+  RedundantSmartptrGetCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANTSMARTPTRGETCHECK_H
diff --git a/clang-tidy/readability/RedundantStringCStrCheck.cpp b/clang-tidy/readability/RedundantStringCStrCheck.cpp
new file mode 100644 (file)
index 0000000..c6b384b
--- /dev/null
@@ -0,0 +1,198 @@
+//===- RedundantStringCStrCheck.cpp - Check for redundant c_str calls -----===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+//  This file implements a check for redundant calls of c_str() on strings.
+//
+//===----------------------------------------------------------------------===//
+
+#include "RedundantStringCStrCheck.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+namespace {
+
+template <typename T>
+StringRef getText(const ast_matchers::MatchFinder::MatchResult &Result,
+                  T const &Node) {
+  return Lexer::getSourceText(
+      CharSourceRange::getTokenRange(Node.getSourceRange()),
+      *Result.SourceManager, Result.Context->getLangOpts());
+}
+
+// Return true if expr needs to be put in parens when it is an argument of a
+// prefix unary operator, e.g. when it is a binary or ternary operator
+// syntactically.
+bool needParensAfterUnaryOperator(const Expr &ExprNode) {
+  if (isa<clang::BinaryOperator>(&ExprNode) ||
+      isa<clang::ConditionalOperator>(&ExprNode)) {
+    return true;
+  }
+  if (const auto *Op = dyn_cast<CXXOperatorCallExpr>(&ExprNode)) {
+    return Op->getNumArgs() == 2 && Op->getOperator() != OO_PlusPlus &&
+           Op->getOperator() != OO_MinusMinus && Op->getOperator() != OO_Call &&
+           Op->getOperator() != OO_Subscript;
+  }
+  return false;
+}
+
+// Format a pointer to an expression: prefix with '*' but simplify
+// when it already begins with '&'.  Return empty string on failure.
+std::string
+formatDereference(const ast_matchers::MatchFinder::MatchResult &Result,
+                  const Expr &ExprNode) {
+  if (const auto *Op = dyn_cast<clang::UnaryOperator>(&ExprNode)) {
+    if (Op->getOpcode() == UO_AddrOf) {
+      // Strip leading '&'.
+      return getText(Result, *Op->getSubExpr()->IgnoreParens());
+    }
+  }
+  StringRef Text = getText(Result, ExprNode);
+  if (Text.empty())
+    return std::string();
+  // Add leading '*'.
+  if (needParensAfterUnaryOperator(ExprNode)) {
+    return (llvm::Twine("*(") + Text + ")").str();
+  }
+  return (llvm::Twine("*") + Text).str();
+}
+
+} // end namespace
+
+void RedundantStringCStrCheck::registerMatchers(
+    ast_matchers::MatchFinder *Finder) {
+  // Only register the matchers for C++; the functionality currently does not
+  // provide any benefit to other languages, despite being benign.
+  if (!getLangOpts().CPlusPlus)
+    return;
+
+  // Match expressions of type 'string' or 'string*'.
+  const auto StringDecl = cxxRecordDecl(hasName("::std::basic_string"));
+  const auto StringExpr =
+      expr(anyOf(hasType(StringDecl), hasType(qualType(pointsTo(StringDecl)))));
+
+  // Match string constructor.
+  const auto StringConstructorExpr = expr(anyOf(
+      cxxConstructExpr(argumentCountIs(1),
+                       hasDeclaration(cxxMethodDecl(hasName("basic_string")))),
+      cxxConstructExpr(
+          argumentCountIs(2),
+          hasDeclaration(cxxMethodDecl(hasName("basic_string"))),
+          // If present, the second argument is the alloc object which must not
+          // be present explicitly.
+          hasArgument(1, cxxDefaultArgExpr()))));
+
+  // Match a call to the string 'c_str()' method.
+  const auto StringCStrCallExpr =
+      cxxMemberCallExpr(on(StringExpr.bind("arg")),
+                        callee(memberExpr().bind("member")),
+                        callee(cxxMethodDecl(hasAnyName("c_str", "data"))))
+          .bind("call");
+
+  // Detect redundant 'c_str()' calls through a string constructor.
+  Finder->addMatcher(cxxConstructExpr(StringConstructorExpr,
+                                      hasArgument(0, StringCStrCallExpr)),
+                     this);
+
+  // Detect: 's == str.c_str()'  ->  's == str'
+  Finder->addMatcher(
+      cxxOperatorCallExpr(
+          anyOf(
+              hasOverloadedOperatorName("<"), hasOverloadedOperatorName(">"),
+              hasOverloadedOperatorName(">="), hasOverloadedOperatorName("<="),
+              hasOverloadedOperatorName("!="), hasOverloadedOperatorName("=="),
+              hasOverloadedOperatorName("+")),
+          anyOf(allOf(hasArgument(0, StringExpr),
+                      hasArgument(1, StringCStrCallExpr)),
+                allOf(hasArgument(0, StringCStrCallExpr),
+                      hasArgument(1, StringExpr)))),
+      this);
+
+  // Detect: 'dst += str.c_str()'  ->  'dst += str'
+  // Detect: 's = str.c_str()'  ->  's = str'
+  Finder->addMatcher(cxxOperatorCallExpr(anyOf(hasOverloadedOperatorName("="),
+                                               hasOverloadedOperatorName("+=")),
+                                         hasArgument(0, StringExpr),
+                                         hasArgument(1, StringCStrCallExpr)),
+                     this);
+
+  // Detect: 'dst.append(str.c_str())'  ->  'dst.append(str)'
+  Finder->addMatcher(
+      cxxMemberCallExpr(on(StringExpr), callee(decl(cxxMethodDecl(hasAnyName(
+                                            "append", "assign", "compare")))),
+                        argumentCountIs(1), hasArgument(0, StringCStrCallExpr)),
+      this);
+
+  // Detect: 'dst.compare(p, n, str.c_str())'  ->  'dst.compare(p, n, str)'
+  Finder->addMatcher(
+      cxxMemberCallExpr(on(StringExpr),
+                        callee(decl(cxxMethodDecl(hasName("compare")))),
+                        argumentCountIs(3), hasArgument(2, StringCStrCallExpr)),
+      this);
+
+  // Detect: 'dst.find(str.c_str())'  ->  'dst.find(str)'
+  Finder->addMatcher(
+      cxxMemberCallExpr(on(StringExpr),
+                        callee(decl(cxxMethodDecl(hasAnyName(
+                            "find", "find_first_not_of", "find_first_of",
+                            "find_last_not_of", "find_last_of", "rfind")))),
+                        anyOf(argumentCountIs(1), argumentCountIs(2)),
+                        hasArgument(0, StringCStrCallExpr)),
+      this);
+
+  // Detect: 'dst.insert(pos, str.c_str())'  ->  'dst.insert(pos, str)'
+  Finder->addMatcher(
+      cxxMemberCallExpr(on(StringExpr),
+                        callee(decl(cxxMethodDecl(hasName("insert")))),
+                        argumentCountIs(2), hasArgument(1, StringCStrCallExpr)),
+      this);
+
+  // Detect redundant 'c_str()' calls through a StringRef constructor.
+  Finder->addMatcher(
+      cxxConstructExpr(
+          // Implicit constructors of these classes are overloaded
+          // wrt. string types and they internally make a StringRef
+          // referring to the argument.  Passing a string directly to
+          // them is preferred to passing a char pointer.
+          hasDeclaration(cxxMethodDecl(hasAnyName(
+              "::llvm::StringRef::StringRef", "::llvm::Twine::Twine"))),
+          argumentCountIs(1),
+          // The only argument must have the form x.c_str() or p->c_str()
+          // where the method is string::c_str().  StringRef also has
+          // a constructor from string which is more efficient (avoids
+          // strlen), so we can construct StringRef from the string
+          // directly.
+          hasArgument(0, StringCStrCallExpr)),
+      this);
+}
+
+void RedundantStringCStrCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *Call = Result.Nodes.getNodeAs<CallExpr>("call");
+  const auto *Arg = Result.Nodes.getNodeAs<Expr>("arg");
+  const auto *Member = Result.Nodes.getNodeAs<MemberExpr>("member");
+  bool Arrow = Member->isArrow();
+  // Replace the "call" node with the "arg" node, prefixed with '*'
+  // if the call was using '->' rather than '.'.
+  std::string ArgText =
+      Arrow ? formatDereference(Result, *Arg) : getText(Result, *Arg).str();
+  if (ArgText.empty())
+    return;
+
+  diag(Call->getLocStart(), "redundant call to %0")
+      << Member->getMemberDecl()
+      << FixItHint::CreateReplacement(Call->getSourceRange(), ArgText);
+}
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/readability/RedundantStringCStrCheck.h b/clang-tidy/readability/RedundantStringCStrCheck.h
new file mode 100644 (file)
index 0000000..9406f8e
--- /dev/null
@@ -0,0 +1,32 @@
+//===--- RedundantStringCStrCheck.h - clang-tidy ----------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANTSTRINGCSTRCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANTSTRINGCSTRCHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+/// Finds unnecessary calls to `std::string::c_str()`.
+class RedundantStringCStrCheck : public ClangTidyCheck {
+public:
+  RedundantStringCStrCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANTSTRINGCSTRCHECK_H
diff --git a/clang-tidy/readability/RedundantStringInitCheck.cpp b/clang-tidy/readability/RedundantStringInitCheck.cpp
new file mode 100644 (file)
index 0000000..b881e22
--- /dev/null
@@ -0,0 +1,70 @@
+//===- RedundantStringInitCheck.cpp - clang-tidy ----------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "RedundantStringInitCheck.h"
+#include "../utils/Matchers.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+
+using namespace clang::ast_matchers;
+using namespace clang::tidy::matchers;
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+void RedundantStringInitCheck::registerMatchers(MatchFinder *Finder) {
+  if (!getLangOpts().CPlusPlus)
+    return;
+
+  // Match string constructor.
+  const auto StringConstructorExpr = expr(anyOf(
+      cxxConstructExpr(argumentCountIs(1),
+                       hasDeclaration(cxxMethodDecl(hasName("basic_string")))),
+      // If present, the second argument is the alloc object which must not
+      // be present explicitly.
+      cxxConstructExpr(argumentCountIs(2),
+                       hasDeclaration(cxxMethodDecl(hasName("basic_string"))),
+                       hasArgument(1, cxxDefaultArgExpr()))));
+
+  // Match a string constructor expression with an empty string literal.
+  const auto EmptyStringCtorExpr = cxxConstructExpr(
+      StringConstructorExpr,
+      hasArgument(0, ignoringParenImpCasts(stringLiteral(hasSize(0)))));
+
+  const auto EmptyStringCtorExprWithTemporaries =
+      cxxConstructExpr(StringConstructorExpr,
+                       hasArgument(0, ignoringImplicit(EmptyStringCtorExpr)));
+
+  // Match a variable declaration with an empty string literal as initializer.
+  // Examples:
+  //     string foo = "";
+  //     string bar("");
+  Finder->addMatcher(
+      namedDecl(
+          varDecl(hasType(cxxRecordDecl(hasName("basic_string"))),
+                  hasInitializer(expr(ignoringImplicit(anyOf(
+                                          EmptyStringCtorExpr,
+                                          EmptyStringCtorExprWithTemporaries)))
+                                     .bind("expr"))),
+          unless(parmVarDecl()))
+          .bind("decl"),
+      this);
+}
+
+void RedundantStringInitCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *CtorExpr = Result.Nodes.getNodeAs<Expr>("expr");
+  const auto *Decl = Result.Nodes.getNodeAs<NamedDecl>("decl");
+  diag(CtorExpr->getExprLoc(), "redundant string initialization")
+      << FixItHint::CreateReplacement(CtorExpr->getSourceRange(),
+                                      Decl->getName());
+}
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/readability/RedundantStringInitCheck.h b/clang-tidy/readability/RedundantStringInitCheck.h
new file mode 100644 (file)
index 0000000..0a32eb6
--- /dev/null
@@ -0,0 +1,32 @@
+//===- RedundantStringInitCheck.h - clang-tidy ------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANT_STRING_INIT_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANT_STRING_INIT_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+/// Finds unnecessary string initializations.
+class RedundantStringInitCheck : public ClangTidyCheck {
+public:
+  RedundantStringInitCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANT_STRING_INIT_H
diff --git a/clang-tidy/readability/SimplifyBooleanExprCheck.cpp b/clang-tidy/readability/SimplifyBooleanExprCheck.cpp
new file mode 100644 (file)
index 0000000..8934e7b
--- /dev/null
@@ -0,0 +1,676 @@
+//===--- SimplifyBooleanExpr.cpp clang-tidy ---------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "SimplifyBooleanExprCheck.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/Lex/Lexer.h"
+
+#include <cassert>
+#include <string>
+#include <utility>
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+namespace {
+
+StringRef getText(const MatchFinder::MatchResult &Result, SourceRange Range) {
+  return Lexer::getSourceText(CharSourceRange::getTokenRange(Range),
+                              *Result.SourceManager,
+                              Result.Context->getLangOpts());
+}
+
+template <typename T>
+StringRef getText(const MatchFinder::MatchResult &Result, T &Node) {
+  return getText(Result, Node.getSourceRange());
+}
+
+const char ConditionThenStmtId[] = "if-bool-yields-then";
+const char ConditionElseStmtId[] = "if-bool-yields-else";
+const char TernaryId[] = "ternary-bool-yields-condition";
+const char TernaryNegatedId[] = "ternary-bool-yields-not-condition";
+const char IfReturnsBoolId[] = "if-return";
+const char IfReturnsNotBoolId[] = "if-not-return";
+const char ThenLiteralId[] = "then-literal";
+const char IfAssignVariableId[] = "if-assign-lvalue";
+const char IfAssignLocId[] = "if-assign-loc";
+const char IfAssignBoolId[] = "if-assign";
+const char IfAssignNotBoolId[] = "if-assign-not";
+const char IfAssignObjId[] = "if-assign-obj";
+const char CompoundReturnId[] = "compound-return";
+const char CompoundBoolId[] = "compound-bool";
+const char CompoundNotBoolId[] = "compound-bool-not";
+
+const char IfStmtId[] = "if";
+
+const char SimplifyOperatorDiagnostic[] =
+    "redundant boolean literal supplied to boolean operator";
+const char SimplifyConditionDiagnostic[] =
+    "redundant boolean literal in if statement condition";
+const char SimplifyConditionalReturnDiagnostic[] =
+    "redundant boolean literal in conditional return statement";
+
+const CXXBoolLiteralExpr *getBoolLiteral(const MatchFinder::MatchResult &Result,
+                                         StringRef Id) {
+  const auto *Literal = Result.Nodes.getNodeAs<CXXBoolLiteralExpr>(Id);
+  return (Literal &&
+          Result.SourceManager->isMacroBodyExpansion(Literal->getLocStart()))
+             ? nullptr
+             : Literal;
+}
+
+internal::Matcher<Stmt> returnsBool(bool Value, StringRef Id = "ignored") {
+  auto SimpleReturnsBool =
+      returnStmt(has(cxxBoolLiteral(equals(Value)).bind(Id)))
+          .bind("returns-bool");
+  return anyOf(SimpleReturnsBool,
+               compoundStmt(statementCountIs(1), has(SimpleReturnsBool)));
+}
+
+bool needsParensAfterUnaryNegation(const Expr *E) {
+  E = E->IgnoreImpCasts();
+  if (isa<BinaryOperator>(E) || isa<ConditionalOperator>(E))
+    return true;
+
+  if (const auto *Op = dyn_cast<CXXOperatorCallExpr>(E))
+    return Op->getNumArgs() == 2 && Op->getOperator() != OO_Call &&
+           Op->getOperator() != OO_Subscript;
+
+  return false;
+}
+
+std::pair<BinaryOperatorKind, BinaryOperatorKind> Opposites[] = {
+    {BO_LT, BO_GE}, {BO_GT, BO_LE}, {BO_EQ, BO_NE}};
+
+StringRef negatedOperator(const BinaryOperator *BinOp) {
+  const BinaryOperatorKind Opcode = BinOp->getOpcode();
+  for (auto NegatableOp : Opposites) {
+    if (Opcode == NegatableOp.first)
+      return BinOp->getOpcodeStr(NegatableOp.second);
+    if (Opcode == NegatableOp.second)
+      return BinOp->getOpcodeStr(NegatableOp.first);
+  }
+  return StringRef();
+}
+
+std::pair<OverloadedOperatorKind, StringRef> OperatorNames[] = {
+    {OO_EqualEqual, "=="},   {OO_ExclaimEqual, "!="}, {OO_Less, "<"},
+    {OO_GreaterEqual, ">="}, {OO_Greater, ">"},       {OO_LessEqual, "<="}};
+
+StringRef getOperatorName(OverloadedOperatorKind OpKind) {
+  for (auto Name : OperatorNames) {
+    if (Name.first == OpKind)
+      return Name.second;
+  }
+
+  return StringRef();
+}
+
+std::pair<OverloadedOperatorKind, OverloadedOperatorKind> OppositeOverloads[] =
+    {{OO_EqualEqual, OO_ExclaimEqual},
+     {OO_Less, OO_GreaterEqual},
+     {OO_Greater, OO_LessEqual}};
+
+StringRef negatedOperator(const CXXOperatorCallExpr *OpCall) {
+  const OverloadedOperatorKind Opcode = OpCall->getOperator();
+  for (auto NegatableOp : OppositeOverloads) {
+    if (Opcode == NegatableOp.first)
+      return getOperatorName(NegatableOp.second);
+    if (Opcode == NegatableOp.second)
+      return getOperatorName(NegatableOp.first);
+  }
+  return StringRef();
+}
+
+std::string asBool(StringRef text, bool NeedsStaticCast) {
+  if (NeedsStaticCast)
+    return ("static_cast<bool>(" + text + ")").str();
+
+  return text;
+}
+
+bool needsNullPtrComparison(const Expr *E) {
+  if (const auto *ImpCast = dyn_cast<ImplicitCastExpr>(E))
+    return ImpCast->getCastKind() == CK_PointerToBoolean ||
+           ImpCast->getCastKind() == CK_MemberPointerToBoolean;
+
+  return false;
+}
+
+bool needsZeroComparison(const Expr *E) {
+  if (const auto *ImpCast = dyn_cast<ImplicitCastExpr>(E))
+    return ImpCast->getCastKind() == CK_IntegralToBoolean;
+
+  return false;
+}
+
+bool needsStaticCast(const Expr *E) {
+  if (const auto *ImpCast = dyn_cast<ImplicitCastExpr>(E)) {
+    if (ImpCast->getCastKind() == CK_UserDefinedConversion &&
+        ImpCast->getSubExpr()->getType()->isBooleanType()) {
+      if (const auto *MemCall =
+              dyn_cast<CXXMemberCallExpr>(ImpCast->getSubExpr())) {
+        if (const auto *MemDecl =
+                dyn_cast<CXXConversionDecl>(MemCall->getMethodDecl())) {
+          if (MemDecl->isExplicit())
+            return true;
+        }
+      }
+    }
+  }
+
+  E = E->IgnoreImpCasts();
+  return !E->getType()->isBooleanType();
+}
+
+std::string compareExpressionToConstant(const MatchFinder::MatchResult &Result,
+                                        const Expr *E, bool Negated,
+                                        const char *Constant) {
+  E = E->IgnoreImpCasts();
+  const std::string ExprText =
+      (isa<BinaryOperator>(E) ? ("(" + getText(Result, *E) + ")")
+                              : getText(Result, *E))
+          .str();
+  return ExprText + " " + (Negated ? "!=" : "==") + " " + Constant;
+}
+
+std::string compareExpressionToNullPtr(const MatchFinder::MatchResult &Result,
+                                       const Expr *E, bool Negated) {
+  const char *NullPtr =
+      Result.Context->getLangOpts().CPlusPlus11 ? "nullptr" : "NULL";
+  return compareExpressionToConstant(Result, E, Negated, NullPtr);
+}
+
+std::string compareExpressionToZero(const MatchFinder::MatchResult &Result,
+                                    const Expr *E, bool Negated) {
+  return compareExpressionToConstant(Result, E, Negated, "0");
+}
+
+std::string replacementExpression(const MatchFinder::MatchResult &Result,
+                                  bool Negated, const Expr *E) {
+  E = E->ignoreParenBaseCasts();
+  const bool NeedsStaticCast = needsStaticCast(E);
+  if (Negated) {
+    if (const auto *UnOp = dyn_cast<UnaryOperator>(E)) {
+      if (UnOp->getOpcode() == UO_LNot) {
+        if (needsNullPtrComparison(UnOp->getSubExpr()))
+          return compareExpressionToNullPtr(Result, UnOp->getSubExpr(), true);
+
+        if (needsZeroComparison(UnOp->getSubExpr()))
+          return compareExpressionToZero(Result, UnOp->getSubExpr(), true);
+
+        return replacementExpression(Result, false, UnOp->getSubExpr());
+      }
+    }
+
+    if (needsNullPtrComparison(E))
+      return compareExpressionToNullPtr(Result, E, false);
+
+    if (needsZeroComparison(E))
+      return compareExpressionToZero(Result, E, false);
+
+    StringRef NegatedOperator;
+    const Expr *LHS = nullptr;
+    const Expr *RHS = nullptr;
+    if (const auto *BinOp = dyn_cast<BinaryOperator>(E)) {
+      NegatedOperator = negatedOperator(BinOp);
+      LHS = BinOp->getLHS();
+      RHS = BinOp->getRHS();
+    } else if (const auto *OpExpr = dyn_cast<CXXOperatorCallExpr>(E)) {
+      if (OpExpr->getNumArgs() == 2) {
+        NegatedOperator = negatedOperator(OpExpr);
+        LHS = OpExpr->getArg(0);
+        RHS = OpExpr->getArg(1);
+      }
+    }
+    if (!NegatedOperator.empty() && LHS && RHS)
+      return (asBool((getText(Result, *LHS) + " " + NegatedOperator + " " +
+                      getText(Result, *RHS))
+                         .str(),
+                     NeedsStaticCast));
+
+    StringRef Text = getText(Result, *E);
+    if (!NeedsStaticCast && needsParensAfterUnaryNegation(E))
+      return ("!(" + Text + ")").str();
+
+    if (needsNullPtrComparison(E))
+      return compareExpressionToNullPtr(Result, E, false);
+
+    if (needsZeroComparison(E))
+      return compareExpressionToZero(Result, E, false);
+
+    return ("!" + asBool(Text, NeedsStaticCast));
+  }
+
+  if (const auto *UnOp = dyn_cast<UnaryOperator>(E)) {
+    if (UnOp->getOpcode() == UO_LNot) {
+      if (needsNullPtrComparison(UnOp->getSubExpr()))
+        return compareExpressionToNullPtr(Result, UnOp->getSubExpr(), false);
+
+      if (needsZeroComparison(UnOp->getSubExpr()))
+        return compareExpressionToZero(Result, UnOp->getSubExpr(), false);
+    }
+  }
+
+  if (needsNullPtrComparison(E))
+    return compareExpressionToNullPtr(Result, E, true);
+
+  if (needsZeroComparison(E))
+    return compareExpressionToZero(Result, E, true);
+
+  return asBool(getText(Result, *E), NeedsStaticCast);
+}
+
+const CXXBoolLiteralExpr *stmtReturnsBool(const ReturnStmt *Ret, bool Negated) {
+  if (const auto *Bool = dyn_cast<CXXBoolLiteralExpr>(Ret->getRetValue())) {
+    if (Bool->getValue() == !Negated)
+      return Bool;
+  }
+
+  return nullptr;
+}
+
+const CXXBoolLiteralExpr *stmtReturnsBool(const IfStmt *IfRet, bool Negated) {
+  if (IfRet->getElse() != nullptr)
+    return nullptr;
+
+  if (const auto *Ret = dyn_cast<ReturnStmt>(IfRet->getThen()))
+    return stmtReturnsBool(Ret, Negated);
+
+  if (const auto *Compound = dyn_cast<CompoundStmt>(IfRet->getThen())) {
+    if (Compound->size() == 1) {
+      if (const auto *CompoundRet = dyn_cast<ReturnStmt>(Compound->body_back()))
+        return stmtReturnsBool(CompoundRet, Negated);
+    }
+  }
+
+  return nullptr;
+}
+
+bool containsDiscardedTokens(const MatchFinder::MatchResult &Result,
+                             CharSourceRange CharRange) {
+  std::string ReplacementText =
+      Lexer::getSourceText(CharRange, *Result.SourceManager,
+                           Result.Context->getLangOpts())
+          .str();
+  Lexer Lex(CharRange.getBegin(), Result.Context->getLangOpts(),
+            ReplacementText.data(), ReplacementText.data(),
+            ReplacementText.data() + ReplacementText.size());
+  Lex.SetCommentRetentionState(true);
+
+  Token Tok;
+  while (!Lex.LexFromRawLexer(Tok)) {
+    if (Tok.is(tok::TokenKind::comment) || Tok.is(tok::TokenKind::hash))
+      return true;
+  }
+
+  return false;
+}
+
+} // namespace
+
+class SimplifyBooleanExprCheck::Visitor : public RecursiveASTVisitor<Visitor> {
+  using Base = RecursiveASTVisitor<Visitor>;
+
+ public:
+  Visitor(SimplifyBooleanExprCheck *Check,
+          const MatchFinder::MatchResult &Result)
+      : Check(Check), Result(Result) {}
+
+  bool VisitBinaryOperator(BinaryOperator *Op) {
+    Check->reportBinOp(Result, Op);
+    return true;
+  }
+
+ private:
+  SimplifyBooleanExprCheck *Check;
+  const MatchFinder::MatchResult &Result;
+};
+
+
+SimplifyBooleanExprCheck::SimplifyBooleanExprCheck(StringRef Name,
+                                                   ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      ChainedConditionalReturn(Options.get("ChainedConditionalReturn", 0U)),
+      ChainedConditionalAssignment(
+          Options.get("ChainedConditionalAssignment", 0U)) {}
+
+bool containsBoolLiteral(const Expr *E) {
+  if (!E)
+    return false;
+  E = E->IgnoreParenImpCasts();
+  if (isa<CXXBoolLiteralExpr>(E))
+    return true;
+  if (const auto *BinOp = dyn_cast<BinaryOperator>(E))
+    return containsBoolLiteral(BinOp->getLHS()) ||
+           containsBoolLiteral(BinOp->getRHS());
+  if (const auto *UnaryOp = dyn_cast<UnaryOperator>(E))
+    return containsBoolLiteral(UnaryOp->getSubExpr());
+  return false;
+}
+
+void SimplifyBooleanExprCheck::reportBinOp(
+    const MatchFinder::MatchResult &Result, const BinaryOperator *Op) {
+  const auto *LHS = Op->getLHS()->IgnoreParenImpCasts();
+  const auto *RHS = Op->getRHS()->IgnoreParenImpCasts();
+
+  const CXXBoolLiteralExpr *Bool = nullptr;
+  const Expr *Other = nullptr;
+  if ((Bool = dyn_cast<CXXBoolLiteralExpr>(LHS)))
+    Other = RHS;
+  else if ((Bool = dyn_cast<CXXBoolLiteralExpr>(RHS)))
+    Other = LHS;
+  else
+    return;
+
+  if (Bool->getLocStart().isMacroID())
+    return;
+
+  // FIXME: why do we need this?
+  if (!isa<CXXBoolLiteralExpr>(Other) && containsBoolLiteral(Other))
+    return;
+
+  bool BoolValue = Bool->getValue();
+
+  auto replaceWithExpression = [this, &Result, LHS, RHS, Bool](
+                                   const Expr *ReplaceWith, bool Negated) {
+    std::string Replacement =
+        replacementExpression(Result, Negated, ReplaceWith);
+    SourceRange Range(LHS->getLocStart(), RHS->getLocEnd());
+    issueDiag(Result, Bool->getLocStart(), SimplifyOperatorDiagnostic, Range,
+              Replacement);
+  };
+
+  switch (Op->getOpcode()) {
+    case BO_LAnd:
+      if (BoolValue) {
+        // expr && true -> expr
+        replaceWithExpression(Other, /*Negated=*/false);
+      } else {
+        // expr && false -> false
+        replaceWithExpression(Bool, /*Negated=*/false);
+      }
+      break;
+    case BO_LOr:
+      if (BoolValue) {
+        // expr || true -> true
+        replaceWithExpression(Bool, /*Negated=*/false);
+      } else {
+        // expr || false -> expr
+        replaceWithExpression(Other, /*Negated=*/false);
+      }
+      break;
+    case BO_EQ:
+      // expr == true -> expr, expr == false -> !expr
+      replaceWithExpression(Other, /*Negated=*/!BoolValue);
+      break;
+    case BO_NE:
+      // expr != true -> !expr, expr != false -> expr
+      replaceWithExpression(Other, /*Negated=*/BoolValue);
+      break;
+    default:
+      break;
+  }
+}
+
+void SimplifyBooleanExprCheck::matchBoolCondition(MatchFinder *Finder,
+                                                  bool Value,
+                                                  StringRef BooleanId) {
+  Finder->addMatcher(
+      ifStmt(isExpansionInMainFile(),
+             hasCondition(cxxBoolLiteral(equals(Value)).bind(BooleanId)))
+          .bind(IfStmtId),
+      this);
+}
+
+void SimplifyBooleanExprCheck::matchTernaryResult(MatchFinder *Finder,
+                                                  bool Value,
+                                                  StringRef TernaryId) {
+  Finder->addMatcher(
+      conditionalOperator(isExpansionInMainFile(),
+                          hasTrueExpression(cxxBoolLiteral(equals(Value))),
+                          hasFalseExpression(cxxBoolLiteral(equals(!Value))))
+          .bind(TernaryId),
+      this);
+}
+
+void SimplifyBooleanExprCheck::matchIfReturnsBool(MatchFinder *Finder,
+                                                  bool Value, StringRef Id) {
+  if (ChainedConditionalReturn)
+    Finder->addMatcher(ifStmt(isExpansionInMainFile(),
+                              hasThen(returnsBool(Value, ThenLiteralId)),
+                              hasElse(returnsBool(!Value)))
+                           .bind(Id),
+                       this);
+  else
+    Finder->addMatcher(ifStmt(isExpansionInMainFile(),
+                              unless(hasParent(ifStmt())),
+                              hasThen(returnsBool(Value, ThenLiteralId)),
+                              hasElse(returnsBool(!Value)))
+                           .bind(Id),
+                       this);
+}
+
+void SimplifyBooleanExprCheck::matchIfAssignsBool(MatchFinder *Finder,
+                                                  bool Value, StringRef Id) {
+  auto SimpleThen = binaryOperator(
+      hasOperatorName("="),
+      hasLHS(declRefExpr(hasDeclaration(decl().bind(IfAssignObjId)))),
+      hasLHS(expr().bind(IfAssignVariableId)),
+      hasRHS(cxxBoolLiteral(equals(Value)).bind(IfAssignLocId)));
+  auto Then = anyOf(SimpleThen, compoundStmt(statementCountIs(1),
+                                             hasAnySubstatement(SimpleThen)));
+  auto SimpleElse = binaryOperator(
+      hasOperatorName("="),
+      hasLHS(declRefExpr(hasDeclaration(equalsBoundNode(IfAssignObjId)))),
+      hasRHS(cxxBoolLiteral(equals(!Value))));
+  auto Else = anyOf(SimpleElse, compoundStmt(statementCountIs(1),
+                                             hasAnySubstatement(SimpleElse)));
+  if (ChainedConditionalAssignment)
+    Finder->addMatcher(
+        ifStmt(isExpansionInMainFile(), hasThen(Then), hasElse(Else)).bind(Id),
+        this);
+  else
+    Finder->addMatcher(ifStmt(isExpansionInMainFile(),
+                              unless(hasParent(ifStmt())), hasThen(Then),
+                              hasElse(Else))
+                           .bind(Id),
+                       this);
+}
+
+void SimplifyBooleanExprCheck::matchCompoundIfReturnsBool(MatchFinder *Finder,
+                                                          bool Value,
+                                                          StringRef Id) {
+  Finder->addMatcher(
+      compoundStmt(allOf(hasAnySubstatement(ifStmt(hasThen(returnsBool(Value)),
+                                                   unless(hasElse(stmt())))),
+                         hasAnySubstatement(
+                             returnStmt(has(ignoringParenImpCasts(
+                                            cxxBoolLiteral(equals(!Value)))))
+                                 .bind(CompoundReturnId))))
+          .bind(Id),
+      this);
+}
+
+void SimplifyBooleanExprCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "ChainedConditionalReturn", ChainedConditionalReturn);
+  Options.store(Opts, "ChainedConditionalAssignment",
+                ChainedConditionalAssignment);
+}
+
+void SimplifyBooleanExprCheck::registerMatchers(MatchFinder *Finder) {
+  Finder->addMatcher(translationUnitDecl().bind("top"), this);
+
+  matchBoolCondition(Finder, true, ConditionThenStmtId);
+  matchBoolCondition(Finder, false, ConditionElseStmtId);
+
+  matchTernaryResult(Finder, true, TernaryId);
+  matchTernaryResult(Finder, false, TernaryNegatedId);
+
+  matchIfReturnsBool(Finder, true, IfReturnsBoolId);
+  matchIfReturnsBool(Finder, false, IfReturnsNotBoolId);
+
+  matchIfAssignsBool(Finder, true, IfAssignBoolId);
+  matchIfAssignsBool(Finder, false, IfAssignNotBoolId);
+
+  matchCompoundIfReturnsBool(Finder, true, CompoundBoolId);
+  matchCompoundIfReturnsBool(Finder, false, CompoundNotBoolId);
+}
+
+void SimplifyBooleanExprCheck::check(const MatchFinder::MatchResult &Result) {
+  if (const CXXBoolLiteralExpr *TrueConditionRemoved =
+          getBoolLiteral(Result, ConditionThenStmtId))
+    replaceWithThenStatement(Result, TrueConditionRemoved);
+  else if (const CXXBoolLiteralExpr *FalseConditionRemoved =
+               getBoolLiteral(Result, ConditionElseStmtId))
+    replaceWithElseStatement(Result, FalseConditionRemoved);
+  else if (const auto *Ternary =
+               Result.Nodes.getNodeAs<ConditionalOperator>(TernaryId))
+    replaceWithCondition(Result, Ternary);
+  else if (const auto *TernaryNegated =
+               Result.Nodes.getNodeAs<ConditionalOperator>(TernaryNegatedId))
+    replaceWithCondition(Result, TernaryNegated, true);
+  else if (const auto *If = Result.Nodes.getNodeAs<IfStmt>(IfReturnsBoolId))
+    replaceWithReturnCondition(Result, If);
+  else if (const auto *IfNot =
+               Result.Nodes.getNodeAs<IfStmt>(IfReturnsNotBoolId))
+    replaceWithReturnCondition(Result, IfNot, true);
+  else if (const auto *IfAssign =
+               Result.Nodes.getNodeAs<IfStmt>(IfAssignBoolId))
+    replaceWithAssignment(Result, IfAssign);
+  else if (const auto *IfAssignNot =
+               Result.Nodes.getNodeAs<IfStmt>(IfAssignNotBoolId))
+    replaceWithAssignment(Result, IfAssignNot, true);
+  else if (const auto *Compound =
+               Result.Nodes.getNodeAs<CompoundStmt>(CompoundBoolId))
+    replaceCompoundReturnWithCondition(Result, Compound);
+  else if (const auto *Compound =
+               Result.Nodes.getNodeAs<CompoundStmt>(CompoundNotBoolId))
+    replaceCompoundReturnWithCondition(Result, Compound, true);
+  else if (const auto TU = Result.Nodes.getNodeAs<Decl>("top"))
+    Visitor(this, Result).TraverseDecl(const_cast<Decl*>(TU));
+}
+
+void SimplifyBooleanExprCheck::issueDiag(
+    const ast_matchers::MatchFinder::MatchResult &Result, SourceLocation Loc,
+    StringRef Description, SourceRange ReplacementRange,
+    StringRef Replacement) {
+  CharSourceRange CharRange =
+      Lexer::makeFileCharRange(CharSourceRange::getTokenRange(ReplacementRange),
+                               *Result.SourceManager, getLangOpts());
+
+  DiagnosticBuilder Diag = diag(Loc, Description);
+  if (!containsDiscardedTokens(Result, CharRange))
+    Diag << FixItHint::CreateReplacement(CharRange, Replacement);
+}
+
+void SimplifyBooleanExprCheck::replaceWithThenStatement(
+    const MatchFinder::MatchResult &Result,
+    const CXXBoolLiteralExpr *TrueConditionRemoved) {
+  const auto *IfStatement = Result.Nodes.getNodeAs<IfStmt>(IfStmtId);
+  issueDiag(Result, TrueConditionRemoved->getLocStart(),
+            SimplifyConditionDiagnostic, IfStatement->getSourceRange(),
+            getText(Result, *IfStatement->getThen()));
+}
+
+void SimplifyBooleanExprCheck::replaceWithElseStatement(
+    const MatchFinder::MatchResult &Result,
+    const CXXBoolLiteralExpr *FalseConditionRemoved) {
+  const auto *IfStatement = Result.Nodes.getNodeAs<IfStmt>(IfStmtId);
+  const Stmt *ElseStatement = IfStatement->getElse();
+  issueDiag(Result, FalseConditionRemoved->getLocStart(),
+            SimplifyConditionDiagnostic, IfStatement->getSourceRange(),
+            ElseStatement ? getText(Result, *ElseStatement) : "");
+}
+
+void SimplifyBooleanExprCheck::replaceWithCondition(
+    const MatchFinder::MatchResult &Result, const ConditionalOperator *Ternary,
+    bool Negated) {
+  std::string Replacement =
+      replacementExpression(Result, Negated, Ternary->getCond());
+  issueDiag(Result, Ternary->getTrueExpr()->getLocStart(),
+            "redundant boolean literal in ternary expression result",
+            Ternary->getSourceRange(), Replacement);
+}
+
+void SimplifyBooleanExprCheck::replaceWithReturnCondition(
+    const MatchFinder::MatchResult &Result, const IfStmt *If, bool Negated) {
+  StringRef Terminator = isa<CompoundStmt>(If->getElse()) ? ";" : "";
+  std::string Condition = replacementExpression(Result, Negated, If->getCond());
+  std::string Replacement = ("return " + Condition + Terminator).str();
+  SourceLocation Start =
+      Result.Nodes.getNodeAs<CXXBoolLiteralExpr>(ThenLiteralId)->getLocStart();
+  issueDiag(Result, Start, SimplifyConditionalReturnDiagnostic,
+            If->getSourceRange(), Replacement);
+}
+
+void SimplifyBooleanExprCheck::replaceCompoundReturnWithCondition(
+    const MatchFinder::MatchResult &Result, const CompoundStmt *Compound,
+    bool Negated) {
+  const auto *Ret = Result.Nodes.getNodeAs<ReturnStmt>(CompoundReturnId);
+
+  // The body shouldn't be empty because the matcher ensures that it must
+  // contain at least two statements:
+  // 1) A `return` statement returning a boolean literal `false` or `true`
+  // 2) An `if` statement with no `else` clause that consists of a single
+  //    `return` statement returning the opposite boolean literal `true` or
+  //    `false`.
+  assert(Compound->size() >= 2);
+  const IfStmt *BeforeIf = nullptr;
+  CompoundStmt::const_body_iterator Current = Compound->body_begin();
+  CompoundStmt::const_body_iterator After = Compound->body_begin();
+  for (++After; After != Compound->body_end() && *Current != Ret;
+       ++Current, ++After) {
+    if (const auto *If = dyn_cast<IfStmt>(*Current)) {
+      if (const CXXBoolLiteralExpr *Lit = stmtReturnsBool(If, Negated)) {
+        if (*After == Ret) {
+          if (!ChainedConditionalReturn && BeforeIf)
+            continue;
+
+          const Expr *Condition = If->getCond();
+          std::string Replacement =
+              "return " + replacementExpression(Result, Negated, Condition);
+          issueDiag(
+              Result, Lit->getLocStart(), SimplifyConditionalReturnDiagnostic,
+              SourceRange(If->getLocStart(), Ret->getLocEnd()), Replacement);
+          return;
+        }
+
+        BeforeIf = If;
+      }
+    } else {
+      BeforeIf = nullptr;
+    }
+  }
+}
+
+void SimplifyBooleanExprCheck::replaceWithAssignment(
+    const MatchFinder::MatchResult &Result, const IfStmt *IfAssign,
+    bool Negated) {
+  SourceRange Range = IfAssign->getSourceRange();
+  StringRef VariableName =
+      getText(Result, *Result.Nodes.getNodeAs<Expr>(IfAssignVariableId));
+  StringRef Terminator = isa<CompoundStmt>(IfAssign->getElse()) ? ";" : "";
+  std::string Condition =
+      replacementExpression(Result, Negated, IfAssign->getCond());
+  std::string Replacement =
+      (VariableName + " = " + Condition + Terminator).str();
+  SourceLocation Location =
+      Result.Nodes.getNodeAs<CXXBoolLiteralExpr>(IfAssignLocId)->getLocStart();
+  issueDiag(Result, Location,
+            "redundant boolean literal in conditional assignment", Range,
+            Replacement);
+}
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/readability/SimplifyBooleanExprCheck.h b/clang-tidy/readability/SimplifyBooleanExprCheck.h
new file mode 100644 (file)
index 0000000..af47453
--- /dev/null
@@ -0,0 +1,90 @@
+//===--- SimplifyBooleanExpr.h clang-tidy -----------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_SIMPLIFY_BOOLEAN_EXPR_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_SIMPLIFY_BOOLEAN_EXPR_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+/// Looks for boolean expressions involving boolean constants and simplifies
+/// them to use the appropriate boolean expression directly.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability-simplify-boolean-expr.html
+class SimplifyBooleanExprCheck : public ClangTidyCheck {
+public:
+  SimplifyBooleanExprCheck(StringRef Name, ClangTidyContext *Context);
+
+  void storeOptions(ClangTidyOptions::OptionMap &Options) override;
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+  class Visitor;
+
+  void reportBinOp(const ast_matchers::MatchFinder::MatchResult &Result,
+                   const BinaryOperator *Op);
+
+  void matchBoolCondition(ast_matchers::MatchFinder *Finder, bool Value,
+                          StringRef BooleanId);
+
+  void matchTernaryResult(ast_matchers::MatchFinder *Finder, bool Value,
+                          StringRef TernaryId);
+
+  void matchIfReturnsBool(ast_matchers::MatchFinder *Finder, bool Value,
+                          StringRef Id);
+
+  void matchIfAssignsBool(ast_matchers::MatchFinder *Finder, bool Value,
+                          StringRef Id);
+
+  void matchCompoundIfReturnsBool(ast_matchers::MatchFinder *Finder, bool Value,
+                                  StringRef Id);
+
+  void
+  replaceWithThenStatement(const ast_matchers::MatchFinder::MatchResult &Result,
+                           const CXXBoolLiteralExpr *BoolLiteral);
+
+  void
+  replaceWithElseStatement(const ast_matchers::MatchFinder::MatchResult &Result,
+                           const CXXBoolLiteralExpr *FalseConditionRemoved);
+
+  void
+  replaceWithCondition(const ast_matchers::MatchFinder::MatchResult &Result,
+                       const ConditionalOperator *Ternary,
+                       bool Negated = false);
+
+  void replaceWithReturnCondition(
+      const ast_matchers::MatchFinder::MatchResult &Result, const IfStmt *If,
+      bool Negated = false);
+
+  void
+  replaceWithAssignment(const ast_matchers::MatchFinder::MatchResult &Result,
+                        const IfStmt *If, bool Negated = false);
+
+  void replaceCompoundReturnWithCondition(
+      const ast_matchers::MatchFinder::MatchResult &Result,
+      const CompoundStmt *Compound, bool Negated = false);
+
+  void issueDiag(const ast_matchers::MatchFinder::MatchResult &Result,
+                 SourceLocation Loc, StringRef Description,
+                 SourceRange ReplacementRange, StringRef Replacement);
+
+  const bool ChainedConditionalReturn;
+  const bool ChainedConditionalAssignment;
+};
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_SIMPLIFY_BOOLEAN_EXPR_H
diff --git a/clang-tidy/readability/StaticDefinitionInAnonymousNamespaceCheck.cpp b/clang-tidy/readability/StaticDefinitionInAnonymousNamespaceCheck.cpp
new file mode 100644 (file)
index 0000000..0554605
--- /dev/null
@@ -0,0 +1,66 @@
+//===--- StaticDefinitionInAnonymousNamespaceCheck.cpp - clang-tidy--------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "StaticDefinitionInAnonymousNamespaceCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+void StaticDefinitionInAnonymousNamespaceCheck::registerMatchers(
+    MatchFinder *Finder) {
+  Finder->addMatcher(
+      namedDecl(anyOf(functionDecl(isDefinition(), isStaticStorageClass()),
+                      varDecl(isDefinition(), isStaticStorageClass())),
+                hasParent(namespaceDecl(isAnonymous())))
+          .bind("static-def"),
+      this);
+}
+
+void StaticDefinitionInAnonymousNamespaceCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  const auto *Def = Result.Nodes.getNodeAs<NamedDecl>("static-def");
+  // Skips all static definitions defined in Macro.
+  if (Def->getLocation().isMacroID())
+    return;
+
+  // Skips all static definitions in function scope.
+  const DeclContext *DC = Def->getDeclContext();
+  if (DC->getDeclKind() != Decl::Namespace)
+    return;
+
+  auto Diag =
+      diag(Def->getLocation(), "%0 is a static definition in "
+                               "anonymous namespace; static is redundant here")
+      << Def;
+  Token Tok;
+  SourceLocation Loc = Def->getSourceRange().getBegin();
+  while (Loc < Def->getSourceRange().getEnd() &&
+         !Lexer::getRawToken(Loc, Tok, *Result.SourceManager, getLangOpts(),
+                             true)) {
+    SourceRange TokenRange(Tok.getLocation(), Tok.getEndLoc());
+    StringRef SourceText =
+        Lexer::getSourceText(CharSourceRange::getTokenRange(TokenRange),
+                             *Result.SourceManager, getLangOpts());
+    if (SourceText == "static") {
+      Diag << FixItHint::CreateRemoval(TokenRange);
+      break;
+    }
+    Loc = Tok.getEndLoc();
+  }
+}
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/readability/StaticDefinitionInAnonymousNamespaceCheck.h b/clang-tidy/readability/StaticDefinitionInAnonymousNamespaceCheck.h
new file mode 100644 (file)
index 0000000..03e99fd
--- /dev/null
@@ -0,0 +1,36 @@
+//===--- StaticDefinitionInAnonymousNamespaceCheck.h - clang-tidy*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_STATIC_DEFINITION_IN_ANONYMOUS_NAMESPACE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_STATIC_DEFINITION_IN_ANONYMOUS_NAMESPACE_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+/// Finds static function and variable definitions in anonymous namespace.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability-static-definition-in-anonymous-namespace.html
+class StaticDefinitionInAnonymousNamespaceCheck : public ClangTidyCheck {
+public:
+  StaticDefinitionInAnonymousNamespaceCheck(StringRef Name,
+                                            ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_STATIC_DEFINITION_IN_ANONYMOUS_NAMESPACE_H
diff --git a/clang-tidy/readability/UniqueptrDeleteReleaseCheck.cpp b/clang-tidy/readability/UniqueptrDeleteReleaseCheck.cpp
new file mode 100644 (file)
index 0000000..3ad346c
--- /dev/null
@@ -0,0 +1,69 @@
+//===--- UniqueptrDeleteReleaseCheck.cpp - clang-tidy----------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "UniqueptrDeleteReleaseCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+void UniqueptrDeleteReleaseCheck::registerMatchers(MatchFinder *Finder) {
+  auto IsSusbstituted = qualType(anyOf(
+      substTemplateTypeParmType(), hasDescendant(substTemplateTypeParmType())));
+
+  auto UniquePtrWithDefaultDelete = classTemplateSpecializationDecl(
+      hasName("std::unique_ptr"),
+      hasTemplateArgument(1, refersToType(qualType(hasDeclaration(cxxRecordDecl(
+                                 hasName("std::default_delete")))))));
+
+  Finder->addMatcher(
+      cxxDeleteExpr(has(ignoringParenImpCasts(cxxMemberCallExpr(
+                        on(expr(hasType(UniquePtrWithDefaultDelete),
+                                unless(hasType(IsSusbstituted)))
+                               .bind("uptr")),
+                        callee(cxxMethodDecl(hasName("release")))))))
+          .bind("delete"),
+      this);
+}
+
+void UniqueptrDeleteReleaseCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  const auto *PtrExpr = Result.Nodes.getNodeAs<Expr>("uptr");
+  const auto *DeleteExpr = Result.Nodes.getNodeAs<Expr>("delete");
+
+  if (PtrExpr->getLocStart().isMacroID())
+    return;
+
+  // Ignore dependent types.
+  // It can give us false positives, so we go with false negatives instead to
+  // be safe.
+  if (PtrExpr->getType()->isDependentType())
+    return;
+
+  SourceLocation AfterPtr = Lexer::getLocForEndOfToken(
+      PtrExpr->getLocEnd(), 0, *Result.SourceManager, getLangOpts());
+
+  diag(DeleteExpr->getLocStart(),
+       "prefer '= nullptr' to 'delete x.release()' to reset unique_ptr<> "
+       "objects")
+      << FixItHint::CreateRemoval(CharSourceRange::getCharRange(
+             DeleteExpr->getLocStart(), PtrExpr->getLocStart()))
+      << FixItHint::CreateReplacement(
+             CharSourceRange::getTokenRange(AfterPtr, DeleteExpr->getLocEnd()),
+             " = nullptr");
+}
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/readability/UniqueptrDeleteReleaseCheck.h b/clang-tidy/readability/UniqueptrDeleteReleaseCheck.h
new file mode 100644 (file)
index 0000000..fd86bdb
--- /dev/null
@@ -0,0 +1,36 @@
+//===--- UniqueptrDeleteReleaseCheck.h - clang-tidy--------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_UNIQUEPTR_DELETE_RELEASE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_UNIQUEPTR_DELETE_RELEASE_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+/// Flags statements of the form ``delete <unique_ptr expr>.release();`` and
+/// replaces them with: ``<unique_ptr expr> = nullptr;``
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability-uniqueptr-delete-release.html
+class UniqueptrDeleteReleaseCheck : public ClangTidyCheck {
+public:
+  UniqueptrDeleteReleaseCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_UNIQUEPTR_DELETE_RELEASE_H
diff --git a/clang-tidy/rename_check.py b/clang-tidy/rename_check.py
new file mode 100755 (executable)
index 0000000..e739a38
--- /dev/null
@@ -0,0 +1,108 @@
+#!/usr/bin/env python
+#
+#===- rename_check.py - clang-tidy check renamer -------------*- python -*--===#
+#
+#                     The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+#
+#===------------------------------------------------------------------------===#
+
+import os
+import glob
+import argparse
+
+
+def replaceInFile(fileName, sFrom, sTo):
+  if sFrom == sTo:
+    return
+  txt = None
+  with open(fileName, "r") as f:
+    txt = f.read()
+
+  if sFrom not in txt:
+    return
+
+  txt = txt.replace(sFrom, sTo)
+  print("Replace '%s' -> '%s' in '%s'" % (sFrom, sTo, fileName))
+  with open(fileName, "w") as f:
+    f.write(txt)
+
+
+def generateCommentLineHeader(filename):
+  return ''.join(['//===--- ',
+                  os.path.basename(filename),
+                  ' - clang-tidy ',
+                  '-' * max(0, 42 - len(os.path.basename(filename))),
+                  '*- C++ -*-===//'])
+
+
+def generateCommentLineSource(filename):
+  return ''.join(['//===--- ',
+                  os.path.basename(filename),
+                  ' - clang-tidy',
+                  '-' * max(0, 52 - len(os.path.basename(filename))),
+                  '-===//'])
+
+
+def fileRename(fileName, sFrom, sTo):
+  if sFrom not in fileName:
+    return fileName
+  newFileName = fileName.replace(sFrom, sTo)
+  print("Rename '%s' -> '%s'" % (fileName, newFileName))
+  os.rename(fileName, newFileName)
+  return newFileName
+
+
+def getListOfFiles(clang_tidy_path):
+  files = glob.glob(os.path.join(clang_tidy_path, '*'))
+  for dirname in files:
+    if os.path.isdir(dirname):
+      files += glob.glob(os.path.join(dirname, '*'))
+  files += glob.glob(os.path.join(clang_tidy_path, '..', 'test',
+                                  'clang-tidy', '*'))
+  files += glob.glob(os.path.join(clang_tidy_path, '..', 'docs',
+                                  'clang-tidy', 'checks', '*'))
+  return [filename for filename in files if os.path.isfile(filename)]
+
+
+def main():
+  parser = argparse.ArgumentParser(description='Rename clang-tidy check.')
+  parser.add_argument('module', type=str,
+                      help='Module where the renamed check is defined')
+  parser.add_argument('old_check_name', type=str,
+                      help='Old check name.')
+  parser.add_argument('new_check_name', type=str,
+                      help='New check name.')
+  args = parser.parse_args()
+
+  args.module = args.module.lower()
+  check_name_camel = ''.join(map(lambda elem: elem.capitalize(),
+                                 args.old_check_name.split('-'))) + 'Check'
+  check_name_new_camel = (''.join(map(lambda elem: elem.capitalize(),
+                                      args.new_check_name.split('-'))) +
+                          'Check')
+
+  clang_tidy_path = os.path.dirname(__file__)
+
+  header_guard_old = (args.module.upper() + '_' +
+                      args.old_check_name.upper().replace('-', '_'))
+  header_guard_new = (args.module.upper() + '_' +
+                      args.new_check_name.upper().replace('-', '_'))
+
+  for filename in getListOfFiles(clang_tidy_path):
+    originalName = filename
+    filename = fileRename(filename, args.old_check_name,
+                          args.new_check_name)
+    filename = fileRename(filename, check_name_camel, check_name_new_camel)
+    replaceInFile(filename, generateCommentLineHeader(originalName),
+                  generateCommentLineHeader(filename))
+    replaceInFile(filename, generateCommentLineSource(originalName),
+                  generateCommentLineSource(filename))
+    replaceInFile(filename, header_guard_old, header_guard_new)
+    replaceInFile(filename, args.old_check_name, args.new_check_name)
+    replaceInFile(filename, check_name_camel, check_name_new_camel)
+
+if __name__ == '__main__':
+  main()
diff --git a/clang-tidy/tool/CMakeLists.txt b/clang-tidy/tool/CMakeLists.txt
new file mode 100644 (file)
index 0000000..b8c34f3
--- /dev/null
@@ -0,0 +1,37 @@
+set(LLVM_LINK_COMPONENTS
+  support
+  )
+
+add_clang_tool(clang-tidy
+  ClangTidyMain.cpp
+  )
+add_dependencies(clang-tidy
+  clang-headers
+  )
+target_link_libraries(clang-tidy
+  clangAST
+  clangASTMatchers
+  clangBasic
+  clangTidy
+  clangTidyAndroidModule
+  clangTidyBoostModule
+  clangTidyBugproneModule
+  clangTidyCERTModule
+  clangTidyCppCoreGuidelinesModule
+  clangTidyGoogleModule
+  clangTidyHICPPModule
+  clangTidyLLVMModule
+  clangTidyMiscModule
+  clangTidyModernizeModule
+  clangTidyMPIModule
+  clangTidyPerformanceModule
+  clangTidyReadabilityModule
+  clangTooling
+  clangToolingCore
+  )
+
+install(TARGETS clang-tidy
+  RUNTIME DESTINATION bin)
+
+install(PROGRAMS clang-tidy-diff.py DESTINATION share/clang)
+install(PROGRAMS run-clang-tidy.py DESTINATION share/clang)
diff --git a/clang-tidy/tool/ClangTidyMain.cpp b/clang-tidy/tool/ClangTidyMain.cpp
new file mode 100644 (file)
index 0000000..803dca1
--- /dev/null
@@ -0,0 +1,525 @@
+//===--- tools/extra/clang-tidy/ClangTidyMain.cpp - Clang tidy tool -------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+///  \file This file implements a clang-tidy tool.
+///
+///  This tool uses the Clang Tooling infrastructure, see
+///    http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html
+///  for details on setting it up with LLVM source tree.
+///
+//===----------------------------------------------------------------------===//
+
+#include "../ClangTidy.h"
+#include "clang/Tooling/CommonOptionsParser.h"
+#include "llvm/Support/Process.h"
+
+using namespace clang::ast_matchers;
+using namespace clang::driver;
+using namespace clang::tooling;
+using namespace llvm;
+
+static cl::OptionCategory ClangTidyCategory("clang-tidy options");
+
+static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage);
+static cl::extrahelp ClangTidyHelp(R"(
+Configuration files:
+  clang-tidy attempts to read configuration for each source file from a
+  .clang-tidy file located in the closest parent directory of the source
+  file. If any configuration options have a corresponding command-line
+  option, command-line option takes precedence. The effective
+  configuration can be inspected using -dump-config:
+
+    $ clang-tidy -dump-config
+    ---
+    Checks:          '-*,some-check'
+    WarningsAsErrors: ''
+    HeaderFilterRegex: ''
+    AnalyzeTemporaryDtors: false
+    FormatStyle:     none
+    User:            user
+    CheckOptions:
+      - key:             some-check.SomeOption
+        value:           'some value'
+    ...
+
+)");
+
+const char DefaultChecks[] = // Enable these checks by default:
+    "clang-diagnostic-*,"    //   * compiler diagnostics
+    "clang-analyzer-*";      //   * Static Analyzer checks
+
+static cl::opt<std::string> Checks("checks", cl::desc(R"(
+Comma-separated list of globs with optional '-'
+prefix. Globs are processed in order of
+appearance in the list. Globs without '-'
+prefix add checks with matching names to the
+set, globs with the '-' prefix remove checks
+with matching names from the set of enabled
+checks. This option's value is appended to the
+value of the 'Checks' option in .clang-tidy
+file, if any.
+)"),
+                                   cl::init(""), cl::cat(ClangTidyCategory));
+
+static cl::opt<std::string> WarningsAsErrors("warnings-as-errors", cl::desc(R"(
+Upgrades warnings to errors. Same format as
+'-checks'.
+This option's value is appended to the value of
+the 'WarningsAsErrors' option in .clang-tidy
+file, if any.
+)"),
+                                             cl::init(""),
+                                             cl::cat(ClangTidyCategory));
+
+static cl::opt<std::string> HeaderFilter("header-filter", cl::desc(R"(
+Regular expression matching the names of the
+headers to output diagnostics from. Diagnostics
+from the main file of each translation unit are
+always displayed.
+Can be used together with -line-filter.
+This option overrides the 'HeaderFilter' option
+in .clang-tidy file, if any.
+)"),
+                                         cl::init(""),
+                                         cl::cat(ClangTidyCategory));
+
+static cl::opt<bool>
+    SystemHeaders("system-headers",
+                  cl::desc("Display the errors from system headers."),
+                  cl::init(false), cl::cat(ClangTidyCategory));
+static cl::opt<std::string> LineFilter("line-filter", cl::desc(R"(
+List of files with line ranges to filter the
+warnings. Can be used together with
+-header-filter. The format of the list is a
+JSON array of objects:
+  [
+    {"name":"file1.cpp","lines":[[1,3],[5,7]]},
+    {"name":"file2.h"}
+  ]
+)"),
+                                       cl::init(""),
+                                       cl::cat(ClangTidyCategory));
+
+static cl::opt<bool> Fix("fix", cl::desc(R"(
+Apply suggested fixes. Without -fix-errors
+clang-tidy will bail out if any compilation
+errors were found.
+)"),
+                         cl::init(false), cl::cat(ClangTidyCategory));
+
+static cl::opt<bool> FixErrors("fix-errors", cl::desc(R"(
+Apply suggested fixes even if compilation
+errors were found. If compiler errors have
+attached fix-its, clang-tidy will apply them as
+well.
+)"),
+                               cl::init(false), cl::cat(ClangTidyCategory));
+
+static cl::opt<std::string> FormatStyle("format-style", cl::desc(R"(
+Style for formatting code around applied fixes:
+  - 'none' (default) turns off formatting
+  - 'file' (literally 'file', not a placeholder)
+    uses .clang-format file in the closest parent
+    directory
+  - '{ <json> }' specifies options inline, e.g.
+    -format-style='{BasedOnStyle: llvm, IndentWidth: 8}'
+  - 'llvm', 'google', 'webkit', 'mozilla'
+See clang-format documentation for the up-to-date
+information about formatting styles and options.
+This option overrides the 'FormatStyle` option in
+.clang-tidy file, if any.
+)"),
+                                   cl::init("none"),
+                                   cl::cat(ClangTidyCategory));
+
+static cl::opt<bool> ListChecks("list-checks", cl::desc(R"(
+List all enabled checks and exit. Use with
+-checks=* to list all available checks.
+)"),
+                                cl::init(false), cl::cat(ClangTidyCategory));
+
+static cl::opt<bool> ExplainConfig("explain-config", cl::desc(R"(
+For each enabled check explains, where it is
+enabled, i.e. in clang-tidy binary, command
+line or a specific configuration file.
+)"),
+                                   cl::init(false), cl::cat(ClangTidyCategory));
+
+static cl::opt<std::string> Config("config", cl::desc(R"(
+Specifies a configuration in YAML/JSON format:
+  -config="{Checks: '*',
+            CheckOptions: [{key: x,
+                            value: y}]}"
+When the value is empty, clang-tidy will
+attempt to find a file named .clang-tidy for
+each source file in its parent directories.
+)"),
+                                   cl::init(""), cl::cat(ClangTidyCategory));
+
+static cl::opt<bool> DumpConfig("dump-config", cl::desc(R"(
+Dumps configuration in the YAML format to
+stdout. This option can be used along with a
+file name (and '--' if the file is outside of a
+project with configured compilation database).
+The configuration used for this file will be
+printed.
+Use along with -checks=* to include
+configuration of all checks.
+)"),
+                                cl::init(false), cl::cat(ClangTidyCategory));
+
+static cl::opt<bool> EnableCheckProfile("enable-check-profile", cl::desc(R"(
+Enable per-check timing profiles, and print a
+report to stderr.
+)"),
+                                        cl::init(false),
+                                        cl::cat(ClangTidyCategory));
+
+static cl::opt<bool> AnalyzeTemporaryDtors("analyze-temporary-dtors",
+                                           cl::desc(R"(
+Enable temporary destructor-aware analysis in
+clang-analyzer- checks.
+This option overrides the value read from a
+.clang-tidy file.
+)"),
+                                           cl::init(false),
+                                           cl::cat(ClangTidyCategory));
+
+static cl::opt<std::string> ExportFixes("export-fixes", cl::desc(R"(
+YAML file to store suggested fixes in. The
+stored fixes can be applied to the input source
+code with clang-apply-replacements.
+)"),
+                                        cl::value_desc("filename"),
+                                        cl::cat(ClangTidyCategory));
+
+static cl::opt<bool> Quiet("quiet", cl::desc(R"(
+Run clang-tidy in quiet mode. This suppresses
+printing statistics about ignored warnings and
+warnings treated as errors if the respective
+options are specified.
+)"),
+                           cl::init(false),
+                           cl::cat(ClangTidyCategory));
+
+namespace clang {
+namespace tidy {
+
+static void printStats(const ClangTidyStats &Stats) {
+  if (Stats.errorsIgnored()) {
+    llvm::errs() << "Suppressed " << Stats.errorsIgnored() << " warnings (";
+    StringRef Separator = "";
+    if (Stats.ErrorsIgnoredNonUserCode) {
+      llvm::errs() << Stats.ErrorsIgnoredNonUserCode << " in non-user code";
+      Separator = ", ";
+    }
+    if (Stats.ErrorsIgnoredLineFilter) {
+      llvm::errs() << Separator << Stats.ErrorsIgnoredLineFilter
+                   << " due to line filter";
+      Separator = ", ";
+    }
+    if (Stats.ErrorsIgnoredNOLINT) {
+      llvm::errs() << Separator << Stats.ErrorsIgnoredNOLINT << " NOLINT";
+      Separator = ", ";
+    }
+    if (Stats.ErrorsIgnoredCheckFilter)
+      llvm::errs() << Separator << Stats.ErrorsIgnoredCheckFilter
+                   << " with check filters";
+    llvm::errs() << ").\n";
+    if (Stats.ErrorsIgnoredNonUserCode)
+      llvm::errs() << "Use -header-filter=.* to display errors from all "
+                      "non-system headers. Use -system-headers to display "
+                      "errors from system headers as well.\n";
+  }
+}
+
+static void printProfileData(const ProfileData &Profile,
+                             llvm::raw_ostream &OS) {
+  // Time is first to allow for sorting by it.
+  std::vector<std::pair<llvm::TimeRecord, StringRef>> Timers;
+  TimeRecord Total;
+
+  for (const auto &P : Profile.Records) {
+    Timers.emplace_back(P.getValue(), P.getKey());
+    Total += P.getValue();
+  }
+
+  std::sort(Timers.begin(), Timers.end());
+
+  std::string Line = "===" + std::string(73, '-') + "===\n";
+  OS << Line;
+
+  if (Total.getUserTime())
+    OS << "   ---User Time---";
+  if (Total.getSystemTime())
+    OS << "   --System Time--";
+  if (Total.getProcessTime())
+    OS << "   --User+System--";
+  OS << "   ---Wall Time---";
+  if (Total.getMemUsed())
+    OS << "  ---Mem---";
+  OS << "  --- Name ---\n";
+
+  // Loop through all of the timing data, printing it out.
+  for (auto I = Timers.rbegin(), E = Timers.rend(); I != E; ++I) {
+    I->first.print(Total, OS);
+    OS << I->second << '\n';
+  }
+
+  Total.print(Total, OS);
+  OS << "Total\n";
+  OS << Line << "\n";
+  OS.flush();
+}
+
+static std::unique_ptr<ClangTidyOptionsProvider> createOptionsProvider() {
+  ClangTidyGlobalOptions GlobalOptions;
+  if (std::error_code Err = parseLineFilter(LineFilter, GlobalOptions)) {
+    llvm::errs() << "Invalid LineFilter: " << Err.message() << "\n\nUsage:\n";
+    llvm::cl::PrintHelpMessage(/*Hidden=*/false, /*Categorized=*/true);
+    return nullptr;
+  }
+
+  ClangTidyOptions DefaultOptions;
+  DefaultOptions.Checks = DefaultChecks;
+  DefaultOptions.WarningsAsErrors = "";
+  DefaultOptions.HeaderFilterRegex = HeaderFilter;
+  DefaultOptions.SystemHeaders = SystemHeaders;
+  DefaultOptions.AnalyzeTemporaryDtors = AnalyzeTemporaryDtors;
+  DefaultOptions.FormatStyle = FormatStyle;
+  DefaultOptions.User = llvm::sys::Process::GetEnv("USER");
+  // USERNAME is used on Windows.
+  if (!DefaultOptions.User)
+    DefaultOptions.User = llvm::sys::Process::GetEnv("USERNAME");
+
+  ClangTidyOptions OverrideOptions;
+  if (Checks.getNumOccurrences() > 0)
+    OverrideOptions.Checks = Checks;
+  if (WarningsAsErrors.getNumOccurrences() > 0)
+    OverrideOptions.WarningsAsErrors = WarningsAsErrors;
+  if (HeaderFilter.getNumOccurrences() > 0)
+    OverrideOptions.HeaderFilterRegex = HeaderFilter;
+  if (SystemHeaders.getNumOccurrences() > 0)
+    OverrideOptions.SystemHeaders = SystemHeaders;
+  if (AnalyzeTemporaryDtors.getNumOccurrences() > 0)
+    OverrideOptions.AnalyzeTemporaryDtors = AnalyzeTemporaryDtors;
+  if (FormatStyle.getNumOccurrences() > 0)
+    OverrideOptions.FormatStyle = FormatStyle;
+
+  if (!Config.empty()) {
+    if (llvm::ErrorOr<ClangTidyOptions> ParsedConfig =
+            parseConfiguration(Config)) {
+      return llvm::make_unique<ConfigOptionsProvider>(
+          GlobalOptions,
+          ClangTidyOptions::getDefaults().mergeWith(DefaultOptions),
+          *ParsedConfig, OverrideOptions);
+    } else {
+      llvm::errs() << "Error: invalid configuration specified.\n"
+                   << ParsedConfig.getError().message() << "\n";
+      return nullptr;
+    }
+  }
+  return llvm::make_unique<FileOptionsProvider>(GlobalOptions, DefaultOptions,
+                                                OverrideOptions);
+}
+
+static int clangTidyMain(int argc, const char **argv) {
+  CommonOptionsParser OptionsParser(argc, argv, ClangTidyCategory,
+                                    cl::ZeroOrMore);
+
+  auto OwningOptionsProvider = createOptionsProvider();
+  auto *OptionsProvider = OwningOptionsProvider.get();
+  if (!OptionsProvider)
+    return 1;
+
+  StringRef FileName("dummy");
+  auto PathList = OptionsParser.getSourcePathList();
+  if (!PathList.empty()) {
+    FileName = PathList.front();
+  }
+
+  SmallString<256> FilePath(FileName);
+  if (std::error_code EC = llvm::sys::fs::make_absolute(FilePath)) {
+    llvm::errs() << "Can't make absolute path from " << FileName << ": "
+                 << EC.message() << "\n";
+  }
+  ClangTidyOptions EffectiveOptions = OptionsProvider->getOptions(FilePath);
+  std::vector<std::string> EnabledChecks = getCheckNames(EffectiveOptions);
+
+  if (ExplainConfig) {
+    // FIXME: Show other ClangTidyOptions' fields, like ExtraArg.
+    std::vector<clang::tidy::ClangTidyOptionsProvider::OptionsSource>
+        RawOptions = OptionsProvider->getRawOptions(FilePath);
+    for (const std::string &Check : EnabledChecks) {
+      for (auto It = RawOptions.rbegin(); It != RawOptions.rend(); ++It) {
+        if (It->first.Checks && GlobList(*It->first.Checks).contains(Check)) {
+          llvm::outs() << "'" << Check << "' is enabled in the " << It->second
+                       << ".\n";
+          break;
+        }
+      }
+    }
+    return 0;
+  }
+
+  if (ListChecks) {
+    if (EnabledChecks.empty()) {
+      llvm::errs() << "No checks enabled.\n";
+      return 1;
+    }
+    llvm::outs() << "Enabled checks:";
+    for (const auto &CheckName : EnabledChecks)
+      llvm::outs() << "\n    " << CheckName;
+    llvm::outs() << "\n\n";
+    return 0;
+  }
+
+  if (DumpConfig) {
+    EffectiveOptions.CheckOptions = getCheckOptions(EffectiveOptions);
+    llvm::outs() << configurationAsText(
+                        ClangTidyOptions::getDefaults().mergeWith(
+                            EffectiveOptions))
+                 << "\n";
+    return 0;
+  }
+
+  if (EnabledChecks.empty()) {
+    llvm::errs() << "Error: no checks enabled.\n";
+    llvm::cl::PrintHelpMessage(/*Hidden=*/false, /*Categorized=*/true);
+    return 1;
+  }
+
+  if (PathList.empty()) {
+    llvm::errs() << "Error: no input files specified.\n";
+    llvm::cl::PrintHelpMessage(/*Hidden=*/false, /*Categorized=*/true);
+    return 1;
+  }
+
+  ProfileData Profile;
+
+  ClangTidyContext Context(std::move(OwningOptionsProvider));
+  runClangTidy(Context, OptionsParser.getCompilations(), PathList,
+               EnableCheckProfile ? &Profile : nullptr);
+  ArrayRef<ClangTidyError> Errors = Context.getErrors();
+  bool FoundErrors =
+      std::find_if(Errors.begin(), Errors.end(), [](const ClangTidyError &E) {
+        return E.DiagLevel == ClangTidyError::Error;
+      }) != Errors.end();
+
+  const bool DisableFixes = Fix && FoundErrors && !FixErrors;
+
+  unsigned WErrorCount = 0;
+
+  // -fix-errors implies -fix.
+  handleErrors(Context, (FixErrors || Fix) && !DisableFixes, WErrorCount);
+
+  if (!ExportFixes.empty() && !Errors.empty()) {
+    std::error_code EC;
+    llvm::raw_fd_ostream OS(ExportFixes, EC, llvm::sys::fs::F_None);
+    if (EC) {
+      llvm::errs() << "Error opening output file: " << EC.message() << '\n';
+      return 1;
+    }
+    exportReplacements(FilePath.str(), Errors, OS);
+  }
+
+  if (!Quiet) {
+    printStats(Context.getStats());
+    if (DisableFixes)
+      llvm::errs()
+          << "Found compiler errors, but -fix-errors was not specified.\n"
+             "Fixes have NOT been applied.\n\n";
+  }
+
+  if (EnableCheckProfile)
+    printProfileData(Profile, llvm::errs());
+
+  if (WErrorCount) {
+    if (!Quiet) {
+      StringRef Plural = WErrorCount == 1 ? "" : "s";
+      llvm::errs() << WErrorCount << " warning" << Plural << " treated as error"
+                   << Plural << "\n";
+    }
+    return WErrorCount;
+  }
+
+  return 0;
+}
+
+// This anchor is used to force the linker to link the CERTModule.
+extern volatile int CERTModuleAnchorSource;
+static int LLVM_ATTRIBUTE_UNUSED CERTModuleAnchorDestination =
+    CERTModuleAnchorSource;
+
+// This anchor is used to force the linker to link the BoostModule.
+extern volatile int BoostModuleAnchorSource;
+static int LLVM_ATTRIBUTE_UNUSED BoostModuleAnchorDestination =
+    BoostModuleAnchorSource;
+
+// This anchor is used to force the linker to link the BugproneModule.
+extern volatile int BugproneModuleAnchorSource;
+static int LLVM_ATTRIBUTE_UNUSED BugproneModuleAnchorDestination =
+    BugproneModuleAnchorSource;
+
+// This anchor is used to force the linker to link the LLVMModule.
+extern volatile int LLVMModuleAnchorSource;
+static int LLVM_ATTRIBUTE_UNUSED LLVMModuleAnchorDestination =
+    LLVMModuleAnchorSource;
+
+// This anchor is used to force the linker to link the CppCoreGuidelinesModule.
+extern volatile int CppCoreGuidelinesModuleAnchorSource;
+static int LLVM_ATTRIBUTE_UNUSED CppCoreGuidelinesModuleAnchorDestination =
+    CppCoreGuidelinesModuleAnchorSource;
+
+// This anchor is used to force the linker to link the GoogleModule.
+extern volatile int GoogleModuleAnchorSource;
+static int LLVM_ATTRIBUTE_UNUSED GoogleModuleAnchorDestination =
+    GoogleModuleAnchorSource;
+
+// This anchor is used to force the linker to link the AndroidModule.
+extern volatile int AndroidModuleAnchorSource;
+static int LLVM_ATTRIBUTE_UNUSED AndroidModuleAnchorDestination =
+    AndroidModuleAnchorSource;
+
+// This anchor is used to force the linker to link the MiscModule.
+extern volatile int MiscModuleAnchorSource;
+static int LLVM_ATTRIBUTE_UNUSED MiscModuleAnchorDestination =
+    MiscModuleAnchorSource;
+
+// This anchor is used to force the linker to link the ModernizeModule.
+extern volatile int ModernizeModuleAnchorSource;
+static int LLVM_ATTRIBUTE_UNUSED ModernizeModuleAnchorDestination =
+    ModernizeModuleAnchorSource;
+
+// This anchor is used to force the linker to link the MPIModule.
+extern volatile int MPIModuleAnchorSource;
+static int LLVM_ATTRIBUTE_UNUSED MPIModuleAnchorDestination =
+    MPIModuleAnchorSource;
+
+// This anchor is used to force the linker to link the PerformanceModule.
+extern volatile int PerformanceModuleAnchorSource;
+static int LLVM_ATTRIBUTE_UNUSED PerformanceModuleAnchorDestination =
+    PerformanceModuleAnchorSource;
+
+// This anchor is used to force the linker to link the ReadabilityModule.
+extern volatile int ReadabilityModuleAnchorSource;
+static int LLVM_ATTRIBUTE_UNUSED ReadabilityModuleAnchorDestination =
+    ReadabilityModuleAnchorSource;
+
+// This anchor is used to force the linker to link the HICPPModule.
+extern volatile int HICPPModuleAnchorSource;
+static int LLVM_ATTRIBUTE_UNUSED HICPPModuleAnchorDestination =
+    HICPPModuleAnchorSource;
+
+} // namespace tidy
+} // namespace clang
+
+int main(int argc, const char **argv) {
+  return clang::tidy::clangTidyMain(argc, argv);
+}
diff --git a/clang-tidy/tool/clang-tidy-diff.py b/clang-tidy/tool/clang-tidy-diff.py
new file mode 100755 (executable)
index 0000000..f2c15e5
--- /dev/null
@@ -0,0 +1,141 @@
+#!/usr/bin/env python
+#
+#===- clang-tidy-diff.py - ClangTidy Diff Checker ------------*- python -*--===#
+#
+#                     The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+#
+#===------------------------------------------------------------------------===#
+
+r"""
+ClangTidy Diff Checker
+======================
+
+This script reads input from a unified diff, runs clang-tidy on all changed
+files and outputs clang-tidy warnings in changed lines only. This is useful to
+detect clang-tidy regressions in the lines touched by a specific patch.
+Example usage for git/svn users:
+
+  git diff -U0 HEAD^ | clang-tidy-diff.py -p1
+  svn diff --diff-cmd=diff -x-U0 | \
+      clang-tidy-diff.py -fix -checks=-*,modernize-use-override
+
+"""
+
+import argparse
+import json
+import re
+import subprocess
+import sys
+
+
+def main():
+  parser = argparse.ArgumentParser(description=
+                                   'Run clang-tidy against changed files, and '
+                                   'output diagnostics only for modified '
+                                   'lines.')
+  parser.add_argument('-clang-tidy-binary', metavar='PATH',
+                      default='clang-tidy',
+                      help='path to clang-tidy binary')
+  parser.add_argument('-p', metavar='NUM', default=0,
+                      help='strip the smallest prefix containing P slashes')
+  parser.add_argument('-regex', metavar='PATTERN', default=None,
+                      help='custom pattern selecting file paths to check '
+                      '(case sensitive, overrides -iregex)')
+  parser.add_argument('-iregex', metavar='PATTERN', default=
+                      r'.*\.(cpp|cc|c\+\+|cxx|c|cl|h|hpp|m|mm|inc)',
+                      help='custom pattern selecting file paths to check '
+                      '(case insensitive, overridden by -regex)')
+
+  parser.add_argument('-fix', action='store_true', default=False,
+                      help='apply suggested fixes')
+  parser.add_argument('-checks',
+                      help='checks filter, when not specified, use clang-tidy '
+                      'default',
+                      default='')
+  parser.add_argument('-path', dest='build_path',
+                      help='Path used to read a compile command database.')
+  parser.add_argument('-extra-arg', dest='extra_arg',
+                      action='append', default=[],
+                      help='Additional argument to append to the compiler '
+                      'command line.')
+  parser.add_argument('-extra-arg-before', dest='extra_arg_before',
+                      action='append', default=[],
+                      help='Additional argument to prepend to the compiler '
+                      'command line.')
+  parser.add_argument('-quiet', action='store_true', default=False,
+                      help='Run clang-tidy in quiet mode')
+  clang_tidy_args = []
+  argv = sys.argv[1:]
+  if '--' in argv:
+    clang_tidy_args.extend(argv[argv.index('--'):])
+    argv = argv[:argv.index('--')]
+
+  args = parser.parse_args(argv)
+
+  # Extract changed lines for each file.
+  filename = None
+  lines_by_file = {}
+  for line in sys.stdin:
+    match = re.search('^\+\+\+\ \"?(.*?/){%s}([^ \t\n\"]*)' % args.p, line)
+    if match:
+      filename = match.group(2)
+    if filename == None:
+      continue
+
+    if args.regex is not None:
+      if not re.match('^%s$' % args.regex, filename):
+        continue
+    else:
+      if not re.match('^%s$' % args.iregex, filename, re.IGNORECASE):
+        continue
+
+    match = re.search('^@@.*\+(\d+)(,(\d+))?', line)
+    if match:
+      start_line = int(match.group(1))
+      line_count = 1
+      if match.group(3):
+        line_count = int(match.group(3))
+      if line_count == 0:
+        continue
+      end_line = start_line + line_count - 1;
+      lines_by_file.setdefault(filename, []).append([start_line, end_line])
+
+  if len(lines_by_file) == 0:
+    print("No relevant changes found.")
+    sys.exit(0)
+
+  line_filter_json = json.dumps(
+    [{"name" : name, "lines" : lines_by_file[name]} for name in lines_by_file],
+    separators = (',', ':'))
+
+  quote = "";
+  if sys.platform == 'win32':
+    line_filter_json=re.sub(r'"', r'"""', line_filter_json)
+  else:
+    quote = "'";
+
+  # Run clang-tidy on files containing changes.
+  command = [args.clang_tidy_binary]
+  command.append('-line-filter=' + quote + line_filter_json + quote)
+  if args.fix:
+    command.append('-fix')
+  if args.checks != '':
+    command.append('-checks=' + quote + args.checks + quote)
+  if args.quiet:
+    command.append('-quiet')
+  if args.build_path is not None:
+    command.append('-p=%s' % args.build_path)
+  command.extend(lines_by_file.keys())
+  for arg in args.extra_arg:
+      command.append('-extra-arg=%s' % arg)
+  for arg in args.extra_arg_before:
+      command.append('-extra-arg-before=%s' % arg)
+  command.extend(clang_tidy_args)
+
+  sys.exit(subprocess.call(' '.join(command), shell=True))
+
+if __name__ == '__main__':
+  main()
diff --git a/clang-tidy/tool/run-clang-tidy.py b/clang-tidy/tool/run-clang-tidy.py
new file mode 100755 (executable)
index 0000000..d9d827b
--- /dev/null
@@ -0,0 +1,245 @@
+#!/usr/bin/env python
+#
+#===- run-clang-tidy.py - Parallel clang-tidy runner ---------*- python -*--===#
+#
+#                     The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+#
+#===------------------------------------------------------------------------===#
+# FIXME: Integrate with clang-tidy-diff.py
+
+"""
+Parallel clang-tidy runner
+==========================
+
+Runs clang-tidy over all files in a compilation database. Requires clang-tidy
+and clang-apply-replacements in $PATH.
+
+Example invocations.
+- Run clang-tidy on all files in the current working directory with a default
+  set of checks and show warnings in the cpp files and all project headers.
+    run-clang-tidy.py $PWD
+
+- Fix all header guards.
+    run-clang-tidy.py -fix -checks=-*,llvm-header-guard
+
+- Fix all header guards included from clang-tidy and header guards
+  for clang-tidy headers.
+    run-clang-tidy.py -fix -checks=-*,llvm-header-guard extra/clang-tidy \
+                      -header-filter=extra/clang-tidy
+
+Compilation database setup:
+http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html
+"""
+
+from __future__ import print_function
+import argparse
+import json
+import multiprocessing
+import os
+import Queue
+import re
+import shutil
+import subprocess
+import sys
+import tempfile
+import threading
+import traceback
+
+
+def find_compilation_database(path):
+  """Adjusts the directory until a compilation database is found."""
+  result = './'
+  while not os.path.isfile(os.path.join(result, path)):
+    if os.path.realpath(result) == '/':
+      print('Error: could not find compilation database.')
+      sys.exit(1)
+    result += '../'
+  return os.path.realpath(result)
+
+
+def get_tidy_invocation(f, clang_tidy_binary, checks, tmpdir, build_path,
+                        header_filter, extra_arg, extra_arg_before, quiet):
+  """Gets a command line for clang-tidy."""
+  start = [clang_tidy_binary]
+  if header_filter is not None:
+    start.append('-header-filter=' + header_filter)
+  else:
+    # Show warnings in all in-project headers by default.
+    start.append('-header-filter=^' + build_path + '/.*')
+  if checks:
+    start.append('-checks=' + checks)
+  if tmpdir is not None:
+    start.append('-export-fixes')
+    # Get a temporary file. We immediately close the handle so clang-tidy can
+    # overwrite it.
+    (handle, name) = tempfile.mkstemp(suffix='.yaml', dir=tmpdir)
+    os.close(handle)
+    start.append(name)
+  for arg in extra_arg:
+      start.append('-extra-arg=%s' % arg)
+  for arg in extra_arg_before:
+      start.append('-extra-arg-before=%s' % arg)
+  start.append('-p=' + build_path)
+  if quiet:
+      start.append('-quiet')
+  start.append(f)
+  return start
+
+
+def check_clang_apply_replacements_binary(args):
+  """Checks if invoking supplied clang-apply-replacements binary works."""
+  try:
+    subprocess.check_call([args.clang_apply_replacements_binary, '--version'])
+  except:
+    print('Unable to run clang-apply-replacements. Is clang-apply-replacements '
+          'binary correctly specified?', file=sys.stderr)
+    traceback.print_exc()
+    sys.exit(1)
+
+
+def apply_fixes(args, tmpdir):
+  """Calls clang-apply-fixes on a given directory. Deletes the dir when done."""
+  invocation = [args.clang_apply_replacements_binary]
+  if args.format:
+    invocation.append('-format')
+  if args.style:
+    invocation.append('-style=' + args.style)
+  invocation.append(tmpdir)
+  subprocess.call(invocation)
+
+
+def run_tidy(args, tmpdir, build_path, queue):
+  """Takes filenames out of queue and runs clang-tidy on them."""
+  while True:
+    name = queue.get()
+    invocation = get_tidy_invocation(name, args.clang_tidy_binary, args.checks,
+                                     tmpdir, build_path, args.header_filter,
+                                     args.extra_arg, args.extra_arg_before,
+                                     args.quiet)
+    sys.stdout.write(' '.join(invocation) + '\n')
+    subprocess.call(invocation)
+    queue.task_done()
+
+
+def main():
+  parser = argparse.ArgumentParser(description='Runs clang-tidy over all files '
+                                   'in a compilation database. Requires '
+                                   'clang-tidy and clang-apply-replacements in '
+                                   '$PATH.')
+  parser.add_argument('-clang-tidy-binary', metavar='PATH',
+                      default='clang-tidy',
+                      help='path to clang-tidy binary')
+  parser.add_argument('-clang-apply-replacements-binary', metavar='PATH',
+                      default='clang-apply-replacements',
+                      help='path to clang-apply-replacements binary')
+  parser.add_argument('-checks', default=None,
+                      help='checks filter, when not specified, use clang-tidy '
+                      'default')
+  parser.add_argument('-header-filter', default=None,
+                      help='regular expression matching the names of the '
+                      'headers to output diagnostics from. Diagnostics from '
+                      'the main file of each translation unit are always '
+                      'displayed.')
+  parser.add_argument('-j', type=int, default=0,
+                      help='number of tidy instances to be run in parallel.')
+  parser.add_argument('files', nargs='*', default=['.*'],
+                      help='files to be processed (regex on path)')
+  parser.add_argument('-fix', action='store_true', help='apply fix-its')
+  parser.add_argument('-format', action='store_true', help='Reformat code '
+                      'after applying fixes')
+  parser.add_argument('-style', default='file', help='The style of reformat '
+                      'code after applying fixes')
+  parser.add_argument('-p', dest='build_path',
+                      help='Path used to read a compile command database.')
+  parser.add_argument('-extra-arg', dest='extra_arg',
+                      action='append', default=[],
+                      help='Additional argument to append to the compiler '
+                      'command line.')
+  parser.add_argument('-extra-arg-before', dest='extra_arg_before',
+                      action='append', default=[],
+                      help='Additional argument to prepend to the compiler '
+                      'command line.')
+  parser.add_argument('-quiet', action='store_true',
+                      help='Run clang-tidy in quiet mode')
+  args = parser.parse_args()
+
+  db_path = 'compile_commands.json'
+
+  if args.build_path is not None:
+    build_path = args.build_path
+  else:
+    # Find our database
+    build_path = find_compilation_database(db_path)
+
+  try:
+    invocation = [args.clang_tidy_binary, '-list-checks']
+    invocation.append('-p=' + build_path)
+    if args.checks:
+      invocation.append('-checks=' + args.checks)
+    invocation.append('-')
+    print(subprocess.check_output(invocation))
+  except:
+    print("Unable to run clang-tidy.", file=sys.stderr)
+    sys.exit(1)
+
+  # Load the database and extract all files.
+  database = json.load(open(os.path.join(build_path, db_path)))
+  files = [entry['file'] for entry in database]
+
+  max_task = args.j
+  if max_task == 0:
+    max_task = multiprocessing.cpu_count()
+
+  tmpdir = None
+  if args.fix:
+    check_clang_apply_replacements_binary(args)
+    tmpdir = tempfile.mkdtemp()
+
+  # Build up a big regexy filter from all command line arguments.
+  file_name_re = re.compile('|'.join(args.files))
+
+  try:
+    # Spin up a bunch of tidy-launching threads.
+    queue = Queue.Queue(max_task)
+    for _ in range(max_task):
+      t = threading.Thread(target=run_tidy,
+                           args=(args, tmpdir, build_path, queue))
+      t.daemon = True
+      t.start()
+
+    # Fill the queue with files.
+    for name in files:
+      if file_name_re.search(name):
+        queue.put(name)
+
+    # Wait for all threads to be done.
+    queue.join()
+
+  except KeyboardInterrupt:
+    # This is a sad hack. Unfortunately subprocess goes
+    # bonkers with ctrl-c and we start forking merrily.
+    print('\nCtrl-C detected, goodbye.')
+    if args.fix:
+      shutil.rmtree(tmpdir)
+    os.kill(0, 9)
+
+  if args.fix:
+    print('Applying fixes ...')
+    successfully_applied = False
+
+    try:
+      apply_fixes(args, tmpdir)
+      successfully_applied = True
+    except:
+      print('Error applying fixes.\n', file=sys.stderr)
+      traceback.print_exc()
+
+    shutil.rmtree(tmpdir)
+    if not successfully_applied:
+      sys.exit(1)
+
+if __name__ == '__main__':
+  main()
diff --git a/clang-tidy/utils/ASTUtils.cpp b/clang-tidy/utils/ASTUtils.cpp
new file mode 100644 (file)
index 0000000..1efcec9
--- /dev/null
@@ -0,0 +1,72 @@
+//===---------- ASTUtils.cpp - clang-tidy ---------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ASTUtils.h"
+
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Lex/Lexer.h"
+
+namespace clang {
+namespace tidy {
+namespace utils {
+using namespace ast_matchers;
+
+const FunctionDecl *getSurroundingFunction(ASTContext &Context,
+                                           const Stmt &Statement) {
+  return selectFirst<const FunctionDecl>(
+      "function", match(stmt(hasAncestor(functionDecl().bind("function"))),
+                        Statement, Context));
+}
+
+bool IsBinaryOrTernary(const Expr *E) {
+  const Expr *E_base = E->IgnoreImpCasts();
+  if (clang::isa<clang::BinaryOperator>(E_base) ||
+      clang::isa<clang::ConditionalOperator>(E_base)) {
+    return true;
+  }
+
+  if (const auto *Operator =
+          clang::dyn_cast<clang::CXXOperatorCallExpr>(E_base)) {
+    return Operator->isInfixBinaryOp();
+  }
+
+  return false;
+}
+
+bool exprHasBitFlagWithSpelling(const Expr *Flags, const SourceManager &SM,
+                                const LangOptions &LangOpts,
+                                StringRef FlagName) {
+  // If the Flag is an integer constant, check it.
+  if (isa<IntegerLiteral>(Flags)) {
+    if (!SM.isMacroBodyExpansion(Flags->getLocStart()) &&
+        !SM.isMacroArgExpansion(Flags->getLocStart()))
+      return false;
+
+    // Get the marco name.
+    auto MacroName = Lexer::getSourceText(
+        CharSourceRange::getTokenRange(Flags->getSourceRange()), SM, LangOpts);
+
+    return MacroName == FlagName;
+  }
+  // If it's a binary OR operation.
+  if (const auto *BO = dyn_cast<BinaryOperator>(Flags))
+    if (BO->getOpcode() == clang::BinaryOperatorKind::BO_Or)
+      return exprHasBitFlagWithSpelling(BO->getLHS()->IgnoreParenCasts(), SM,
+                                        LangOpts, FlagName) ||
+             exprHasBitFlagWithSpelling(BO->getRHS()->IgnoreParenCasts(), SM,
+                                        LangOpts, FlagName);
+
+  // Otherwise, assume it has the flag.
+  return true;
+}
+
+} // namespace utils
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/utils/ASTUtils.h b/clang-tidy/utils/ASTUtils.h
new file mode 100644 (file)
index 0000000..ccff000
--- /dev/null
@@ -0,0 +1,34 @@
+//===---------- ASTUtils.h - clang-tidy -----------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ASTUTILS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ASTUTILS_H
+
+#include "clang/AST/AST.h"
+
+namespace clang {
+namespace tidy {
+namespace utils {
+// Returns the (closest) Function declaration surrounding |Statement| or NULL.
+const FunctionDecl *getSurroundingFunction(ASTContext &Context,
+                                           const Stmt &Statement);
+// Determine whether Expr is a Binary or Ternary expression.
+bool IsBinaryOrTernary(const Expr *E);
+
+/// Checks whether a macro flag is present in the given argument. Only considers
+/// cases of single match or match in a binary OR expression. For example,
+/// <needed-flag> or <flag> | <needed-flag> | ...
+bool exprHasBitFlagWithSpelling(const Expr *Flags, const SourceManager &SM,
+                                const LangOptions &LangOpts,
+                                StringRef FlagName);
+} // namespace utils
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ASTUTILS_H
diff --git a/clang-tidy/utils/CMakeLists.txt b/clang-tidy/utils/CMakeLists.txt
new file mode 100644 (file)
index 0000000..9162bce
--- /dev/null
@@ -0,0 +1,24 @@
+set(LLVM_LINK_COMPONENTS support)
+
+add_clang_library(clangTidyUtils
+  ASTUtils.cpp
+  DeclRefExprUtils.cpp
+  ExprSequence.cpp
+  FixItHintUtils.cpp
+  HeaderFileExtensionsUtils.cpp
+  HeaderGuard.cpp
+  IncludeInserter.cpp
+  IncludeSorter.cpp
+  LexerUtils.cpp
+  NamespaceAliaser.cpp
+  OptionsUtils.cpp
+  TypeTraits.cpp
+  UsingInserter.cpp
+
+  LINK_LIBS
+  clangAST
+  clangASTMatchers
+  clangBasic
+  clangLex
+  clangTidy
+  )
diff --git a/clang-tidy/utils/DeclRefExprUtils.cpp b/clang-tidy/utils/DeclRefExprUtils.cpp
new file mode 100644 (file)
index 0000000..06acd31
--- /dev/null
@@ -0,0 +1,172 @@
+//===--- DeclRefExprUtils.cpp - clang-tidy---------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "DeclRefExprUtils.h"
+#include "Matchers.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+namespace clang {
+namespace tidy {
+namespace utils {
+namespace decl_ref_expr {
+
+using namespace ::clang::ast_matchers;
+using llvm::SmallPtrSet;
+
+namespace {
+
+template <typename S> bool isSetDifferenceEmpty(const S &S1, const S &S2) {
+  for (const auto &E : S1)
+    if (S2.count(E) == 0)
+      return false;
+  return true;
+}
+
+// Extracts all Nodes keyed by ID from Matches and inserts them into Nodes.
+template <typename Node>
+void extractNodesByIdTo(ArrayRef<BoundNodes> Matches, StringRef ID,
+                        SmallPtrSet<const Node *, 16> &Nodes) {
+  for (const auto &Match : Matches)
+    Nodes.insert(Match.getNodeAs<Node>(ID));
+}
+
+} // namespace
+
+// Finds all DeclRefExprs where a const method is called on VarDecl or VarDecl
+// is the a const reference or value argument to a CallExpr or CXXConstructExpr.
+SmallPtrSet<const DeclRefExpr *, 16>
+constReferenceDeclRefExprs(const VarDecl &VarDecl, const Stmt &Stmt,
+                           ASTContext &Context) {
+  auto DeclRefToVar =
+      declRefExpr(to(varDecl(equalsNode(&VarDecl)))).bind("declRef");
+  auto ConstMethodCallee = callee(cxxMethodDecl(isConst()));
+  // Match method call expressions where the variable is referenced as the this
+  // implicit object argument and opertor call expression for member operators
+  // where the variable is the 0-th argument.
+  auto Matches = match(
+      findAll(expr(anyOf(cxxMemberCallExpr(ConstMethodCallee, on(DeclRefToVar)),
+                         cxxOperatorCallExpr(ConstMethodCallee,
+                                             hasArgument(0, DeclRefToVar))))),
+      Stmt, Context);
+  SmallPtrSet<const DeclRefExpr *, 16> DeclRefs;
+  extractNodesByIdTo(Matches, "declRef", DeclRefs);
+  auto ConstReferenceOrValue =
+      qualType(anyOf(referenceType(pointee(qualType(isConstQualified()))),
+                     unless(anyOf(referenceType(), pointerType()))));
+  auto UsedAsConstRefOrValueArg = forEachArgumentWithParam(
+      DeclRefToVar, parmVarDecl(hasType(ConstReferenceOrValue)));
+  Matches = match(findAll(callExpr(UsedAsConstRefOrValueArg)), Stmt, Context);
+  extractNodesByIdTo(Matches, "declRef", DeclRefs);
+  Matches =
+      match(findAll(cxxConstructExpr(UsedAsConstRefOrValueArg)), Stmt, Context);
+  extractNodesByIdTo(Matches, "declRef", DeclRefs);
+  return DeclRefs;
+}
+
+// Finds all DeclRefExprs where a const method is called on VarDecl or VarDecl
+// is the a const reference or value argument to a CallExpr or CXXConstructExpr.
+SmallPtrSet<const DeclRefExpr *, 16>
+constReferenceDeclRefExprs(const VarDecl &VarDecl, const Decl &Decl,
+                           ASTContext &Context) {
+  auto DeclRefToVar =
+      declRefExpr(to(varDecl(equalsNode(&VarDecl)))).bind("declRef");
+  auto ConstMethodCallee = callee(cxxMethodDecl(isConst()));
+  // Match method call expressions where the variable is referenced as the this
+  // implicit object argument and opertor call expression for member operators
+  // where the variable is the 0-th argument.
+  auto Matches =
+      match(decl(forEachDescendant(expr(
+                anyOf(cxxMemberCallExpr(ConstMethodCallee, on(DeclRefToVar)),
+                      cxxOperatorCallExpr(ConstMethodCallee,
+                                          hasArgument(0, DeclRefToVar)))))),
+            Decl, Context);
+  SmallPtrSet<const DeclRefExpr *, 16> DeclRefs;
+  extractNodesByIdTo(Matches, "declRef", DeclRefs);
+  auto ConstReferenceOrValue =
+      qualType(anyOf(referenceType(pointee(qualType(isConstQualified()))),
+                     unless(anyOf(referenceType(), pointerType()))));
+  auto UsedAsConstRefOrValueArg = forEachArgumentWithParam(
+      DeclRefToVar, parmVarDecl(hasType(ConstReferenceOrValue)));
+  Matches = match(decl(forEachDescendant(callExpr(UsedAsConstRefOrValueArg))),
+                  Decl, Context);
+  extractNodesByIdTo(Matches, "declRef", DeclRefs);
+  Matches =
+      match(decl(forEachDescendant(cxxConstructExpr(UsedAsConstRefOrValueArg))),
+            Decl, Context);
+  extractNodesByIdTo(Matches, "declRef", DeclRefs);
+  return DeclRefs;
+}
+
+bool isOnlyUsedAsConst(const VarDecl &Var, const Stmt &Stmt,
+                       ASTContext &Context) {
+  // Collect all DeclRefExprs to the loop variable and all CallExprs and
+  // CXXConstructExprs where the loop variable is used as argument to a const
+  // reference parameter.
+  // If the difference is empty it is safe for the loop variable to be a const
+  // reference.
+  auto AllDeclRefs = allDeclRefExprs(Var, Stmt, Context);
+  auto ConstReferenceDeclRefs = constReferenceDeclRefExprs(Var, Stmt, Context);
+  return isSetDifferenceEmpty(AllDeclRefs, ConstReferenceDeclRefs);
+}
+
+SmallPtrSet<const DeclRefExpr *, 16>
+allDeclRefExprs(const VarDecl &VarDecl, const Stmt &Stmt, ASTContext &Context) {
+  auto Matches = match(
+      findAll(declRefExpr(to(varDecl(equalsNode(&VarDecl)))).bind("declRef")),
+      Stmt, Context);
+  SmallPtrSet<const DeclRefExpr *, 16> DeclRefs;
+  extractNodesByIdTo(Matches, "declRef", DeclRefs);
+  return DeclRefs;
+}
+
+SmallPtrSet<const DeclRefExpr *, 16>
+allDeclRefExprs(const VarDecl &VarDecl, const Decl &Decl, ASTContext &Context) {
+  auto Matches = match(
+      decl(forEachDescendant(
+          declRefExpr(to(varDecl(equalsNode(&VarDecl)))).bind("declRef"))),
+      Decl, Context);
+  SmallPtrSet<const DeclRefExpr *, 16> DeclRefs;
+  extractNodesByIdTo(Matches, "declRef", DeclRefs);
+  return DeclRefs;
+}
+
+bool isCopyConstructorArgument(const DeclRefExpr &DeclRef, const Decl &Decl,
+                               ASTContext &Context) {
+  auto UsedAsConstRefArg = forEachArgumentWithParam(
+      declRefExpr(equalsNode(&DeclRef)),
+      parmVarDecl(hasType(matchers::isReferenceToConst())));
+  auto Matches = match(
+      decl(hasDescendant(
+          cxxConstructExpr(UsedAsConstRefArg, hasDeclaration(cxxConstructorDecl(
+                                                  isCopyConstructor())))
+              .bind("constructExpr"))),
+      Decl, Context);
+  return !Matches.empty();
+}
+
+bool isCopyAssignmentArgument(const DeclRefExpr &DeclRef, const Decl &Decl,
+                              ASTContext &Context) {
+  auto UsedAsConstRefArg = forEachArgumentWithParam(
+      declRefExpr(equalsNode(&DeclRef)),
+      parmVarDecl(hasType(matchers::isReferenceToConst())));
+  auto Matches = match(
+      decl(hasDescendant(
+          cxxOperatorCallExpr(UsedAsConstRefArg, hasOverloadedOperatorName("="),
+                              callee(cxxMethodDecl(isCopyAssignmentOperator())))
+              .bind("operatorCallExpr"))),
+      Decl, Context);
+  return !Matches.empty();
+}
+
+} // namespace decl_ref_expr
+} // namespace utils
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/utils/DeclRefExprUtils.h b/clang-tidy/utils/DeclRefExprUtils.h
new file mode 100644 (file)
index 0000000..c25102f
--- /dev/null
@@ -0,0 +1,66 @@
+//===--- DeclRefExprUtils.h - clang-tidy-------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_DECLREFEXPRUTILS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_DECLREFEXPRUTILS_H
+
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Type.h"
+#include "llvm/ADT/SmallPtrSet.h"
+
+namespace clang {
+namespace tidy {
+namespace utils {
+namespace decl_ref_expr {
+
+/// \brief Returns true if all ``DeclRefExpr`` to the variable within ``Stmt``
+/// do not modify it.
+///
+/// Returns ``true`` if only const methods or operators are called on the
+/// variable or the variable is a const reference or value argument to a
+/// ``callExpr()``.
+bool isOnlyUsedAsConst(const VarDecl &Var, const Stmt &Stmt,
+                       ASTContext &Context);
+
+/// Returns set of all ``DeclRefExprs`` to ``VarDecl`` within ``Stmt``.
+llvm::SmallPtrSet<const DeclRefExpr *, 16>
+allDeclRefExprs(const VarDecl &VarDecl, const Stmt &Stmt, ASTContext &Context);
+
+/// Returns set of all ``DeclRefExprs`` to ``VarDecl`` within ``Decl``.
+llvm::SmallPtrSet<const DeclRefExpr *, 16>
+allDeclRefExprs(const VarDecl &VarDecl, const Decl &Decl, ASTContext &Context);
+
+/// Returns set of all ``DeclRefExprs`` to ``VarDecl`` within ``Stmt`` where
+/// ``VarDecl`` is guaranteed to be accessed in a const fashion.
+llvm::SmallPtrSet<const DeclRefExpr *, 16>
+constReferenceDeclRefExprs(const VarDecl &VarDecl, const Stmt &Stmt,
+                           ASTContext &Context);
+
+/// Returns set of all ``DeclRefExprs`` to ``VarDecl`` within ``Decl`` where
+/// ``VarDecl`` is guaranteed to be accessed in a const fashion.
+llvm::SmallPtrSet<const DeclRefExpr *, 16>
+constReferenceDeclRefExprs(const VarDecl &VarDecl, const Decl &Decl,
+                           ASTContext &Context);
+
+/// Returns ``true`` if ``DeclRefExpr`` is the argument of a copy-constructor
+/// call expression within ``Decl``.
+bool isCopyConstructorArgument(const DeclRefExpr &DeclRef, const Decl &Decl,
+                               ASTContext &Context);
+
+/// Returns ``true`` if ``DeclRefExpr`` is the argument of a copy-assignment
+/// operator CallExpr within ``Decl``.
+bool isCopyAssignmentArgument(const DeclRefExpr &DeclRef, const Decl &Decl,
+                              ASTContext &Context);
+
+} // namespace decl_ref_expr
+} // namespace utils
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_DECLREFEXPRUTILS_H
diff --git a/clang-tidy/utils/ExprSequence.cpp b/clang-tidy/utils/ExprSequence.cpp
new file mode 100644 (file)
index 0000000..02d4a0b
--- /dev/null
@@ -0,0 +1,182 @@
+//===---------- ExprSequence.cpp - clang-tidy -----------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ExprSequence.h"
+
+namespace clang {
+namespace tidy {
+namespace utils {
+
+// Returns the Stmt nodes that are parents of 'S', skipping any potential
+// intermediate non-Stmt nodes.
+//
+// In almost all cases, this function returns a single parent or no parents at
+// all.
+//
+// The case that a Stmt has multiple parents is rare but does actually occur in
+// the parts of the AST that we're interested in. Specifically, InitListExpr
+// nodes cause ASTContext::getParent() to return multiple parents for certain
+// nodes in their subtree because RecursiveASTVisitor visits both the syntactic
+// and semantic forms of InitListExpr, and the parent-child relationships are
+// different between the two forms.
+static SmallVector<const Stmt *, 1> getParentStmts(const Stmt *S,
+                                                   ASTContext *Context) {
+  SmallVector<const Stmt *, 1> Result;
+
+  ASTContext::DynTypedNodeList Parents = Context->getParents(*S);
+
+  SmallVector<ast_type_traits::DynTypedNode, 1> NodesToProcess(Parents.begin(),
+                                                               Parents.end());
+
+  while (!NodesToProcess.empty()) {
+    ast_type_traits::DynTypedNode Node = NodesToProcess.back();
+    NodesToProcess.pop_back();
+
+    if (const auto *S = Node.get<Stmt>()) {
+      Result.push_back(S);
+    } else {
+      Parents = Context->getParents(Node);
+      NodesToProcess.append(Parents.begin(), Parents.end());
+    }
+  }
+
+  return Result;
+}
+
+namespace {
+bool isDescendantOrEqual(const Stmt *Descendant, const Stmt *Ancestor,
+                         ASTContext *Context) {
+  if (Descendant == Ancestor)
+    return true;
+  for (const Stmt *Parent : getParentStmts(Descendant, Context)) {
+    if (isDescendantOrEqual(Parent, Ancestor, Context))
+      return true;
+  }
+
+  return false;
+}
+}
+
+ExprSequence::ExprSequence(const CFG *TheCFG, ASTContext *TheContext)
+    : Context(TheContext) {
+  for (const auto &SyntheticStmt : TheCFG->synthetic_stmts()) {
+    SyntheticStmtSourceMap[SyntheticStmt.first] = SyntheticStmt.second;
+  }
+}
+
+bool ExprSequence::inSequence(const Stmt *Before, const Stmt *After) const {
+  Before = resolveSyntheticStmt(Before);
+  After = resolveSyntheticStmt(After);
+
+  // If 'After' is in the subtree of the siblings that follow 'Before' in the
+  // chain of successors, we know that 'After' is sequenced after 'Before'.
+  for (const Stmt *Successor = getSequenceSuccessor(Before); Successor;
+       Successor = getSequenceSuccessor(Successor)) {
+    if (isDescendantOrEqual(After, Successor, Context))
+      return true;
+  }
+
+  // If 'After' is a parent of 'Before' or is sequenced after one of these
+  // parents, we know that it is sequenced after 'Before'.
+  for (const Stmt *Parent : getParentStmts(Before, Context)) {
+    if (Parent == After || inSequence(Parent, After))
+      return true;
+  }
+
+  return false;
+}
+
+bool ExprSequence::potentiallyAfter(const Stmt *After,
+                                    const Stmt *Before) const {
+  return !inSequence(After, Before);
+}
+
+const Stmt *ExprSequence::getSequenceSuccessor(const Stmt *S) const {
+  for (const Stmt *Parent : getParentStmts(S, Context)) {
+    if (const auto *BO = dyn_cast<BinaryOperator>(Parent)) {
+      // Comma operator: Right-hand side is sequenced after the left-hand side.
+      if (BO->getLHS() == S && BO->getOpcode() == BO_Comma)
+        return BO->getRHS();
+    } else if (const auto *InitList = dyn_cast<InitListExpr>(Parent)) {
+      // Initializer list: Each initializer clause is sequenced after the
+      // clauses that precede it.
+      for (unsigned I = 1; I < InitList->getNumInits(); ++I) {
+        if (InitList->getInit(I - 1) == S)
+          return InitList->getInit(I);
+      }
+    } else if (const auto *Compound = dyn_cast<CompoundStmt>(Parent)) {
+      // Compound statement: Each sub-statement is sequenced after the
+      // statements that precede it.
+      const Stmt *Previous = nullptr;
+      for (const auto *Child : Compound->body()) {
+        if (Previous == S)
+          return Child;
+        Previous = Child;
+      }
+    } else if (const auto *TheDeclStmt = dyn_cast<DeclStmt>(Parent)) {
+      // Declaration: Every initializer expression is sequenced after the
+      // initializer expressions that precede it.
+      const Expr *PreviousInit = nullptr;
+      for (const Decl *TheDecl : TheDeclStmt->decls()) {
+        if (const auto *TheVarDecl = dyn_cast<VarDecl>(TheDecl)) {
+          if (const Expr *Init = TheVarDecl->getInit()) {
+            if (PreviousInit == S)
+              return Init;
+            PreviousInit = Init;
+          }
+        }
+      }
+    } else if (const auto *ForRange = dyn_cast<CXXForRangeStmt>(Parent)) {
+      // Range-based for: Loop variable declaration is sequenced before the
+      // body. (We need this rule because these get placed in the same
+      // CFGBlock.)
+      if (S == ForRange->getLoopVarStmt())
+        return ForRange->getBody();
+    } else if (const auto *TheIfStmt = dyn_cast<IfStmt>(Parent)) {
+      // If statement: If a variable is declared inside the condition, the
+      // expression used to initialize the variable is sequenced before the
+      // evaluation of the condition.
+      if (S == TheIfStmt->getConditionVariableDeclStmt())
+        return TheIfStmt->getCond();
+    }
+  }
+
+  return nullptr;
+}
+
+const Stmt *ExprSequence::resolveSyntheticStmt(const Stmt *S) const {
+  if (SyntheticStmtSourceMap.count(S))
+    return SyntheticStmtSourceMap.lookup(S);
+  return S;
+}
+
+StmtToBlockMap::StmtToBlockMap(const CFG *TheCFG, ASTContext *TheContext)
+    : Context(TheContext) {
+  for (const auto *B : *TheCFG) {
+    for (const auto &Elem : *B) {
+      if (Optional<CFGStmt> S = Elem.getAs<CFGStmt>())
+        Map[S->getStmt()] = B;
+    }
+  }
+}
+
+const CFGBlock *StmtToBlockMap::blockContainingStmt(const Stmt *S) const {
+  while (!Map.count(S)) {
+    SmallVector<const Stmt *, 1> Parents = getParentStmts(S, Context);
+    if (Parents.empty())
+      return nullptr;
+    S = Parents[0];
+  }
+
+  return Map.lookup(S);
+}
+
+} // namespace utils
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/utils/ExprSequence.h b/clang-tidy/utils/ExprSequence.h
new file mode 100644 (file)
index 0000000..2b355d9
--- /dev/null
@@ -0,0 +1,124 @@
+//===------------- ExprSequence.h - clang-tidy ----------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_EXPRSEQUENCE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_EXPRSEQUENCE_H
+
+#include "clang/Analysis/CFG.h"
+#include "clang/Lex/Lexer.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/ADT/SmallVector.h"
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace utils {
+
+/// Provides information about the evaluation order of (sub-)expressions within
+/// a `CFGBlock`.
+///
+/// While a `CFGBlock` does contain individual `CFGElement`s for some
+/// sub-expressions, the order in which those `CFGElement`s appear reflects
+/// only one possible order in which the sub-expressions may be evaluated.
+/// However, we want to warn if any of the potential evaluation orders can lead
+/// to a use-after-move, not just the one contained in the `CFGBlock`.
+///
+/// This class implements only a simplified version of the C++ sequencing
+/// rules. The main limitation is that we do not distinguish between value
+/// computation and side effect -- see the "Implementation" section for more
+/// details.
+///
+/// Note: `SequenceChecker` from SemaChecking.cpp does a similar job (and much
+/// more thoroughly), but using it would require
+/// - Pulling `SequenceChecker` out into a header file (i.e. making it part of
+///   the API),
+/// - Removing the dependency of `SequenceChecker` on `Sema`, and
+/// - (Probably) modifying `SequenceChecker` to make it suitable to be used in
+///   this context.
+/// For the moment, it seems preferable to re-implement our own version of
+/// sequence checking that is special-cased to what we need here.
+///
+/// Implementation
+/// --------------
+///
+/// `ExprSequence` uses two types of sequencing edges between nodes in the AST:
+///
+/// - Every `Stmt` is assumed to be sequenced after its children. This is
+///   overly optimistic because the standard only states that value computations
+///   of operands are sequenced before the value computation of the operator,
+///   making no guarantees about side effects (in general).
+///
+///   For our purposes, this rule is sufficient, however, because this check is
+///   interested in operations on objects, which are generally performed through
+///   function calls (whether explicit and implicit). Function calls guarantee
+///   that the value computations and side effects for all function arguments
+///   are sequenced before the execution of the function.
+///
+/// - In addition, some `Stmt`s are known to be sequenced before or after
+///   their siblings. For example, the `Stmt`s that make up a `CompoundStmt`are
+///   all sequenced relative to each other. The function
+///   `getSequenceSuccessor()` implements these sequencing rules.
+class ExprSequence {
+public:
+  /// Initializes this `ExprSequence` with sequence information for the given
+  /// `CFG`.
+  ExprSequence(const CFG *TheCFG, ASTContext *TheContext);
+
+  /// Returns whether \p Before is sequenced before \p After.
+  bool inSequence(const Stmt *Before, const Stmt *After) const;
+
+  /// Returns whether \p After can potentially be evaluated after \p Before.
+  /// This is exactly equivalent to `!inSequence(After, Before)` but makes some
+  /// conditions read more naturally.
+  bool potentiallyAfter(const Stmt *After, const Stmt *Before) const;
+
+private:
+  // Returns the sibling of \p S (if any) that is directly sequenced after \p S,
+  // or nullptr if no such sibling exists. For example, if \p S is the child of
+  // a `CompoundStmt`, this would return the Stmt that directly follows \p S in
+  // the `CompoundStmt`.
+  //
+  // As the sequencing of many constructs that change control flow is already
+  // encoded in the `CFG`, this function only implements the sequencing rules
+  // for those constructs where sequencing cannot be inferred from the `CFG`.
+  const Stmt *getSequenceSuccessor(const Stmt *S) const;
+
+  const Stmt *resolveSyntheticStmt(const Stmt *S) const;
+
+  ASTContext *Context;
+
+  llvm::DenseMap<const Stmt *, const Stmt *> SyntheticStmtSourceMap;
+};
+
+/// Maps `Stmt`s to the `CFGBlock` that contains them. Some `Stmt`s may be
+/// contained in more than one `CFGBlock`; in this case, they are mapped to the
+/// innermost block (i.e. the one that is furthest from the root of the tree).
+class StmtToBlockMap {
+public:
+  /// Initializes the map for the given `CFG`.
+  StmtToBlockMap(const CFG *TheCFG, ASTContext *TheContext);
+
+  /// Returns the block that \p S is contained in. Some `Stmt`s may be contained
+  /// in more than one `CFGBlock`; in this case, this function returns the
+  /// innermost block (i.e. the one that is furthest from the root of the tree).
+  const CFGBlock *blockContainingStmt(const Stmt *S) const;
+
+private:
+  ASTContext *Context;
+
+  llvm::DenseMap<const Stmt *, const CFGBlock *> Map;
+};
+
+} // namespace utils
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_EXPRSEQUENCE_H
diff --git a/clang-tidy/utils/FixItHintUtils.cpp b/clang-tidy/utils/FixItHintUtils.cpp
new file mode 100644 (file)
index 0000000..0627e51
--- /dev/null
@@ -0,0 +1,36 @@
+//===--- FixItHintUtils.cpp - clang-tidy-----------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "FixItHintUtils.h"
+#include "LexerUtils.h"
+#include "clang/AST/ASTContext.h"
+
+namespace clang {
+namespace tidy {
+namespace utils {
+namespace fixit {
+
+FixItHint changeVarDeclToReference(const VarDecl &Var, ASTContext &Context) {
+  SourceLocation AmpLocation = Var.getLocation();
+  auto Token = utils::lexer::getPreviousToken(Context, AmpLocation);
+  if (!Token.is(tok::unknown))
+    AmpLocation = Lexer::getLocForEndOfToken(Token.getLocation(), 0,
+                                             Context.getSourceManager(),
+                                             Context.getLangOpts());
+  return FixItHint::CreateInsertion(AmpLocation, "&");
+}
+
+FixItHint changeVarDeclToConst(const VarDecl &Var) {
+  return FixItHint::CreateInsertion(Var.getTypeSpecStartLoc(), "const ");
+}
+
+} // namespace fixit
+} // namespace utils
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/utils/FixItHintUtils.h b/clang-tidy/utils/FixItHintUtils.h
new file mode 100644 (file)
index 0000000..e64a6e4
--- /dev/null
@@ -0,0 +1,32 @@
+//===--- FixItHintUtils.h - clang-tidy---------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_FIXITHINTUTILS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_FIXITHINTUTILS_H
+
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+
+namespace clang {
+namespace tidy {
+namespace utils {
+namespace fixit {
+
+/// \brief Creates fix to make ``VarDecl`` a reference by adding ``&``.
+FixItHint changeVarDeclToReference(const VarDecl &Var, ASTContext &Context);
+
+/// \brief Creates fix to make ``VarDecl`` const qualified.
+FixItHint changeVarDeclToConst(const VarDecl &Var);
+
+} // namespace fixit
+} // namespace utils
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_FIXITHINTUTILS_H
diff --git a/clang-tidy/utils/HeaderFileExtensionsUtils.cpp b/clang-tidy/utils/HeaderFileExtensionsUtils.cpp
new file mode 100644 (file)
index 0000000..b734b89
--- /dev/null
@@ -0,0 +1,71 @@
+//===--- HeaderFileExtensionsUtils.cpp - clang-tidy--------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "HeaderFileExtensionsUtils.h"
+#include "clang/Basic/CharInfo.h"
+#include "llvm/Support/Path.h"
+
+namespace clang {
+namespace tidy {
+namespace utils {
+
+bool isExpansionLocInHeaderFile(
+    SourceLocation Loc, const SourceManager &SM,
+    const HeaderFileExtensionsSet &HeaderFileExtensions) {
+  SourceLocation ExpansionLoc = SM.getExpansionLoc(Loc);
+  return isHeaderFileExtension(SM.getFilename(ExpansionLoc),
+                               HeaderFileExtensions);
+}
+
+bool isPresumedLocInHeaderFile(
+    SourceLocation Loc, SourceManager &SM,
+    const HeaderFileExtensionsSet &HeaderFileExtensions) {
+  PresumedLoc PresumedLocation = SM.getPresumedLoc(Loc);
+  return isHeaderFileExtension(PresumedLocation.getFilename(),
+                               HeaderFileExtensions);
+}
+
+bool isSpellingLocInHeaderFile(
+    SourceLocation Loc, SourceManager &SM,
+    const HeaderFileExtensionsSet &HeaderFileExtensions) {
+  SourceLocation SpellingLoc = SM.getSpellingLoc(Loc);
+  return isHeaderFileExtension(SM.getFilename(SpellingLoc),
+                               HeaderFileExtensions);
+}
+
+bool parseHeaderFileExtensions(StringRef AllHeaderFileExtensions,
+                               HeaderFileExtensionsSet &HeaderFileExtensions,
+                               char delimiter) {
+  SmallVector<StringRef, 5> Suffixes;
+  AllHeaderFileExtensions.split(Suffixes, delimiter);
+  HeaderFileExtensions.clear();
+  for (StringRef Suffix : Suffixes) {
+    StringRef Extension = Suffix.trim();
+    for (StringRef::const_iterator it = Extension.begin();
+         it != Extension.end(); ++it) {
+      if (!isAlphanumeric(*it))
+        return false;
+    }
+    HeaderFileExtensions.insert(Extension);
+  }
+  return true;
+}
+
+bool isHeaderFileExtension(
+    StringRef FileName, const HeaderFileExtensionsSet &HeaderFileExtensions) {
+  StringRef extension = llvm::sys::path::extension(FileName);
+  if (extension.empty())
+    return false;
+  // Skip "." prefix.
+  return HeaderFileExtensions.count(extension.substr(1)) > 0;
+}
+
+} // namespace utils
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/utils/HeaderFileExtensionsUtils.h b/clang-tidy/utils/HeaderFileExtensionsUtils.h
new file mode 100644 (file)
index 0000000..c9fedf0
--- /dev/null
@@ -0,0 +1,52 @@
+//===--- HeaderFileExtensionsUtils.h - clang-tidy----------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_HEADER_FILE_EXTENSIONS_UTILS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_HEADER_FILE_EXTENSIONS_UTILS_H
+
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/SourceManager.h"
+#include "llvm/ADT/SmallSet.h"
+#include "llvm/ADT/StringRef.h"
+
+namespace clang {
+namespace tidy {
+namespace utils {
+
+typedef llvm::SmallSet<llvm::StringRef, 5> HeaderFileExtensionsSet;
+
+/// \brief Checks whether expansion location of \p Loc is in header file.
+bool isExpansionLocInHeaderFile(
+    SourceLocation Loc, const SourceManager &SM,
+    const HeaderFileExtensionsSet &HeaderFileExtensions);
+
+/// \brief Checks whether presumed location of \p Loc is in header file.
+bool isPresumedLocInHeaderFile(
+    SourceLocation Loc, SourceManager &SM,
+    const HeaderFileExtensionsSet &HeaderFileExtensions);
+
+/// \brief Checks whether spelling location of \p Loc is in header file.
+bool isSpellingLocInHeaderFile(
+    SourceLocation Loc, SourceManager &SM,
+    const HeaderFileExtensionsSet &HeaderFileExtensions);
+
+/// \brief Parses header file extensions from a semicolon-separated list.
+bool parseHeaderFileExtensions(StringRef AllHeaderFileExtensions,
+                               HeaderFileExtensionsSet &HeaderFileExtensions,
+                               char delimiter);
+
+/// \brief Decides whether a file has a header file extension.
+bool isHeaderFileExtension(StringRef FileName,
+                           const HeaderFileExtensionsSet &HeaderFileExtensions);
+
+} // namespace utils
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_HEADER_FILE_EXTENSIONS_UTILS_H
diff --git a/clang-tidy/utils/HeaderGuard.cpp b/clang-tidy/utils/HeaderGuard.cpp
new file mode 100644 (file)
index 0000000..515f882
--- /dev/null
@@ -0,0 +1,292 @@
+//===--- HeaderGuard.cpp - clang-tidy -------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "HeaderGuard.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Lex/PPCallbacks.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/Support/Path.h"
+
+namespace clang {
+namespace tidy {
+namespace utils {
+
+/// \brief canonicalize a path by removing ./ and ../ components.
+static std::string cleanPath(StringRef Path) {
+  SmallString<256> Result = Path;
+  llvm::sys::path::remove_dots(Result, true);
+  return Result.str();
+}
+
+namespace {
+class HeaderGuardPPCallbacks : public PPCallbacks {
+public:
+  HeaderGuardPPCallbacks(Preprocessor *PP, HeaderGuardCheck *Check)
+      : PP(PP), Check(Check) {}
+
+  void FileChanged(SourceLocation Loc, FileChangeReason Reason,
+                   SrcMgr::CharacteristicKind FileType,
+                   FileID PrevFID) override {
+    // Record all files we enter. We'll need them to diagnose headers without
+    // guards.
+    SourceManager &SM = PP->getSourceManager();
+    if (Reason == EnterFile && FileType == SrcMgr::C_User) {
+      if (const FileEntry *FE = SM.getFileEntryForID(SM.getFileID(Loc))) {
+        std::string FileName = cleanPath(FE->getName());
+        Files[FileName] = FE;
+      }
+    }
+  }
+
+  void Ifndef(SourceLocation Loc, const Token &MacroNameTok,
+              const MacroDefinition &MD) override {
+    if (MD)
+      return;
+
+    // Record #ifndefs that succeeded. We also need the Location of the Name.
+    Ifndefs[MacroNameTok.getIdentifierInfo()] =
+        std::make_pair(Loc, MacroNameTok.getLocation());
+  }
+
+  void MacroDefined(const Token &MacroNameTok,
+                    const MacroDirective *MD) override {
+    // Record all defined macros. We store the whole token to get info on the
+    // name later.
+    Macros.emplace_back(MacroNameTok, MD->getMacroInfo());
+  }
+
+  void Endif(SourceLocation Loc, SourceLocation IfLoc) override {
+    // Record all #endif and the corresponding #ifs (including #ifndefs).
+    EndIfs[IfLoc] = Loc;
+  }
+
+  void EndOfMainFile() override {
+    // Now that we have all this information from the preprocessor, use it!
+    SourceManager &SM = PP->getSourceManager();
+
+    for (const auto &MacroEntry : Macros) {
+      const MacroInfo *MI = MacroEntry.second;
+
+      // We use clang's header guard detection. This has the advantage of also
+      // emitting a warning for cases where a pseudo header guard is found but
+      // preceded by something blocking the header guard optimization.
+      if (!MI->isUsedForHeaderGuard())
+        continue;
+
+      const FileEntry *FE =
+          SM.getFileEntryForID(SM.getFileID(MI->getDefinitionLoc()));
+      std::string FileName = cleanPath(FE->getName());
+      Files.erase(FileName);
+
+      // See if we should check and fix this header guard.
+      if (!Check->shouldFixHeaderGuard(FileName))
+        continue;
+
+      // Look up Locations for this guard.
+      SourceLocation Ifndef =
+          Ifndefs[MacroEntry.first.getIdentifierInfo()].second;
+      SourceLocation Define = MacroEntry.first.getLocation();
+      SourceLocation EndIf =
+          EndIfs[Ifndefs[MacroEntry.first.getIdentifierInfo()].first];
+
+      // If the macro Name is not equal to what we can compute, correct it in
+      // the #ifndef and #define.
+      StringRef CurHeaderGuard =
+          MacroEntry.first.getIdentifierInfo()->getName();
+      std::vector<FixItHint> FixIts;
+      std::string NewGuard = checkHeaderGuardDefinition(
+          Ifndef, Define, EndIf, FileName, CurHeaderGuard, FixIts);
+
+      // Now look at the #endif. We want a comment with the header guard. Fix it
+      // at the slightest deviation.
+      checkEndifComment(FileName, EndIf, NewGuard, FixIts);
+
+      // Bundle all fix-its into one warning. The message depends on whether we
+      // changed the header guard or not.
+      if (!FixIts.empty()) {
+        if (CurHeaderGuard != NewGuard) {
+          Check->diag(Ifndef, "header guard does not follow preferred style")
+              << FixIts;
+        } else {
+          Check->diag(EndIf, "#endif for a header guard should reference the "
+                             "guard macro in a comment")
+              << FixIts;
+        }
+      }
+    }
+
+    // Emit warnings for headers that are missing guards.
+    checkGuardlessHeaders();
+
+    // Clear all state.
+    Macros.clear();
+    Files.clear();
+    Ifndefs.clear();
+    EndIfs.clear();
+  }
+
+  bool wouldFixEndifComment(StringRef FileName, SourceLocation EndIf,
+                            StringRef HeaderGuard,
+                            size_t *EndIfLenPtr = nullptr) {
+    if (!EndIf.isValid())
+      return false;
+    const char *EndIfData = PP->getSourceManager().getCharacterData(EndIf);
+    size_t EndIfLen = std::strcspn(EndIfData, "\r\n");
+    if (EndIfLenPtr)
+      *EndIfLenPtr = EndIfLen;
+
+    StringRef EndIfStr(EndIfData, EndIfLen);
+    EndIfStr = EndIfStr.substr(EndIfStr.find_first_not_of("#endif \t"));
+
+    // Give up if there's an escaped newline.
+    size_t FindEscapedNewline = EndIfStr.find_last_not_of(' ');
+    if (FindEscapedNewline != StringRef::npos &&
+        EndIfStr[FindEscapedNewline] == '\\')
+      return false;
+
+    if (!Check->shouldSuggestEndifComment(FileName) &&
+        !(EndIfStr.startswith("//") ||
+          (EndIfStr.startswith("/*") && EndIfStr.endswith("*/"))))
+      return false;
+
+    return (EndIfStr != "// " + HeaderGuard.str()) &&
+           (EndIfStr != "/* " + HeaderGuard.str() + " */");
+  }
+
+  /// \brief Look for header guards that don't match the preferred style. Emit
+  /// fix-its and return the suggested header guard (or the original if no
+  /// change was made.
+  std::string checkHeaderGuardDefinition(SourceLocation Ifndef,
+                                         SourceLocation Define,
+                                         SourceLocation EndIf,
+                                         StringRef FileName,
+                                         StringRef CurHeaderGuard,
+                                         std::vector<FixItHint> &FixIts) {
+    std::string CPPVar = Check->getHeaderGuard(FileName, CurHeaderGuard);
+    std::string CPPVarUnder = CPPVar + '_';
+
+    // Allow a trailing underscore iff we don't have to change the endif comment
+    // too.
+    if (Ifndef.isValid() && CurHeaderGuard != CPPVar &&
+        (CurHeaderGuard != CPPVarUnder ||
+         wouldFixEndifComment(FileName, EndIf, CurHeaderGuard))) {
+      FixIts.push_back(FixItHint::CreateReplacement(
+          CharSourceRange::getTokenRange(
+              Ifndef, Ifndef.getLocWithOffset(CurHeaderGuard.size())),
+          CPPVar));
+      FixIts.push_back(FixItHint::CreateReplacement(
+          CharSourceRange::getTokenRange(
+              Define, Define.getLocWithOffset(CurHeaderGuard.size())),
+          CPPVar));
+      return CPPVar;
+    }
+    return CurHeaderGuard;
+  }
+
+  /// \brief Checks the comment after the #endif of a header guard and fixes it
+  /// if it doesn't match \c HeaderGuard.
+  void checkEndifComment(StringRef FileName, SourceLocation EndIf,
+                         StringRef HeaderGuard,
+                         std::vector<FixItHint> &FixIts) {
+    size_t EndIfLen;
+    if (wouldFixEndifComment(FileName, EndIf, HeaderGuard, &EndIfLen)) {
+      FixIts.push_back(FixItHint::CreateReplacement(
+          CharSourceRange::getCharRange(EndIf,
+                                        EndIf.getLocWithOffset(EndIfLen)),
+          Check->formatEndIf(HeaderGuard)));
+    }
+  }
+
+  /// \brief Looks for files that were visited but didn't have a header guard.
+  /// Emits a warning with fixits suggesting adding one.
+  void checkGuardlessHeaders() {
+    // Look for header files that didn't have a header guard. Emit a warning and
+    // fix-its to add the guard.
+    // TODO: Insert the guard after top comments.
+    for (const auto &FE : Files) {
+      StringRef FileName = FE.getKey();
+      if (!Check->shouldSuggestToAddHeaderGuard(FileName))
+        continue;
+
+      SourceManager &SM = PP->getSourceManager();
+      FileID FID = SM.translateFile(FE.getValue());
+      SourceLocation StartLoc = SM.getLocForStartOfFile(FID);
+      if (StartLoc.isInvalid())
+        continue;
+
+      std::string CPPVar = Check->getHeaderGuard(FileName);
+      std::string CPPVarUnder = CPPVar + '_'; // Allow a trailing underscore.
+      // If there's a macro with a name that follows the header guard convention
+      // but was not recognized by the preprocessor as a header guard there must
+      // be code outside of the guarded area. Emit a plain warning without
+      // fix-its.
+      // FIXME: Can we move it into the right spot?
+      bool SeenMacro = false;
+      for (const auto &MacroEntry : Macros) {
+        StringRef Name = MacroEntry.first.getIdentifierInfo()->getName();
+        SourceLocation DefineLoc = MacroEntry.first.getLocation();
+        if ((Name == CPPVar || Name == CPPVarUnder) &&
+            SM.isWrittenInSameFile(StartLoc, DefineLoc)) {
+          Check->diag(DefineLoc, "code/includes outside of area guarded by "
+                                 "header guard; consider moving it");
+          SeenMacro = true;
+          break;
+        }
+      }
+
+      if (SeenMacro)
+        continue;
+
+      Check->diag(StartLoc, "header is missing header guard")
+          << FixItHint::CreateInsertion(
+                 StartLoc, "#ifndef " + CPPVar + "\n#define " + CPPVar + "\n\n")
+          << FixItHint::CreateInsertion(
+                 SM.getLocForEndOfFile(FID),
+                 Check->shouldSuggestEndifComment(FileName)
+                     ? "\n#" + Check->formatEndIf(CPPVar) + "\n"
+                     : "\n#endif\n");
+    }
+  }
+
+private:
+  std::vector<std::pair<Token, const MacroInfo *>> Macros;
+  llvm::StringMap<const FileEntry *> Files;
+  std::map<const IdentifierInfo *, std::pair<SourceLocation, SourceLocation>>
+      Ifndefs;
+  std::map<SourceLocation, SourceLocation> EndIfs;
+
+  Preprocessor *PP;
+  HeaderGuardCheck *Check;
+};
+} // namespace
+
+void HeaderGuardCheck::registerPPCallbacks(CompilerInstance &Compiler) {
+  Compiler.getPreprocessor().addPPCallbacks(
+      llvm::make_unique<HeaderGuardPPCallbacks>(&Compiler.getPreprocessor(),
+                                                this));
+}
+
+bool HeaderGuardCheck::shouldSuggestEndifComment(StringRef FileName) {
+  return utils::isHeaderFileExtension(FileName, HeaderFileExtensions);
+}
+
+bool HeaderGuardCheck::shouldFixHeaderGuard(StringRef FileName) { return true; }
+
+bool HeaderGuardCheck::shouldSuggestToAddHeaderGuard(StringRef FileName) {
+  return utils::isHeaderFileExtension(FileName, HeaderFileExtensions);
+}
+
+std::string HeaderGuardCheck::formatEndIf(StringRef HeaderGuard) {
+  return "endif // " + HeaderGuard.str();
+}
+
+} // namespace utils
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/utils/HeaderGuard.h b/clang-tidy/utils/HeaderGuard.h
new file mode 100644 (file)
index 0000000..c90a312
--- /dev/null
@@ -0,0 +1,64 @@
+//===--- HeaderGuard.h - clang-tidy -----------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_HEADERGUARD_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_HEADERGUARD_H
+
+#include "../ClangTidy.h"
+#include "../utils/HeaderFileExtensionsUtils.h"
+
+namespace clang {
+namespace tidy {
+namespace utils {
+
+/// Finds and fixes header guards.
+/// The check supports these options:
+///   - `HeaderFileExtensions`: a comma-separated list of filename extensions of
+///     header files (The filename extension should not contain "." prefix).
+///     ",h,hh,hpp,hxx" by default.
+///     For extension-less header files, using an empty string or leaving an
+///     empty string between "," if there are other filename extensions.
+class HeaderGuardCheck : public ClangTidyCheck {
+public:
+  HeaderGuardCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context),
+        RawStringHeaderFileExtensions(
+            Options.getLocalOrGlobal("HeaderFileExtensions", ",h,hh,hpp,hxx")) {
+    utils::parseHeaderFileExtensions(RawStringHeaderFileExtensions,
+                                     HeaderFileExtensions, ',');
+  }
+  void registerPPCallbacks(CompilerInstance &Compiler) override;
+
+  /// Returns ``true`` if the check should suggest inserting a trailing comment
+  /// on the ``#endif`` of the header guard. It will use the same name as
+  /// returned by ``HeaderGuardCheck::getHeaderGuard``.
+  virtual bool shouldSuggestEndifComment(StringRef Filename);
+  /// Returns ``true`` if the check should suggest changing an existing header
+  /// guard to the string returned by ``HeaderGuardCheck::getHeaderGuard``.
+  virtual bool shouldFixHeaderGuard(StringRef Filename);
+  /// Returns ``true`` if the check should add a header guard to the file
+  /// if it has none.
+  virtual bool shouldSuggestToAddHeaderGuard(StringRef Filename);
+  /// Returns a replacement for the ``#endif`` line with a comment mentioning
+  /// \p HeaderGuard. The replacement should start with ``endif``.
+  virtual std::string formatEndIf(StringRef HeaderGuard);
+  /// Gets the canonical header guard for a file.
+  virtual std::string getHeaderGuard(StringRef Filename,
+                                     StringRef OldGuard = StringRef()) = 0;
+
+private:
+  std::string RawStringHeaderFileExtensions;
+  utils::HeaderFileExtensionsSet HeaderFileExtensions;
+};
+
+} // namespace utils
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_HEADERGUARD_H
diff --git a/clang-tidy/utils/IncludeInserter.cpp b/clang-tidy/utils/IncludeInserter.cpp
new file mode 100644 (file)
index 0000000..9fc7f52
--- /dev/null
@@ -0,0 +1,85 @@
+//===-------- IncludeInserter.cpp - clang-tidy ----------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "IncludeInserter.h"
+#include "clang/Lex/Token.h"
+
+namespace clang {
+namespace tidy {
+namespace utils {
+
+class IncludeInserterCallback : public PPCallbacks {
+public:
+  explicit IncludeInserterCallback(IncludeInserter *Inserter)
+      : Inserter(Inserter) {}
+  // Implements PPCallbacks::InclusionDerective(). Records the names and source
+  // locations of the inclusions in the main source file being processed.
+  void InclusionDirective(SourceLocation HashLocation,
+                          const Token &IncludeToken, StringRef FileNameRef,
+                          bool IsAngled, CharSourceRange FileNameRange,
+                          const FileEntry * /*IncludedFile*/,
+                          StringRef /*SearchPath*/, StringRef /*RelativePath*/,
+                          const Module * /*ImportedModule*/) override {
+    Inserter->AddInclude(FileNameRef, IsAngled, HashLocation,
+                         IncludeToken.getEndLoc());
+  }
+
+private:
+  IncludeInserter *Inserter;
+};
+
+IncludeInserter::IncludeInserter(const SourceManager &SourceMgr,
+                                 const LangOptions &LangOpts,
+                                 IncludeSorter::IncludeStyle Style)
+    : SourceMgr(SourceMgr), LangOpts(LangOpts), Style(Style) {}
+
+IncludeInserter::~IncludeInserter() {}
+
+std::unique_ptr<PPCallbacks> IncludeInserter::CreatePPCallbacks() {
+  return llvm::make_unique<IncludeInserterCallback>(this);
+}
+
+llvm::Optional<FixItHint>
+IncludeInserter::CreateIncludeInsertion(FileID FileID, StringRef Header,
+                                        bool IsAngled) {
+  // We assume the same Header will never be included both angled and not
+  // angled.
+  if (!InsertedHeaders[FileID].insert(Header).second)
+    return llvm::None;
+
+  if (IncludeSorterByFile.find(FileID) == IncludeSorterByFile.end()) {
+    // This may happen if there have been no preprocessor directives in this
+    // file.
+    IncludeSorterByFile.insert(std::make_pair(
+        FileID,
+        llvm::make_unique<IncludeSorter>(
+            &SourceMgr, &LangOpts, FileID,
+            SourceMgr.getFilename(SourceMgr.getLocForStartOfFile(FileID)),
+            Style)));
+  }
+  return IncludeSorterByFile[FileID]->CreateIncludeInsertion(Header, IsAngled);
+}
+
+void IncludeInserter::AddInclude(StringRef FileName, bool IsAngled,
+                                 SourceLocation HashLocation,
+                                 SourceLocation EndLocation) {
+  FileID FileID = SourceMgr.getFileID(HashLocation);
+  if (IncludeSorterByFile.find(FileID) == IncludeSorterByFile.end()) {
+    IncludeSorterByFile.insert(std::make_pair(
+        FileID, llvm::make_unique<IncludeSorter>(
+                    &SourceMgr, &LangOpts, FileID,
+                    SourceMgr.getFilename(HashLocation), Style)));
+  }
+  IncludeSorterByFile[FileID]->AddInclude(FileName, IsAngled, HashLocation,
+                                          EndLocation);
+}
+
+} // namespace utils
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/utils/IncludeInserter.h b/clang-tidy/utils/IncludeInserter.h
new file mode 100644 (file)
index 0000000..75f2554
--- /dev/null
@@ -0,0 +1,84 @@
+//===---------- IncludeInserter.h - clang-tidy ----------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_INCLUDEINSERTER_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_INCLUDEINSERTER_H
+
+#include "IncludeSorter.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Lex/PPCallbacks.h"
+#include <memory>
+#include <string>
+
+namespace clang {
+namespace tidy {
+namespace utils {
+
+/// \brief Produces fixes to insert specified includes to source files, if not
+/// yet present.
+///
+/// ``IncludeInserter`` can be used by ``ClangTidyCheck`` in the following
+/// fashion:
+/// \code
+/// class MyCheck : public ClangTidyCheck {
+///  public:
+///   void registerPPCallbacks(CompilerInstance& Compiler) override {
+///     Inserter.reset(new IncludeInserter(&Compiler.getSourceManager(),
+///                                        &Compiler.getLangOpts()));
+///     Compiler.getPreprocessor().addPPCallbacks(
+///         Inserter->CreatePPCallback());
+///   }
+///
+///   void registerMatchers(ast_matchers::MatchFinder* Finder) override { ... }
+///
+///   void check(
+///       const ast_matchers::MatchFinder::MatchResult& Result) override {
+///     ...
+///     Inserter->CreateIncludeInsertion(
+///         Result.SourceManager->getMainFileID(), "path/to/Header.h",
+///         /*IsAngled=*/false);
+///     ...
+///   }
+///
+///  private:
+///   std::unique_ptr<IncludeInserter> Inserter;
+/// };
+/// \endcode
+class IncludeInserter {
+public:
+  IncludeInserter(const SourceManager &SourceMgr, const LangOptions &LangOpts,
+                  IncludeSorter::IncludeStyle Style);
+  ~IncludeInserter();
+
+  /// Create ``PPCallbacks`` for registration with the compiler's preprocessor.
+  std::unique_ptr<PPCallbacks> CreatePPCallbacks();
+
+  /// Creates a \p Header inclusion directive fixit. Returns ``llvm::None`` on
+  /// error or if inclusion directive already exists.
+  llvm::Optional<FixItHint>
+  CreateIncludeInsertion(FileID FileID, llvm::StringRef Header, bool IsAngled);
+
+private:
+  void AddInclude(StringRef FileName, bool IsAngled,
+                  SourceLocation HashLocation, SourceLocation EndLocation);
+
+  llvm::DenseMap<FileID, std::unique_ptr<IncludeSorter>> IncludeSorterByFile;
+  llvm::DenseMap<FileID, std::set<std::string>> InsertedHeaders;
+  const SourceManager &SourceMgr;
+  const LangOptions &LangOpts;
+  const IncludeSorter::IncludeStyle Style;
+  friend class IncludeInserterCallback;
+};
+
+} // namespace utils
+} // namespace tidy
+} // namespace clang
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_INCLUDEINSERTER_H
diff --git a/clang-tidy/utils/IncludeSorter.cpp b/clang-tidy/utils/IncludeSorter.cpp
new file mode 100644 (file)
index 0000000..1502da7
--- /dev/null
@@ -0,0 +1,290 @@
+//===---------- IncludeSorter.cpp - clang-tidy ----------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "IncludeSorter.h"
+#include "clang/Lex/Lexer.h"
+
+namespace clang {
+namespace tidy {
+namespace utils {
+
+namespace {
+
+StringRef RemoveFirstSuffix(StringRef Str, ArrayRef<const char *> Suffixes) {
+  for (StringRef Suffix : Suffixes) {
+    if (Str.endswith(Suffix)) {
+      return Str.substr(0, Str.size() - Suffix.size());
+    }
+  }
+  return Str;
+}
+
+StringRef MakeCanonicalName(StringRef Str, IncludeSorter::IncludeStyle Style) {
+  // The list of suffixes to remove from source file names to get the
+  // "canonical" file names.
+  // E.g. tools/sort_includes.cc and tools/sort_includes_test.cc
+  // would both canonicalize to tools/sort_includes and tools/sort_includes.h
+  // (once canonicalized) will match as being the main include file associated
+  // with the source files.
+  if (Style == IncludeSorter::IS_LLVM) {
+    return RemoveFirstSuffix(
+        RemoveFirstSuffix(Str, {".cc", ".cpp", ".c", ".h", ".hpp"}), {"Test"});
+  }
+  return RemoveFirstSuffix(
+      RemoveFirstSuffix(Str, {".cc", ".cpp", ".c", ".h", ".hpp"}),
+      {"_unittest", "_regtest", "_test"});
+}
+
+// Scan to the end of the line and return the offset of the next line.
+size_t FindNextLine(const char *Text) {
+  size_t EOLIndex = std::strcspn(Text, "\n");
+  return Text[EOLIndex] == '\0' ? EOLIndex : EOLIndex + 1;
+}
+
+IncludeSorter::IncludeKinds
+DetermineIncludeKind(StringRef CanonicalFile, StringRef IncludeFile,
+                     bool IsAngled, IncludeSorter::IncludeStyle Style) {
+  // Compute the two "canonical" forms of the include's filename sans extension.
+  // The first form is the include's filename without ".h" or "-inl.h" at the
+  // end. The second form is the first form with "/public/" in the file path
+  // replaced by "/internal/".
+  if (IsAngled) {
+    // If the system include (<foo>) ends with ".h", then it is a normal C-style
+    // include. Otherwise assume it is a C++-style extensionless include.
+    return IncludeFile.endswith(".h") ? IncludeSorter::IK_CSystemInclude
+                                      : IncludeSorter::IK_CXXSystemInclude;
+  }
+  StringRef CanonicalInclude = MakeCanonicalName(IncludeFile, Style);
+  if (CanonicalFile.endswith(CanonicalInclude)
+      || CanonicalInclude.endswith(CanonicalFile)) {
+    return IncludeSorter::IK_MainTUInclude;
+  }
+  if (Style == IncludeSorter::IS_Google) {
+    std::pair<StringRef, StringRef> Parts = CanonicalInclude.split("/public/");
+    std::string AltCanonicalInclude =
+        Parts.first.str() + "/internal/" + Parts.second.str();
+    std::string ProtoCanonicalInclude =
+        Parts.first.str() + "/proto/" + Parts.second.str();
+
+    // Determine the kind of this inclusion.
+    if (CanonicalFile.equals(AltCanonicalInclude) ||
+        CanonicalFile.equals(ProtoCanonicalInclude)) {
+      return IncludeSorter::IK_MainTUInclude;
+    }
+  }
+  return IncludeSorter::IK_NonSystemInclude;
+}
+
+} // namespace
+
+IncludeSorter::IncludeSorter(const SourceManager *SourceMgr,
+                             const LangOptions *LangOpts, const FileID FileID,
+                             StringRef FileName, IncludeStyle Style)
+    : SourceMgr(SourceMgr), LangOpts(LangOpts), Style(Style),
+      CurrentFileID(FileID), CanonicalFile(MakeCanonicalName(FileName, Style)) {
+}
+
+void IncludeSorter::AddInclude(StringRef FileName, bool IsAngled,
+                               SourceLocation HashLocation,
+                               SourceLocation EndLocation) {
+  int Offset = FindNextLine(SourceMgr->getCharacterData(EndLocation));
+
+  // Record the relevant location information for this inclusion directive.
+  IncludeLocations[FileName].push_back(
+      SourceRange(HashLocation, EndLocation.getLocWithOffset(Offset)));
+  SourceLocations.push_back(IncludeLocations[FileName].back());
+
+  // Stop if this inclusion is a duplicate.
+  if (IncludeLocations[FileName].size() > 1)
+    return;
+
+  // Add the included file's name to the appropriate bucket.
+  IncludeKinds Kind =
+      DetermineIncludeKind(CanonicalFile, FileName, IsAngled, Style);
+  if (Kind != IK_InvalidInclude)
+    IncludeBucket[Kind].push_back(FileName.str());
+}
+
+Optional<FixItHint> IncludeSorter::CreateIncludeInsertion(StringRef FileName,
+                                                          bool IsAngled) {
+  std::string IncludeStmt =
+      IsAngled ? llvm::Twine("#include <" + FileName + ">\n").str()
+               : llvm::Twine("#include \"" + FileName + "\"\n").str();
+  if (SourceLocations.empty()) {
+    // If there are no includes in this file, add it in the first line.
+    // FIXME: insert after the file comment or the header guard, if present.
+    IncludeStmt.append("\n");
+    return FixItHint::CreateInsertion(
+        SourceMgr->getLocForStartOfFile(CurrentFileID), IncludeStmt);
+  }
+
+  auto IncludeKind =
+      DetermineIncludeKind(CanonicalFile, FileName, IsAngled, Style);
+
+  if (!IncludeBucket[IncludeKind].empty()) {
+    for (const std::string &IncludeEntry : IncludeBucket[IncludeKind]) {
+      if (FileName < IncludeEntry) {
+        const auto &Location = IncludeLocations[IncludeEntry][0];
+        return FixItHint::CreateInsertion(Location.getBegin(), IncludeStmt);
+      } else if (FileName == IncludeEntry) {
+        return llvm::None;
+      }
+    }
+    // FileName comes after all include entries in bucket, insert it after
+    // last.
+    const std::string &LastInclude = IncludeBucket[IncludeKind].back();
+    SourceRange LastIncludeLocation = IncludeLocations[LastInclude].back();
+    return FixItHint::CreateInsertion(LastIncludeLocation.getEnd(),
+                                      IncludeStmt);
+  }
+  // Find the non-empty include bucket to be sorted directly above
+  // 'IncludeKind'. If such a bucket exists, we'll want to sort the include
+  // after that bucket. If no such bucket exists, find the first non-empty
+  // include bucket in the file. In that case, we'll want to sort the include
+  // before that bucket.
+  IncludeKinds NonEmptyKind = IK_InvalidInclude;
+  for (int i = IK_InvalidInclude - 1; i >= 0; --i) {
+    if (!IncludeBucket[i].empty()) {
+      NonEmptyKind = static_cast<IncludeKinds>(i);
+      if (NonEmptyKind < IncludeKind)
+        break;
+    }
+  }
+  if (NonEmptyKind == IK_InvalidInclude) {
+    return llvm::None;
+  }
+
+  if (NonEmptyKind < IncludeKind) {
+    // Create a block after.
+    const std::string &LastInclude = IncludeBucket[NonEmptyKind].back();
+    SourceRange LastIncludeLocation = IncludeLocations[LastInclude].back();
+    IncludeStmt = '\n' + IncludeStmt;
+    return FixItHint::CreateInsertion(LastIncludeLocation.getEnd(),
+                                      IncludeStmt);
+  }
+  // Create a block before.
+  const std::string &FirstInclude = IncludeBucket[NonEmptyKind][0];
+  SourceRange FirstIncludeLocation = IncludeLocations[FirstInclude].back();
+  IncludeStmt.append("\n");
+  return FixItHint::CreateInsertion(FirstIncludeLocation.getBegin(),
+                                    IncludeStmt);
+}
+
+std::vector<FixItHint> IncludeSorter::GetEdits() {
+  if (SourceLocations.empty())
+    return {};
+
+  typedef std::map<int, std::pair<SourceRange, std::string>>
+      FileLineToSourceEditMap;
+  FileLineToSourceEditMap Edits;
+  auto SourceLocationIterator = SourceLocations.begin();
+  auto SourceLocationIteratorEnd = SourceLocations.end();
+
+  // Compute the Edits that need to be done to each line to add, replace, or
+  // delete inclusions.
+  for (int IncludeKind = 0; IncludeKind < IK_InvalidInclude; ++IncludeKind) {
+    std::sort(IncludeBucket[IncludeKind].begin(),
+              IncludeBucket[IncludeKind].end());
+    for (const auto &IncludeEntry : IncludeBucket[IncludeKind]) {
+      auto &Location = IncludeLocations[IncludeEntry];
+      SourceRangeVector::iterator LocationIterator = Location.begin();
+      SourceRangeVector::iterator LocationIteratorEnd = Location.end();
+      SourceRange FirstLocation = *LocationIterator;
+
+      // If the first occurrence of a particular include is on the current
+      // source line we are examining, leave it alone.
+      if (FirstLocation == *SourceLocationIterator)
+        ++LocationIterator;
+
+      // Add the deletion Edits for any (remaining) instances of this inclusion,
+      // and remove their Locations from the source Locations to be processed.
+      for (; LocationIterator != LocationIteratorEnd; ++LocationIterator) {
+        int LineNumber =
+            SourceMgr->getSpellingLineNumber(LocationIterator->getBegin());
+        Edits[LineNumber] = std::make_pair(*LocationIterator, "");
+        SourceLocationIteratorEnd =
+            std::remove(SourceLocationIterator, SourceLocationIteratorEnd,
+                        *LocationIterator);
+      }
+
+      if (FirstLocation == *SourceLocationIterator) {
+        // Do nothing except move to the next source Location (Location of an
+        // inclusion in the original, unchanged source file).
+        ++SourceLocationIterator;
+        continue;
+      }
+
+      // Add (or append to) the replacement text for this line in source file.
+      int LineNumber =
+          SourceMgr->getSpellingLineNumber(SourceLocationIterator->getBegin());
+      if (Edits.find(LineNumber) == Edits.end()) {
+        Edits[LineNumber].first =
+            SourceRange(SourceLocationIterator->getBegin());
+      }
+      StringRef SourceText = Lexer::getSourceText(
+          CharSourceRange::getCharRange(FirstLocation), *SourceMgr, *LangOpts);
+      Edits[LineNumber].second.append(SourceText.data(), SourceText.size());
+    }
+
+    // Clear the bucket.
+    IncludeBucket[IncludeKind].clear();
+  }
+
+  // Go through the single-line Edits and combine them into blocks of Edits.
+  int CurrentEndLine = 0;
+  SourceRange CurrentRange;
+  std::string CurrentText;
+  std::vector<FixItHint> Fixes;
+  for (const auto &LineEdit : Edits) {
+    // If the current edit is on the next line after the previous edit, add it
+    // to the current block edit.
+    if (LineEdit.first == CurrentEndLine + 1 &&
+        CurrentRange.getBegin() != CurrentRange.getEnd()) {
+      SourceRange EditRange = LineEdit.second.first;
+      if (EditRange.getBegin() != EditRange.getEnd()) {
+        ++CurrentEndLine;
+        CurrentRange.setEnd(EditRange.getEnd());
+      }
+      CurrentText += LineEdit.second.second;
+      // Otherwise report the current block edit and start a new block.
+    } else {
+      if (CurrentEndLine) {
+        Fixes.push_back(FixItHint::CreateReplacement(
+            CharSourceRange::getCharRange(CurrentRange), CurrentText));
+      }
+
+      CurrentEndLine = LineEdit.first;
+      CurrentRange = LineEdit.second.first;
+      CurrentText = LineEdit.second.second;
+    }
+  }
+  // Finally, report the current block edit if there is one.
+  if (CurrentEndLine) {
+    Fixes.push_back(FixItHint::CreateReplacement(
+        CharSourceRange::getCharRange(CurrentRange), CurrentText));
+  }
+
+  // Reset the remaining internal state.
+  SourceLocations.clear();
+  IncludeLocations.clear();
+  return Fixes;
+}
+
+IncludeSorter::IncludeStyle
+IncludeSorter::parseIncludeStyle(const std::string &Value) {
+  return Value == "llvm" ? IS_LLVM : IS_Google;
+}
+
+StringRef IncludeSorter::toString(IncludeStyle Style) {
+  return Style == IS_LLVM ? "llvm" : "google";
+}
+
+} // namespace utils
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/utils/IncludeSorter.h b/clang-tidy/utils/IncludeSorter.h
new file mode 100644 (file)
index 0000000..07fa293
--- /dev/null
@@ -0,0 +1,86 @@
+//===------------ IncludeSorter.h - clang-tidy ----------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_INCLUDESORTER_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_INCLUDESORTER_H
+
+#include "../ClangTidy.h"
+#include <string>
+
+namespace clang {
+namespace tidy {
+namespace utils {
+
+/// Class used by ``IncludeInserterCallback`` to record the names of the
+/// inclusions in a given source file being processed and generate the necessary
+/// commands to sort the inclusions according to the precedence encoded in
+/// ``IncludeKinds``.
+class IncludeSorter {
+public:
+  /// Supported include styles.
+  enum IncludeStyle { IS_LLVM = 0, IS_Google = 1 };
+
+  /// Converts "llvm" to ``IS_LLVM``, otherwise returns ``IS_Google``.
+  static IncludeStyle parseIncludeStyle(const std::string &Value);
+
+  /// Converts ``IncludeStyle`` to string representation.
+  static StringRef toString(IncludeStyle Style);
+
+  /// The classifications of inclusions, in the order they should be sorted.
+  enum IncludeKinds {
+    IK_MainTUInclude = 0,    ///< e.g. ``#include "foo.h"`` when editing foo.cc
+    IK_CSystemInclude = 1,   ///< e.g. ``#include <stdio.h>``
+    IK_CXXSystemInclude = 2, ///< e.g. ``#include <vector>``
+    IK_NonSystemInclude = 3, ///< e.g. ``#include "bar.h"``
+    IK_InvalidInclude = 4    ///< total number of valid ``IncludeKind``s
+  };
+
+  /// ``IncludeSorter`` constructor; takes the FileID and name of the file to be
+  /// processed by the sorter.
+  IncludeSorter(const SourceManager *SourceMgr, const LangOptions *LangOpts,
+                const FileID FileID, StringRef FileName, IncludeStyle Style);
+
+  /// Returns the ``SourceManager``-specific file ID for the file being handled
+  /// by the sorter.
+  const FileID current_FileID() const { return CurrentFileID; }
+
+  /// Adds the given include directive to the sorter.
+  void AddInclude(StringRef FileName, bool IsAngled,
+                  SourceLocation HashLocation, SourceLocation EndLocation);
+
+  /// Returns the edits needed to sort the current set of includes and reset the
+  /// internal state (so that different blocks of includes are sorted separately
+  /// within the same file).
+  std::vector<FixItHint> GetEdits();
+
+  /// Creates a quoted inclusion directive in the right sort order. Returns None
+  /// on error or if header inclusion directive for header already exists.
+  Optional<FixItHint> CreateIncludeInsertion(StringRef FileName, bool IsAngled);
+
+private:
+  typedef SmallVector<SourceRange, 1> SourceRangeVector;
+
+  const SourceManager *SourceMgr;
+  const LangOptions *LangOpts;
+  const IncludeStyle Style;
+  FileID CurrentFileID;
+  /// The file name stripped of common suffixes.
+  StringRef CanonicalFile;
+  /// Locations of visited include directives.
+  SourceRangeVector SourceLocations;
+  /// Mapping from file name to #include locations.
+  llvm::StringMap<SourceRangeVector> IncludeLocations;
+  /// Includes sorted into buckets.
+  SmallVector<std::string, 1> IncludeBucket[IK_InvalidInclude];
+};
+
+} // namespace utils
+} // namespace tidy
+} // namespace clang
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_INCLUDESORTER_H
diff --git a/clang-tidy/utils/LexerUtils.cpp b/clang-tidy/utils/LexerUtils.cpp
new file mode 100644 (file)
index 0000000..d027219
--- /dev/null
@@ -0,0 +1,41 @@
+//===--- LexerUtils.cpp - clang-tidy---------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "LexerUtils.h"
+
+namespace clang {
+namespace tidy {
+namespace utils {
+namespace lexer {
+
+Token getPreviousToken(const ASTContext &Context, SourceLocation Location,
+                       bool SkipComments) {
+  const auto &SourceManager = Context.getSourceManager();
+  Token Token;
+  Token.setKind(tok::unknown);
+  Location = Location.getLocWithOffset(-1);
+  auto StartOfFile =
+      SourceManager.getLocForStartOfFile(SourceManager.getFileID(Location));
+  while (Location != StartOfFile) {
+    Location = Lexer::GetBeginningOfToken(Location, SourceManager,
+                                          Context.getLangOpts());
+    if (!Lexer::getRawToken(Location, Token, SourceManager,
+                            Context.getLangOpts()) &&
+        (!SkipComments || !Token.is(tok::comment))) {
+      break;
+    }
+    Location = Location.getLocWithOffset(-1);
+  }
+  return Token;
+}
+
+} // namespace lexer
+} // namespace utils
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/utils/LexerUtils.h b/clang-tidy/utils/LexerUtils.h
new file mode 100644 (file)
index 0000000..f7bcd6f
--- /dev/null
@@ -0,0 +1,30 @@
+//===--- LexerUtils.h - clang-tidy-------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_LEXER_UTILS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_LEXER_UTILS_H
+
+#include "clang/AST/ASTContext.h"
+#include "clang/Lex/Lexer.h"
+
+namespace clang {
+namespace tidy {
+namespace utils {
+namespace lexer {
+
+/// Returns previous token or ``tok::unknown`` if not found.
+Token getPreviousToken(const ASTContext &Context, SourceLocation Location,
+                       bool SkipComments = true);
+
+} // namespace lexer
+} // namespace utils
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_LEXER_UTILS_H
diff --git a/clang-tidy/utils/Matchers.h b/clang-tidy/utils/Matchers.h
new file mode 100644 (file)
index 0000000..adafdd9
--- /dev/null
@@ -0,0 +1,51 @@
+//===--- Matchers.h - clang-tidy-------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_MATCHERS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_MATCHERS_H
+
+#include "TypeTraits.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+
+namespace clang {
+namespace tidy {
+namespace matchers {
+
+AST_MATCHER(BinaryOperator, isRelationalOperator) {
+  return Node.isRelationalOp();
+}
+
+AST_MATCHER(BinaryOperator, isEqualityOperator) { return Node.isEqualityOp(); }
+
+AST_MATCHER(BinaryOperator, isComparisonOperator) {
+  return Node.isComparisonOp();
+}
+
+AST_MATCHER(QualType, isExpensiveToCopy) {
+  llvm::Optional<bool> IsExpensive =
+      utils::type_traits::isExpensiveToCopy(Node, Finder->getASTContext());
+  return IsExpensive && *IsExpensive;
+}
+
+AST_MATCHER(RecordDecl, isTriviallyDefaultConstructible) {
+  return utils::type_traits::recordIsTriviallyDefaultConstructible(
+      Node, Finder->getASTContext());
+}
+
+// Returns QualType matcher for references to const.
+AST_MATCHER_FUNCTION(ast_matchers::TypeMatcher, isReferenceToConst) {
+  using namespace ast_matchers;
+  return referenceType(pointee(qualType(isConstQualified())));
+}
+
+} // namespace matchers
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_MATCHERS_H
diff --git a/clang-tidy/utils/NamespaceAliaser.cpp b/clang-tidy/utils/NamespaceAliaser.cpp
new file mode 100644 (file)
index 0000000..1a5120d
--- /dev/null
@@ -0,0 +1,97 @@
+//===---------- NamespaceAliaser.cpp - clang-tidy -------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "NamespaceAliaser.h"
+
+#include "ASTUtils.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Lex/Lexer.h"
+namespace clang {
+namespace tidy {
+namespace utils {
+
+using namespace ast_matchers;
+
+NamespaceAliaser::NamespaceAliaser(const SourceManager &SourceMgr)
+    : SourceMgr(SourceMgr) {}
+
+AST_MATCHER_P(NamespaceAliasDecl, hasTargetNamespace,
+              ast_matchers::internal::Matcher<NamespaceDecl>, innerMatcher) {
+  return innerMatcher.matches(*Node.getNamespace(), Finder, Builder);
+}
+
+Optional<FixItHint>
+NamespaceAliaser::createAlias(ASTContext &Context, const Stmt &Statement,
+                              StringRef Namespace,
+                              const std::vector<std::string> &Abbreviations) {
+  const FunctionDecl *Function = getSurroundingFunction(Context, Statement);
+  if (!Function || !Function->hasBody())
+    return None;
+
+  if (AddedAliases[Function].count(Namespace.str()) != 0)
+    return None;
+
+  // FIXME: Doesn't consider the order of declarations.
+  // If we accidentially pick an alias defined later in the function,
+  // the output won't compile.
+  // FIXME: Also doesn't consider file or class-scope aliases.
+
+  const auto *ExistingAlias = selectFirst<NamedDecl>(
+      "alias",
+      match(functionDecl(hasBody(compoundStmt(has(declStmt(
+                has(namespaceAliasDecl(hasTargetNamespace(hasName(Namespace)))
+                        .bind("alias"))))))),
+            *Function, Context));
+
+  if (ExistingAlias != nullptr) {
+    AddedAliases[Function][Namespace.str()] = ExistingAlias->getName().str();
+    return None;
+  }
+
+  for (const auto &Abbreviation : Abbreviations) {
+    DeclarationMatcher ConflictMatcher = namedDecl(hasName(Abbreviation));
+    const auto HasConflictingChildren =
+        !match(findAll(ConflictMatcher), *Function, Context).empty();
+    const auto HasConflictingAncestors =
+        !match(functionDecl(hasAncestor(decl(has(ConflictMatcher)))), *Function,
+               Context)
+             .empty();
+    if (HasConflictingAncestors || HasConflictingChildren)
+      continue;
+
+    std::string Declaration =
+        (llvm::Twine("\nnamespace ") + Abbreviation + " = " + Namespace + ";")
+            .str();
+    SourceLocation Loc =
+        Lexer::getLocForEndOfToken(Function->getBody()->getLocStart(), 0,
+                                   SourceMgr, Context.getLangOpts());
+    AddedAliases[Function][Namespace.str()] = Abbreviation;
+    return FixItHint::CreateInsertion(Loc, Declaration);
+  }
+
+  return None;
+}
+
+std::string NamespaceAliaser::getNamespaceName(ASTContext &Context,
+                                               const Stmt &Statement,
+                                               StringRef Namespace) const {
+  const auto *Function = getSurroundingFunction(Context, Statement);
+  auto FunctionAliases = AddedAliases.find(Function);
+  if (FunctionAliases != AddedAliases.end()) {
+    if (FunctionAliases->second.count(Namespace) != 0) {
+      return FunctionAliases->second.find(Namespace)->getValue();
+    }
+  }
+  return Namespace.str();
+}
+
+} // namespace utils
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/utils/NamespaceAliaser.h b/clang-tidy/utils/NamespaceAliaser.h
new file mode 100644 (file)
index 0000000..e56d69d
--- /dev/null
@@ -0,0 +1,52 @@
+//===---------- NamespaceAliaser.h - clang-tidy ---------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_NAMESPACEALIASER_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_NAMESPACEALIASER_H
+
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Stmt.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/SourceManager.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/StringMap.h"
+#include <map>
+
+namespace clang {
+namespace tidy {
+namespace utils {
+
+// This class creates function-level namespace aliases.
+class NamespaceAliaser {
+public:
+  explicit NamespaceAliaser(const SourceManager &SourceMgr);
+  // Adds a namespace alias for \p Namespace valid near \p
+  // Statement. Picks the first available name from \p Abbreviations.
+  // Returns ``llvm::None`` if an alias already exists or there is an error.
+  llvm::Optional<FixItHint>
+  createAlias(ASTContext &Context, const Stmt &Statement,
+              llvm::StringRef Namespace,
+              const std::vector<std::string> &Abbreviations);
+
+  // Get an alias name for \p Namespace valid at \p Statement. Returns \p
+  // Namespace if there is no alias.
+  std::string getNamespaceName(ASTContext &Context, const Stmt &Statement,
+                               llvm::StringRef Namespace) const;
+
+private:
+  const SourceManager &SourceMgr;
+  llvm::DenseMap<const FunctionDecl *, llvm::StringMap<std::string>>
+      AddedAliases;
+};
+
+} // namespace utils
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_NAMESPACEALIASER_H
diff --git a/clang-tidy/utils/OptionsUtils.cpp b/clang-tidy/utils/OptionsUtils.cpp
new file mode 100644 (file)
index 0000000..0b1d27d
--- /dev/null
@@ -0,0 +1,38 @@
+//===--- DanglingHandleCheck.cpp - clang-tidy------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "OptionsUtils.h"
+
+namespace clang {
+namespace tidy {
+namespace utils {
+namespace options {
+
+static const char StringsDelimiter[] = ";";
+
+std::vector<std::string> parseStringList(StringRef Option) {
+  SmallVector<StringRef, 4> Names;
+  Option.split(Names, StringsDelimiter);
+  std::vector<std::string> Result;
+  for (StringRef &Name : Names) {
+    Name = Name.trim();
+    if (!Name.empty())
+      Result.push_back(Name);
+  }
+  return Result;
+}
+
+std::string serializeStringList(ArrayRef<std::string> Strings) {
+  return llvm::join(Strings.begin(), Strings.end(), StringsDelimiter);
+}
+
+} // namespace options
+} // namespace utils
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/utils/OptionsUtils.h b/clang-tidy/utils/OptionsUtils.h
new file mode 100644 (file)
index 0000000..d822ac9
--- /dev/null
@@ -0,0 +1,32 @@
+//===--- DanglingHandleCheck.h - clang-tidy----------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_OPTIONUTILS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_OPTIONUTILS_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace utils {
+namespace options {
+
+/// \brief Parse a semicolon separated list of strings.
+std::vector<std::string> parseStringList(StringRef Option);
+
+/// \brief Serialize a sequence of names that can be parsed by
+/// ``parseStringList``.
+std::string serializeStringList(ArrayRef<std::string> Strings);
+
+} // namespace options
+} // namespace utils
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_OPTIONUTILS_H
diff --git a/clang-tidy/utils/TypeTraits.cpp b/clang-tidy/utils/TypeTraits.cpp
new file mode 100644 (file)
index 0000000..6dd4141
--- /dev/null
@@ -0,0 +1,149 @@
+//===--- TypeTraits.cpp - clang-tidy---------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "TypeTraits.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+namespace clang {
+namespace tidy {
+namespace utils {
+namespace type_traits {
+
+namespace {
+
+bool classHasTrivialCopyAndDestroy(QualType Type) {
+  auto *Record = Type->getAsCXXRecordDecl();
+  return Record && Record->hasDefinition() &&
+         !Record->hasNonTrivialCopyConstructor() &&
+         !Record->hasNonTrivialDestructor();
+}
+
+bool hasDeletedCopyConstructor(QualType Type) {
+  auto *Record = Type->getAsCXXRecordDecl();
+  if (!Record || !Record->hasDefinition())
+    return false;
+  for (const auto *Constructor : Record->ctors()) {
+    if (Constructor->isCopyConstructor() && Constructor->isDeleted())
+      return true;
+  }
+  return false;
+}
+
+} // namespace
+
+llvm::Optional<bool> isExpensiveToCopy(QualType Type,
+                                       const ASTContext &Context) {
+  if (Type->isDependentType() || Type->isIncompleteType())
+    return llvm::None;
+  return !Type.isTriviallyCopyableType(Context) &&
+         !classHasTrivialCopyAndDestroy(Type) &&
+         !hasDeletedCopyConstructor(Type);
+}
+
+bool recordIsTriviallyDefaultConstructible(const RecordDecl &RecordDecl,
+                                           const ASTContext &Context) {
+  const auto *ClassDecl = dyn_cast<CXXRecordDecl>(&RecordDecl);
+  // Non-C++ records are always trivially constructible.
+  if (!ClassDecl)
+    return true;
+  // A class with a user-provided default constructor is not trivially
+  // constructible.
+  if (ClassDecl->hasUserProvidedDefaultConstructor())
+    return false;
+  // A polymorphic class is not trivially constructible
+  if (ClassDecl->isPolymorphic())
+    return false;
+  // A class is trivially constructible if it has a trivial default constructor.
+  if (ClassDecl->hasTrivialDefaultConstructor())
+    return true;
+
+  // If all its fields are trivially constructible and have no default
+  // initializers.
+  for (const FieldDecl *Field : ClassDecl->fields()) {
+    if (Field->hasInClassInitializer())
+      return false;
+    if (!isTriviallyDefaultConstructible(Field->getType(), Context))
+      return false;
+  }
+  // If all its direct bases are trivially constructible.
+  for (const CXXBaseSpecifier &Base : ClassDecl->bases()) {
+    if (!isTriviallyDefaultConstructible(Base.getType(), Context))
+      return false;
+    if (Base.isVirtual())
+      return false;
+  }
+
+  return true;
+}
+
+// Based on QualType::isTrivial.
+bool isTriviallyDefaultConstructible(QualType Type, const ASTContext &Context) {
+  if (Type.isNull())
+    return false;
+
+  if (Type->isArrayType())
+    return isTriviallyDefaultConstructible(Context.getBaseElementType(Type),
+                                           Context);
+
+  // Return false for incomplete types after skipping any incomplete array
+  // types which are expressly allowed by the standard and thus our API.
+  if (Type->isIncompleteType())
+    return false;
+
+  if (Context.getLangOpts().ObjCAutoRefCount) {
+    switch (Type.getObjCLifetime()) {
+    case Qualifiers::OCL_ExplicitNone:
+      return true;
+
+    case Qualifiers::OCL_Strong:
+    case Qualifiers::OCL_Weak:
+    case Qualifiers::OCL_Autoreleasing:
+      return false;
+
+    case Qualifiers::OCL_None:
+      if (Type->isObjCLifetimeType())
+        return false;
+      break;
+    }
+  }
+
+  QualType CanonicalType = Type.getCanonicalType();
+  if (CanonicalType->isDependentType())
+    return false;
+
+  // As an extension, Clang treats vector types as Scalar types.
+  if (CanonicalType->isScalarType() || CanonicalType->isVectorType())
+    return true;
+
+  if (const auto *RT = CanonicalType->getAs<RecordType>()) {
+    return recordIsTriviallyDefaultConstructible(*RT->getDecl(), Context);
+  }
+
+  // No other types can match.
+  return false;
+}
+
+bool hasNonTrivialMoveConstructor(QualType Type) {
+  auto *Record = Type->getAsCXXRecordDecl();
+  return Record && Record->hasDefinition() &&
+         Record->hasNonTrivialMoveConstructor();
+}
+
+bool hasNonTrivialMoveAssignment(QualType Type) {
+  auto *Record = Type->getAsCXXRecordDecl();
+  return Record && Record->hasDefinition() &&
+         Record->hasNonTrivialMoveAssignment();
+}
+
+} // namespace type_traits
+} // namespace utils
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/utils/TypeTraits.h b/clang-tidy/utils/TypeTraits.h
new file mode 100644 (file)
index 0000000..ae0b3f0
--- /dev/null
@@ -0,0 +1,43 @@
+//===--- TypeTraits.h - clang-tidy-------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_TYPETRAITS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_TYPETRAITS_H
+
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Type.h"
+
+namespace clang {
+namespace tidy {
+namespace utils {
+namespace type_traits {
+
+/// Returns `true` if `Type` is expensive to copy.
+llvm::Optional<bool> isExpensiveToCopy(QualType Type,
+                                       const ASTContext &Context);
+
+/// Returns `true` if `Type` is trivially default constructible.
+bool isTriviallyDefaultConstructible(QualType Type, const ASTContext &Context);
+
+/// Returns `true` if `RecordDecl` is trivially default constructible.
+bool recordIsTriviallyDefaultConstructible(const RecordDecl &RecordDecl,
+                                           const ASTContext &Context);
+
+/// Returns true if `Type` has a non-trivial move constructor.
+bool hasNonTrivialMoveConstructor(QualType Type);
+
+/// Return true if `Type` has a non-trivial move assignment operator.
+bool hasNonTrivialMoveAssignment(QualType Type);
+
+} // type_traits
+} // namespace utils
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_TYPETRAITS_H
diff --git a/clang-tidy/utils/UsingInserter.cpp b/clang-tidy/utils/UsingInserter.cpp
new file mode 100644 (file)
index 0000000..e7200c9
--- /dev/null
@@ -0,0 +1,89 @@
+//===---------- UsingInserter.cpp - clang-tidy ----------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "UsingInserter.h"
+
+#include "ASTUtils.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Lex/Lexer.h"
+
+namespace clang {
+namespace tidy {
+namespace utils {
+
+using namespace ast_matchers;
+
+static StringRef getUnqualifiedName(StringRef QualifiedName) {
+  size_t LastSeparatorPos = QualifiedName.rfind("::");
+  if (LastSeparatorPos == StringRef::npos)
+    return QualifiedName;
+  return QualifiedName.drop_front(LastSeparatorPos + 2);
+}
+
+UsingInserter::UsingInserter(const SourceManager &SourceMgr)
+    : SourceMgr(SourceMgr) {}
+
+Optional<FixItHint> UsingInserter::createUsingDeclaration(
+    ASTContext &Context, const Stmt &Statement, StringRef QualifiedName) {
+  StringRef UnqualifiedName = getUnqualifiedName(QualifiedName);
+  const FunctionDecl *Function = getSurroundingFunction(Context, Statement);
+  if (!Function)
+    return None;
+
+  if (AddedUsing.count(std::make_pair(Function, QualifiedName.str())) != 0)
+    return None;
+
+  SourceLocation InsertLoc = Lexer::getLocForEndOfToken(
+      Function->getBody()->getLocStart(), 0, SourceMgr, Context.getLangOpts());
+
+  // Only use using declarations in the main file, not in includes.
+  if (SourceMgr.getFileID(InsertLoc) != SourceMgr.getMainFileID())
+    return None;
+
+  // FIXME: This declaration could be masked. Investigate if
+  // there is a way to avoid using Sema.
+  bool AlreadyHasUsingDecl =
+      !match(stmt(hasAncestor(decl(has(usingDecl(hasAnyUsingShadowDecl(
+                 hasTargetDecl(hasName(QualifiedName.str())))))))),
+             Statement, Context)
+           .empty();
+  if (AlreadyHasUsingDecl) {
+    AddedUsing.emplace(NameInFunction(Function, QualifiedName.str()));
+    return None;
+  }
+  // Find conflicting declarations and references.
+  auto ConflictingDecl = namedDecl(hasName(UnqualifiedName));
+  bool HasConflictingDeclaration =
+      !match(findAll(ConflictingDecl), *Function, Context).empty();
+  bool HasConflictingDeclRef =
+      !match(findAll(declRefExpr(to(ConflictingDecl))), *Function, Context)
+           .empty();
+  if (HasConflictingDeclaration || HasConflictingDeclRef)
+    return None;
+
+  std::string Declaration =
+      (llvm::Twine("\nusing ") + QualifiedName + ";").str();
+
+  AddedUsing.emplace(std::make_pair(Function, QualifiedName.str()));
+  return FixItHint::CreateInsertion(InsertLoc, Declaration);
+}
+
+StringRef UsingInserter::getShortName(ASTContext &Context,
+                                      const Stmt &Statement,
+                                      StringRef QualifiedName) {
+  const FunctionDecl *Function = getSurroundingFunction(Context, Statement);
+  if (AddedUsing.count(NameInFunction(Function, QualifiedName.str())) != 0)
+    return getUnqualifiedName(QualifiedName);
+  return QualifiedName;
+}
+
+} // namespace utils
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/utils/UsingInserter.h b/clang-tidy/utils/UsingInserter.h
new file mode 100644 (file)
index 0000000..62108e4
--- /dev/null
@@ -0,0 +1,50 @@
+//===---------- UsingInserter.h - clang-tidy ----------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_USINGINSERTER_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_USINGINSERTER_H
+
+#include "clang/AST/Decl.h"
+#include "clang/AST/Stmt.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/SourceManager.h"
+#include <set>
+
+namespace clang {
+namespace tidy {
+namespace utils {
+
+// UsingInserter adds using declarations for |QualifiedName| to the surrounding
+// function.
+// This allows using a shorter name without clobbering other scopes.
+class UsingInserter {
+public:
+  UsingInserter(const SourceManager &SourceMgr);
+
+  // Creates a \p using declaration fixit. Returns ``llvm::None`` on error
+  // or if the using declaration already exists.
+  llvm::Optional<FixItHint>
+  createUsingDeclaration(ASTContext &Context, const Stmt &Statement,
+                         llvm::StringRef QualifiedName);
+
+  // Returns the unqualified version of the name if there is an
+  // appropriate using declaration and the qualified name otherwise.
+  llvm::StringRef getShortName(ASTContext &Context, const Stmt &Statement,
+                               llvm::StringRef QualifiedName);
+
+private:
+  typedef std::pair<const FunctionDecl *, std::string> NameInFunction;
+  const SourceManager &SourceMgr;
+  std::set<NameInFunction> AddedUsing;
+};
+
+} // namespace utils
+} // namespace tidy
+} // namespace clang
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_USINGINSERTER_H
diff --git a/clangd/CMakeLists.txt b/clangd/CMakeLists.txt
new file mode 100644 (file)
index 0000000..446ce54
--- /dev/null
@@ -0,0 +1,29 @@
+set(LLVM_LINK_COMPONENTS
+  Support
+  )
+
+add_clang_library(clangDaemon
+  ClangdLSPServer.cpp
+  ClangdServer.cpp
+  ClangdUnit.cpp
+  ClangdUnitStore.cpp
+  DraftStore.cpp
+  GlobalCompilationDatabase.cpp
+  JSONRPCDispatcher.cpp
+  Protocol.cpp
+  ProtocolHandlers.cpp
+
+  LINK_LIBS
+  clangAST
+  clangBasic
+  clangFormat
+  clangFrontend
+  clangIndex
+  clangLex
+  clangSema
+  clangTooling
+  clangToolingCore
+  ${LLVM_PTHREAD_LIB}
+  )
+
+add_subdirectory(tool)
diff --git a/clangd/ClangdLSPServer.cpp b/clangd/ClangdLSPServer.cpp
new file mode 100644 (file)
index 0000000..e0489eb
--- /dev/null
@@ -0,0 +1,287 @@
+//===--- ClangdLSPServer.cpp - LSP server ------------------------*- C++-*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===---------------------------------------------------------------------===//
+
+#include "ClangdLSPServer.h"
+#include "JSONRPCDispatcher.h"
+#include "ProtocolHandlers.h"
+
+using namespace clang::clangd;
+using namespace clang;
+
+namespace {
+
+std::string
+replacementsToEdits(StringRef Code,
+                    const std::vector<tooling::Replacement> &Replacements) {
+  // Turn the replacements into the format specified by the Language Server
+  // Protocol. Fuse them into one big JSON array.
+  std::string Edits;
+  for (auto &R : Replacements) {
+    Range ReplacementRange = {
+        offsetToPosition(Code, R.getOffset()),
+        offsetToPosition(Code, R.getOffset() + R.getLength())};
+    TextEdit TE = {ReplacementRange, R.getReplacementText()};
+    Edits += TextEdit::unparse(TE);
+    Edits += ',';
+  }
+  if (!Edits.empty())
+    Edits.pop_back();
+
+  return Edits;
+}
+
+} // namespace
+
+ClangdLSPServer::LSPDiagnosticsConsumer::LSPDiagnosticsConsumer(
+    ClangdLSPServer &Server)
+    : Server(Server) {}
+
+void ClangdLSPServer::LSPDiagnosticsConsumer::onDiagnosticsReady(
+    PathRef File, Tagged<std::vector<DiagWithFixIts>> Diagnostics) {
+  Server.consumeDiagnostics(File, Diagnostics.Value);
+}
+
+class ClangdLSPServer::LSPProtocolCallbacks : public ProtocolCallbacks {
+public:
+  LSPProtocolCallbacks(ClangdLSPServer &LangServer) : LangServer(LangServer) {}
+
+  void onInitialize(StringRef ID, JSONOutput &Out) override;
+  void onShutdown(JSONOutput &Out) override;
+  void onDocumentDidOpen(DidOpenTextDocumentParams Params,
+                         JSONOutput &Out) override;
+  void onDocumentDidChange(DidChangeTextDocumentParams Params,
+                           JSONOutput &Out) override;
+  void onDocumentDidClose(DidCloseTextDocumentParams Params,
+                          JSONOutput &Out) override;
+  void onDocumentOnTypeFormatting(DocumentOnTypeFormattingParams Params,
+                                  StringRef ID, JSONOutput &Out) override;
+  void onDocumentRangeFormatting(DocumentRangeFormattingParams Params,
+                                 StringRef ID, JSONOutput &Out) override;
+  void onDocumentFormatting(DocumentFormattingParams Params, StringRef ID,
+                            JSONOutput &Out) override;
+  void onCodeAction(CodeActionParams Params, StringRef ID,
+                    JSONOutput &Out) override;
+  void onCompletion(TextDocumentPositionParams Params, StringRef ID,
+                    JSONOutput &Out) override;
+  void onGoToDefinition(TextDocumentPositionParams Params, StringRef ID,
+                            JSONOutput &Out) override;
+
+private:
+  ClangdLSPServer &LangServer;
+};
+
+void ClangdLSPServer::LSPProtocolCallbacks::onInitialize(StringRef ID,
+                                                         JSONOutput &Out) {
+  Out.writeMessage(
+      R"({"jsonrpc":"2.0","id":)" + ID +
+      R"(,"result":{"capabilities":{
+          "textDocumentSync": 1,
+          "documentFormattingProvider": true,
+          "documentRangeFormattingProvider": true,
+          "documentOnTypeFormattingProvider": {"firstTriggerCharacter":"}","moreTriggerCharacter":[]},
+          "codeActionProvider": true,
+          "completionProvider": {"resolveProvider": false, "triggerCharacters": [".",">"]},
+          "definitionProvider": true
+        }}})");
+}
+
+void ClangdLSPServer::LSPProtocolCallbacks::onShutdown(JSONOutput &Out) {
+  LangServer.IsDone = true;
+}
+
+void ClangdLSPServer::LSPProtocolCallbacks::onDocumentDidOpen(
+    DidOpenTextDocumentParams Params, JSONOutput &Out) {
+  if (Params.metadata && !Params.metadata->extraFlags.empty())
+    LangServer.CDB.setExtraFlagsForFile(Params.textDocument.uri.file,
+                                        std::move(Params.metadata->extraFlags));
+  LangServer.Server.addDocument(Params.textDocument.uri.file,
+                                Params.textDocument.text);
+}
+
+void ClangdLSPServer::LSPProtocolCallbacks::onDocumentDidChange(
+    DidChangeTextDocumentParams Params, JSONOutput &Out) {
+  // We only support full syncing right now.
+  LangServer.Server.addDocument(Params.textDocument.uri.file,
+                                Params.contentChanges[0].text);
+}
+
+void ClangdLSPServer::LSPProtocolCallbacks::onDocumentDidClose(
+    DidCloseTextDocumentParams Params, JSONOutput &Out) {
+  LangServer.Server.removeDocument(Params.textDocument.uri.file);
+}
+
+void ClangdLSPServer::LSPProtocolCallbacks::onDocumentOnTypeFormatting(
+    DocumentOnTypeFormattingParams Params, StringRef ID, JSONOutput &Out) {
+  auto File = Params.textDocument.uri.file;
+  std::string Code = LangServer.Server.getDocument(File);
+  std::string Edits = replacementsToEdits(
+      Code, LangServer.Server.formatOnType(File, Params.position));
+
+  Out.writeMessage(R"({"jsonrpc":"2.0","id":)" + ID.str() +
+                   R"(,"result":[)" + Edits + R"(]})");
+}
+
+void ClangdLSPServer::LSPProtocolCallbacks::onDocumentRangeFormatting(
+    DocumentRangeFormattingParams Params, StringRef ID, JSONOutput &Out) {
+  auto File = Params.textDocument.uri.file;
+  std::string Code = LangServer.Server.getDocument(File);
+  std::string Edits = replacementsToEdits(
+      Code, LangServer.Server.formatRange(File, Params.range));
+
+  Out.writeMessage(R"({"jsonrpc":"2.0","id":)" + ID.str() +
+                   R"(,"result":[)" + Edits + R"(]})");
+}
+
+void ClangdLSPServer::LSPProtocolCallbacks::onDocumentFormatting(
+    DocumentFormattingParams Params, StringRef ID, JSONOutput &Out) {
+  auto File = Params.textDocument.uri.file;
+  std::string Code = LangServer.Server.getDocument(File);
+  std::string Edits =
+      replacementsToEdits(Code, LangServer.Server.formatFile(File));
+
+  Out.writeMessage(R"({"jsonrpc":"2.0","id":)" + ID.str() +
+                   R"(,"result":[)" + Edits + R"(]})");
+}
+
+void ClangdLSPServer::LSPProtocolCallbacks::onCodeAction(
+    CodeActionParams Params, StringRef ID, JSONOutput &Out) {
+  // We provide a code action for each diagnostic at the requested location
+  // which has FixIts available.
+  std::string Code =
+      LangServer.Server.getDocument(Params.textDocument.uri.file);
+  std::string Commands;
+  for (Diagnostic &D : Params.context.diagnostics) {
+    std::vector<clang::tooling::Replacement> Fixes =
+        LangServer.getFixIts(Params.textDocument.uri.file, D);
+    std::string Edits = replacementsToEdits(Code, Fixes);
+
+    if (!Edits.empty())
+      Commands +=
+          R"({"title":"Apply FixIt ')" + llvm::yaml::escape(D.message) +
+          R"('", "command": "clangd.applyFix", "arguments": [")" +
+          llvm::yaml::escape(Params.textDocument.uri.uri) +
+          R"(", [)" + Edits +
+          R"(]]},)";
+  }
+  if (!Commands.empty())
+    Commands.pop_back();
+
+  Out.writeMessage(
+      R"({"jsonrpc":"2.0","id":)" + ID.str() +
+      R"(, "result": [)" + Commands +
+      R"(]})");
+}
+
+void ClangdLSPServer::LSPProtocolCallbacks::onCompletion(
+    TextDocumentPositionParams Params, StringRef ID, JSONOutput &Out) {
+
+  auto Items = LangServer.Server.codeComplete(
+      Params.textDocument.uri.file,
+      Position{Params.position.line, Params.position.character}).Value;
+
+  std::string Completions;
+  for (const auto &Item : Items) {
+    Completions += CompletionItem::unparse(Item);
+    Completions += ",";
+  }
+  if (!Completions.empty())
+    Completions.pop_back();
+  Out.writeMessage(
+      R"({"jsonrpc":"2.0","id":)" + ID.str() +
+      R"(,"result":[)" + Completions + R"(]})");
+}
+
+void ClangdLSPServer::LSPProtocolCallbacks::onGoToDefinition(
+    TextDocumentPositionParams Params, StringRef ID, JSONOutput &Out) {
+
+  auto Items = LangServer.Server.findDefinitions(
+      Params.textDocument.uri.file,
+      Position{Params.position.line, Params.position.character}).Value;
+
+  std::string Locations;
+  for (const auto &Item : Items) {
+    Locations += Location::unparse(Item);
+    Locations += ",";
+  }
+  if (!Locations.empty())
+    Locations.pop_back();
+  Out.writeMessage(
+      R"({"jsonrpc":"2.0","id":)" + ID.str() +
+      R"(,"result":[)" + Locations + R"(]})");
+}
+
+ClangdLSPServer::ClangdLSPServer(JSONOutput &Out, bool RunSynchronously)
+    : Out(Out), DiagConsumer(*this),
+      Server(CDB, DiagConsumer, FSProvider, RunSynchronously) {}
+
+void ClangdLSPServer::run(std::istream &In) {
+  assert(!IsDone && "Run was called before");
+
+  // Set up JSONRPCDispatcher.
+  LSPProtocolCallbacks Callbacks(*this);
+  JSONRPCDispatcher Dispatcher(llvm::make_unique<Handler>(Out));
+  regiterCallbackHandlers(Dispatcher, Out, Callbacks);
+
+  // Run the Language Server loop.
+  runLanguageServerLoop(In, Out, Dispatcher, IsDone);
+
+  // Make sure IsDone is set to true after this method exits to ensure assertion
+  // at the start of the method fires if it's ever executed again.
+  IsDone = true;
+}
+
+std::vector<clang::tooling::Replacement>
+ClangdLSPServer::getFixIts(StringRef File, const clangd::Diagnostic &D) {
+  std::lock_guard<std::mutex> Lock(FixItsMutex);
+  auto DiagToFixItsIter = FixItsMap.find(File);
+  if (DiagToFixItsIter == FixItsMap.end())
+    return {};
+
+  const auto &DiagToFixItsMap = DiagToFixItsIter->second;
+  auto FixItsIter = DiagToFixItsMap.find(D);
+  if (FixItsIter == DiagToFixItsMap.end())
+    return {};
+
+  return FixItsIter->second;
+}
+
+void ClangdLSPServer::consumeDiagnostics(
+    PathRef File, std::vector<DiagWithFixIts> Diagnostics) {
+  std::string DiagnosticsJSON;
+
+  DiagnosticToReplacementMap LocalFixIts; // Temporary storage
+  for (auto &DiagWithFixes : Diagnostics) {
+    auto Diag = DiagWithFixes.Diag;
+    DiagnosticsJSON +=
+        R"({"range":)" + Range::unparse(Diag.range) +
+        R"(,"severity":)" + std::to_string(Diag.severity) +
+        R"(,"message":")" + llvm::yaml::escape(Diag.message) +
+        R"("},)";
+
+    // We convert to Replacements to become independent of the SourceManager.
+    auto &FixItsForDiagnostic = LocalFixIts[Diag];
+    std::copy(DiagWithFixes.FixIts.begin(), DiagWithFixes.FixIts.end(),
+              std::back_inserter(FixItsForDiagnostic));
+  }
+
+  // Cache FixIts
+  {
+    // FIXME(ibiryukov): should be deleted when documents are removed
+    std::lock_guard<std::mutex> Lock(FixItsMutex);
+    FixItsMap[File] = LocalFixIts;
+  }
+
+  // Publish diagnostics.
+  if (!DiagnosticsJSON.empty())
+    DiagnosticsJSON.pop_back(); // Drop trailing comma.
+  Out.writeMessage(
+      R"({"jsonrpc":"2.0","method":"textDocument/publishDiagnostics","params":{"uri":")" +
+      URI::fromFile(File).uri + R"(","diagnostics":[)" + DiagnosticsJSON +
+      R"(]}})");
+}
diff --git a/clangd/ClangdLSPServer.h b/clangd/ClangdLSPServer.h
new file mode 100644 (file)
index 0000000..e8e66f6
--- /dev/null
@@ -0,0 +1,86 @@
+//===--- ClangdLSPServer.h - LSP server --------------------------*- C++-*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===---------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDLSPSERVER_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDLSPSERVER_H
+
+#include "ClangdServer.h"
+#include "GlobalCompilationDatabase.h"
+#include "Path.h"
+#include "Protocol.h"
+#include "clang/Tooling/Core/Replacement.h"
+
+namespace clang {
+namespace clangd {
+
+class JSONOutput;
+
+/// This class provides implementation of an LSP server, glueing the JSON
+/// dispatch and ClangdServer together.
+class ClangdLSPServer {
+public:
+  ClangdLSPServer(JSONOutput &Out, bool RunSynchronously);
+
+  /// Run LSP server loop, receiving input for it from \p In. \p In must be
+  /// opened in binary mode. Output will be written using Out variable passed to
+  /// class constructor. This method must not be executed more than once for
+  /// each instance of ClangdLSPServer.
+  void run(std::istream &In);
+
+private:
+  class LSPProtocolCallbacks;
+  class LSPDiagnosticsConsumer : public DiagnosticsConsumer {
+  public:
+    LSPDiagnosticsConsumer(ClangdLSPServer &Server);
+
+    virtual void
+    onDiagnosticsReady(PathRef File,
+                       Tagged<std::vector<DiagWithFixIts>> Diagnostics);
+
+  private:
+    ClangdLSPServer &Server;
+  };
+
+  std::vector<clang::tooling::Replacement>
+  getFixIts(StringRef File, const clangd::Diagnostic &D);
+
+  /// Function that will be called on a separate thread when diagnostics are
+  /// ready. Sends the Dianostics to LSP client via Out.writeMessage and caches
+  /// corresponding fixits in the FixItsMap.
+  void consumeDiagnostics(PathRef File,
+                          std::vector<DiagWithFixIts> Diagnostics);
+
+  JSONOutput &Out;
+  /// Used to indicate that the 'shutdown' request was received from the
+  /// Language Server client.
+  /// It's used to break out of the LSP parsing loop.
+  bool IsDone = false;
+
+  std::mutex FixItsMutex;
+  typedef std::map<clangd::Diagnostic, std::vector<clang::tooling::Replacement>>
+      DiagnosticToReplacementMap;
+  /// Caches FixIts per file and diagnostics
+  llvm::StringMap<DiagnosticToReplacementMap> FixItsMap;
+
+  // Various ClangdServer parameters go here. It's important they're created
+  // before ClangdServer.
+  DirectoryBasedGlobalCompilationDatabase CDB;
+  LSPDiagnosticsConsumer DiagConsumer;
+  RealFileSystemProvider FSProvider;
+
+  // Server must be the last member of the class to allow its destructor to exit
+  // the worker thread that may otherwise run an async callback on partially
+  // destructed instance of ClangdLSPServer.
+  ClangdServer Server;
+};
+
+} // namespace clangd
+} // namespace clang
+
+#endif
diff --git a/clangd/ClangdServer.cpp b/clangd/ClangdServer.cpp
new file mode 100644 (file)
index 0000000..0a1f927
--- /dev/null
@@ -0,0 +1,288 @@
+//===--- ClangdServer.cpp - Main clangd server code --------------*- C++-*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===-------------------------------------------------------------------===//
+
+#include "ClangdServer.h"
+#include "clang/Format/Format.h"
+#include "clang/Frontend/ASTUnit.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/CompilerInvocation.h"
+#include "clang/Tooling/CompilationDatabase.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/raw_ostream.h"
+#include <future>
+
+using namespace clang;
+using namespace clang::clangd;
+
+namespace {
+
+std::vector<tooling::Replacement> formatCode(StringRef Code, StringRef Filename,
+                                             ArrayRef<tooling::Range> Ranges) {
+  // Call clang-format.
+  // FIXME: Don't ignore style.
+  format::FormatStyle Style = format::getLLVMStyle();
+  auto Result = format::reformat(Style, Code, Ranges, Filename);
+
+  return std::vector<tooling::Replacement>(Result.begin(), Result.end());
+}
+
+std::string getStandardResourceDir() {
+  static int Dummy; // Just an address in this process.
+  return CompilerInvocation::GetResourcesPath("clangd", (void *)&Dummy);
+}
+
+} // namespace
+
+size_t clangd::positionToOffset(StringRef Code, Position P) {
+  size_t Offset = 0;
+  for (int I = 0; I != P.line; ++I) {
+    // FIXME: \r\n
+    // FIXME: UTF-8
+    size_t F = Code.find('\n', Offset);
+    if (F == StringRef::npos)
+      return 0; // FIXME: Is this reasonable?
+    Offset = F + 1;
+  }
+  return (Offset == 0 ? 0 : (Offset - 1)) + P.character;
+}
+
+/// Turn an offset in Code into a [line, column] pair.
+Position clangd::offsetToPosition(StringRef Code, size_t Offset) {
+  StringRef JustBefore = Code.substr(0, Offset);
+  // FIXME: \r\n
+  // FIXME: UTF-8
+  int Lines = JustBefore.count('\n');
+  int Cols = JustBefore.size() - JustBefore.rfind('\n') - 1;
+  return {Lines, Cols};
+}
+
+Tagged<IntrusiveRefCntPtr<vfs::FileSystem>>
+RealFileSystemProvider::getTaggedFileSystem(PathRef File) {
+  return make_tagged(vfs::getRealFileSystem(), VFSTag());
+}
+
+ClangdScheduler::ClangdScheduler(bool RunSynchronously)
+    : RunSynchronously(RunSynchronously) {
+  if (RunSynchronously) {
+    // Don't start the worker thread if we're running synchronously
+    return;
+  }
+
+  // Initialize Worker in ctor body, rather than init list to avoid potentially
+  // using not-yet-initialized members
+  Worker = std::thread([this]() {
+    while (true) {
+      std::function<void()> Request;
+
+      // Pick request from the queue
+      {
+        std::unique_lock<std::mutex> Lock(Mutex);
+        // Wait for more requests.
+        RequestCV.wait(Lock, [this] { return !RequestQueue.empty() || Done; });
+        if (Done)
+          return;
+
+        assert(!RequestQueue.empty() && "RequestQueue was empty");
+
+        // We process requests starting from the front of the queue. Users of
+        // ClangdScheduler have a way to prioritise their requests by putting
+        // them to the either side of the queue (using either addToEnd or
+        // addToFront).
+        Request = std::move(RequestQueue.front());
+        RequestQueue.pop_front();
+      } // unlock Mutex
+
+      Request();
+    }
+  });
+}
+
+ClangdScheduler::~ClangdScheduler() {
+  if (RunSynchronously)
+    return; // no worker thread is running in that case
+
+  {
+    std::lock_guard<std::mutex> Lock(Mutex);
+    // Wake up the worker thread
+    Done = true;
+  } // unlock Mutex
+  RequestCV.notify_one();
+  Worker.join();
+}
+
+void ClangdScheduler::addToFront(std::function<void()> Request) {
+  if (RunSynchronously) {
+    Request();
+    return;
+  }
+
+  {
+    std::lock_guard<std::mutex> Lock(Mutex);
+    RequestQueue.push_front(Request);
+  }
+  RequestCV.notify_one();
+}
+
+void ClangdScheduler::addToEnd(std::function<void()> Request) {
+  if (RunSynchronously) {
+    Request();
+    return;
+  }
+
+  {
+    std::lock_guard<std::mutex> Lock(Mutex);
+    RequestQueue.push_back(Request);
+  }
+  RequestCV.notify_one();
+}
+
+ClangdServer::ClangdServer(GlobalCompilationDatabase &CDB,
+                           DiagnosticsConsumer &DiagConsumer,
+                           FileSystemProvider &FSProvider,
+                           bool RunSynchronously,
+                           llvm::Optional<StringRef> ResourceDir)
+    : CDB(CDB), DiagConsumer(DiagConsumer), FSProvider(FSProvider),
+      ResourceDir(ResourceDir ? ResourceDir->str() : getStandardResourceDir()),
+      PCHs(std::make_shared<PCHContainerOperations>()),
+      WorkScheduler(RunSynchronously) {}
+
+void ClangdServer::addDocument(PathRef File, StringRef Contents) {
+  DocVersion Version = DraftMgr.updateDraft(File, Contents);
+  Path FileStr = File;
+  WorkScheduler.addToFront([this, FileStr, Version]() {
+    auto FileContents = DraftMgr.getDraft(FileStr);
+    if (FileContents.Version != Version)
+      return; // This request is outdated, do nothing
+
+    assert(FileContents.Draft &&
+           "No contents inside a file that was scheduled for reparse");
+    auto TaggedFS = FSProvider.getTaggedFileSystem(FileStr);
+    Units.runOnUnit(
+        FileStr, *FileContents.Draft, ResourceDir, CDB, PCHs, TaggedFS.Value,
+        [&](ClangdUnit const &Unit) {
+          DiagConsumer.onDiagnosticsReady(
+              FileStr, make_tagged(Unit.getLocalDiagnostics(), TaggedFS.Tag));
+        });
+  });
+}
+
+void ClangdServer::removeDocument(PathRef File) {
+  auto Version = DraftMgr.removeDraft(File);
+  Path FileStr = File;
+  WorkScheduler.addToFront([this, FileStr, Version]() {
+    if (Version != DraftMgr.getVersion(FileStr))
+      return; // This request is outdated, do nothing
+
+    Units.removeUnitIfPresent(FileStr);
+  });
+}
+
+void ClangdServer::forceReparse(PathRef File) {
+  // The addDocument schedules the reparse even if the contents of the file
+  // never changed, so we just call it here.
+  addDocument(File, getDocument(File));
+}
+
+Tagged<std::vector<CompletionItem>>
+ClangdServer::codeComplete(PathRef File, Position Pos,
+                           llvm::Optional<StringRef> OverridenContents) {
+  std::string DraftStorage;
+  if (!OverridenContents) {
+    auto FileContents = DraftMgr.getDraft(File);
+    assert(FileContents.Draft &&
+           "codeComplete is called for non-added document");
+
+    DraftStorage = std::move(*FileContents.Draft);
+    OverridenContents = DraftStorage;
+  }
+
+  std::vector<CompletionItem> Result;
+  auto TaggedFS = FSProvider.getTaggedFileSystem(File);
+  // It would be nice to use runOnUnitWithoutReparse here, but we can't
+  // guarantee the correctness of code completion cache here if we don't do the
+  // reparse.
+  Units.runOnUnit(File, *OverridenContents, ResourceDir, CDB, PCHs,
+                  TaggedFS.Value, [&](ClangdUnit &Unit) {
+                    Result = Unit.codeComplete(*OverridenContents, Pos,
+                                               TaggedFS.Value);
+                  });
+  return make_tagged(std::move(Result), TaggedFS.Tag);
+}
+
+std::vector<tooling::Replacement> ClangdServer::formatRange(PathRef File,
+                                                            Range Rng) {
+  std::string Code = getDocument(File);
+
+  size_t Begin = positionToOffset(Code, Rng.start);
+  size_t Len = positionToOffset(Code, Rng.end) - Begin;
+  return formatCode(Code, File, {tooling::Range(Begin, Len)});
+}
+
+std::vector<tooling::Replacement> ClangdServer::formatFile(PathRef File) {
+  // Format everything.
+  std::string Code = getDocument(File);
+  return formatCode(Code, File, {tooling::Range(0, Code.size())});
+}
+
+std::vector<tooling::Replacement> ClangdServer::formatOnType(PathRef File,
+                                                             Position Pos) {
+  // Look for the previous opening brace from the character position and
+  // format starting from there.
+  std::string Code = getDocument(File);
+  size_t CursorPos = positionToOffset(Code, Pos);
+  size_t PreviousLBracePos = StringRef(Code).find_last_of('{', CursorPos);
+  if (PreviousLBracePos == StringRef::npos)
+    PreviousLBracePos = CursorPos;
+  size_t Len = 1 + CursorPos - PreviousLBracePos;
+
+  return formatCode(Code, File, {tooling::Range(PreviousLBracePos, Len)});
+}
+
+std::string ClangdServer::getDocument(PathRef File) {
+  auto draft = DraftMgr.getDraft(File);
+  assert(draft.Draft && "File is not tracked, cannot get contents");
+  return *draft.Draft;
+}
+
+std::string ClangdServer::dumpAST(PathRef File) {
+  std::promise<std::string> DumpPromise;
+  auto DumpFuture = DumpPromise.get_future();
+  auto Version = DraftMgr.getVersion(File);
+
+  WorkScheduler.addToEnd([this, &DumpPromise, File, Version]() {
+    assert(DraftMgr.getVersion(File) == Version && "Version has changed");
+    (void)Version;
+
+    Units.runOnExistingUnit(File, [&DumpPromise](ClangdUnit &Unit) {
+      std::string Result;
+
+      llvm::raw_string_ostream ResultOS(Result);
+      Unit.dumpAST(ResultOS);
+      ResultOS.flush();
+
+      DumpPromise.set_value(std::move(Result));
+    });
+  });
+  return DumpFuture.get();
+}
+
+Tagged<std::vector<Location>>
+ClangdServer::findDefinitions(PathRef File, Position Pos) {
+  auto FileContents = DraftMgr.getDraft(File);
+  assert(FileContents.Draft && "findDefinitions is called for non-added document");
+
+  std::vector<Location> Result;
+  auto TaggedFS = FSProvider.getTaggedFileSystem(File);
+  Units.runOnUnit(File, *FileContents.Draft, ResourceDir, CDB, PCHs,
+      TaggedFS.Value, [&](ClangdUnit &Unit) {
+        Result = Unit.findDefinitions(Pos);
+      });
+  return make_tagged(std::move(Result), TaggedFS.Tag);
+}
diff --git a/clangd/ClangdServer.h b/clangd/ClangdServer.h
new file mode 100644 (file)
index 0000000..8a2936f
--- /dev/null
@@ -0,0 +1,220 @@
+//===--- ClangdServer.h - Main clangd server code ----------------*- C++-*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDSERVER_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDSERVER_H
+
+#include "ClangdUnitStore.h"
+#include "DraftStore.h"
+#include "GlobalCompilationDatabase.h"
+#include "clang/Frontend/ASTUnit.h"
+#include "clang/Tooling/CompilationDatabase.h"
+#include "clang/Tooling/Core/Replacement.h"
+#include "llvm/ADT/IntrusiveRefCntPtr.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/StringRef.h"
+
+#include "ClangdUnit.h"
+#include "Protocol.h"
+
+#include <condition_variable>
+#include <functional>
+#include <mutex>
+#include <string>
+#include <thread>
+#include <type_traits>
+#include <utility>
+
+namespace clang {
+class PCHContainerOperations;
+
+namespace clangd {
+
+/// Turn a [line, column] pair into an offset in Code.
+size_t positionToOffset(StringRef Code, Position P);
+
+/// Turn an offset in Code into a [line, column] pair.
+Position offsetToPosition(StringRef Code, size_t Offset);
+
+/// A tag supplied by the FileSytemProvider.
+typedef std::string VFSTag;
+
+/// A value of an arbitrary type and VFSTag that was supplied by the
+/// FileSystemProvider when this value was computed.
+template <class T> class Tagged {
+public:
+  template <class U>
+  Tagged(U &&Value, VFSTag Tag)
+      : Value(std::forward<U>(Value)), Tag(std::move(Tag)) {}
+
+  template <class U>
+  Tagged(const Tagged<U> &Other) : Value(Other.Value), Tag(Other.Tag) {}
+
+  template <class U>
+  Tagged(Tagged<U> &&Other)
+      : Value(std::move(Other.Value)), Tag(std::move(Other.Tag)) {}
+
+  T Value;
+  VFSTag Tag;
+};
+
+template <class T>
+Tagged<typename std::decay<T>::type> make_tagged(T &&Value, VFSTag Tag) {
+  return Tagged<T>(std::forward<T>(Value), Tag);
+}
+
+class DiagnosticsConsumer {
+public:
+  virtual ~DiagnosticsConsumer() = default;
+
+  /// Called by ClangdServer when \p Diagnostics for \p File are ready.
+  virtual void
+  onDiagnosticsReady(PathRef File,
+                     Tagged<std::vector<DiagWithFixIts>> Diagnostics) = 0;
+};
+
+class FileSystemProvider {
+public:
+  virtual ~FileSystemProvider() = default;
+  /// Called by ClangdServer to obtain a vfs::FileSystem to be used for parsing.
+  /// Name of the file that will be parsed is passed in \p File.
+  ///
+  /// \return A filesystem that will be used for all file accesses in clangd.
+  /// A Tag returned by this method will be propagated to all results of clangd
+  /// that will use this filesystem.
+  virtual Tagged<IntrusiveRefCntPtr<vfs::FileSystem>>
+  getTaggedFileSystem(PathRef File) = 0;
+};
+
+class RealFileSystemProvider : public FileSystemProvider {
+public:
+  /// \return getRealFileSystem() tagged with default tag, i.e. VFSTag()
+  Tagged<IntrusiveRefCntPtr<vfs::FileSystem>>
+  getTaggedFileSystem(PathRef File) override;
+};
+
+class ClangdServer;
+
+/// Handles running WorkerRequests of ClangdServer on a separate threads.
+/// Currently runs only one worker thread.
+class ClangdScheduler {
+public:
+  ClangdScheduler(bool RunSynchronously);
+  ~ClangdScheduler();
+
+  /// Add \p Request to the start of the queue. \p Request will be run on a
+  /// separate worker thread.
+  /// \p Request is scheduled to be executed before all currently added
+  /// requests.
+  void addToFront(std::function<void()> Request);
+  /// Add \p Request to the end of the queue. \p Request will be run on a
+  /// separate worker thread.
+  /// \p Request is scheduled to be executed after all currently added
+  /// requests.
+  void addToEnd(std::function<void()> Request);
+
+private:
+  bool RunSynchronously;
+  std::mutex Mutex;
+  /// We run some tasks on a separate thread(parsing, ClangdUnit cleanup).
+  /// This thread looks into RequestQueue to find requests to handle and
+  /// terminates when Done is set to true.
+  std::thread Worker;
+  /// Setting Done to true will make the worker thread terminate.
+  bool Done = false;
+  /// A queue of requests.
+  /// FIXME(krasimir): code completion should always have priority over parsing
+  /// for diagnostics.
+  std::deque<std::function<void()>> RequestQueue;
+  /// Condition variable to wake up the worker thread.
+  std::condition_variable RequestCV;
+};
+
+/// Provides API to manage ASTs for a collection of C++ files and request
+/// various language features(currently, only codeCompletion and async
+/// diagnostics for tracked files).
+class ClangdServer {
+public:
+  /// Creates a new ClangdServer. If \p RunSynchronously is false, no worker
+  /// thread will be created and all requests will be completed synchronously on
+  /// the calling thread (this is mostly used for tests). If \p RunSynchronously
+  /// is true, a worker thread will be created to parse files in the background
+  /// and provide diagnostics results via DiagConsumer.onDiagnosticsReady
+  /// callback. File accesses for each instance of parsing will be conducted via
+  /// a vfs::FileSystem provided by \p FSProvider. Results of code
+  /// completion/diagnostics also include a tag, that \p FSProvider returns
+  /// along with the vfs::FileSystem.
+  /// When \p ResourceDir is set, it will be used to search for internal headers
+  /// (overriding defaults and -resource-dir compiler flag, if set). If \p
+  /// ResourceDir is None, ClangdServer will attempt to set it to a standard
+  /// location, obtained via CompilerInvocation::GetResourcePath.
+  ClangdServer(GlobalCompilationDatabase &CDB,
+               DiagnosticsConsumer &DiagConsumer,
+               FileSystemProvider &FSProvider, bool RunSynchronously,
+               llvm::Optional<StringRef> ResourceDir = llvm::None);
+
+  /// Add a \p File to the list of tracked C++ files or update the contents if
+  /// \p File is already tracked. Also schedules parsing of the AST for it on a
+  /// separate thread. When the parsing is complete, DiagConsumer passed in
+  /// constructor will receive onDiagnosticsReady callback.
+  void addDocument(PathRef File, StringRef Contents);
+  /// Remove \p File from list of tracked files, schedule a request to free
+  /// resources associated with it.
+  void removeDocument(PathRef File);
+  /// Force \p File to be reparsed using the latest contents.
+  void forceReparse(PathRef File);
+
+  /// Run code completion for \p File at \p Pos. If \p OverridenContents is not
+  /// None, they will used only for code completion, i.e. no diagnostics update
+  /// will be scheduled and a draft for \p File will not be updated.
+  /// If \p OverridenContents is None, contents of the current draft for \p File
+  /// will be used.
+  /// This method should only be called for currently tracked files.
+  Tagged<std::vector<CompletionItem>>
+  codeComplete(PathRef File, Position Pos,
+               llvm::Optional<StringRef> OverridenContents = llvm::None);
+  /// Get definition of symbol at a specified \p Line and \p Column in \p File.
+  Tagged<std::vector<Location>> findDefinitions(PathRef File, Position Pos);
+
+  /// Run formatting for \p Rng inside \p File.
+  std::vector<tooling::Replacement> formatRange(PathRef File, Range Rng);
+  /// Run formatting for the whole \p File.
+  std::vector<tooling::Replacement> formatFile(PathRef File);
+  /// Run formatting after a character was typed at \p Pos in \p File.
+  std::vector<tooling::Replacement> formatOnType(PathRef File, Position Pos);
+
+  /// Gets current document contents for \p File. \p File must point to a
+  /// currently tracked file.
+  /// FIXME(ibiryukov): This function is here to allow offset-to-Position
+  /// conversions in outside code, maybe there's a way to get rid of it.
+  std::string getDocument(PathRef File);
+
+  /// Only for testing purposes.
+  /// Waits until all requests to worker thread are finished and dumps AST for
+  /// \p File. \p File must be in the list of added documents.
+  std::string dumpAST(PathRef File);
+
+private:
+  GlobalCompilationDatabase &CDB;
+  DiagnosticsConsumer &DiagConsumer;
+  FileSystemProvider &FSProvider;
+  DraftStore DraftMgr;
+  ClangdUnitStore Units;
+  std::string ResourceDir;
+  std::shared_ptr<PCHContainerOperations> PCHs;
+  // WorkScheduler has to be the last member, because its destructor has to be
+  // called before all other members to stop the worker thread that references
+  // ClangdServer
+  ClangdScheduler WorkScheduler;
+};
+
+} // namespace clangd
+} // namespace clang
+
+#endif
diff --git a/clangd/ClangdUnit.cpp b/clangd/ClangdUnit.cpp
new file mode 100644 (file)
index 0000000..9f02187
--- /dev/null
@@ -0,0 +1,415 @@
+//===--- ClangdUnit.cpp -----------------------------------------*- C++-*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===---------------------------------------------------------------------===//
+
+#include "ClangdUnit.h"
+
+#include "clang/Frontend/ASTUnit.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/CompilerInvocation.h"
+#include "clang/Frontend/Utils.h"
+#include "clang/Index/IndexingAction.h"
+#include "clang/Index/IndexDataConsumer.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Lex/MacroInfo.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Tooling/CompilationDatabase.h"
+#include "llvm/Support/Format.h"
+
+#include <algorithm>
+
+using namespace clang::clangd;
+using namespace clang;
+
+ClangdUnit::ClangdUnit(PathRef FileName, StringRef Contents,
+                       StringRef ResourceDir,
+                       std::shared_ptr<PCHContainerOperations> PCHs,
+                       std::vector<tooling::CompileCommand> Commands,
+                       IntrusiveRefCntPtr<vfs::FileSystem> VFS)
+    : FileName(FileName), PCHs(PCHs) {
+  assert(!Commands.empty() && "No compile commands provided");
+
+  // Inject the resource dir.
+  // FIXME: Don't overwrite it if it's already there.
+  Commands.front().CommandLine.push_back("-resource-dir=" +
+                                         std::string(ResourceDir));
+
+  IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
+      CompilerInstance::createDiagnostics(new DiagnosticOptions);
+
+  std::vector<const char *> ArgStrs;
+  for (const auto &S : Commands.front().CommandLine)
+    ArgStrs.push_back(S.c_str());
+
+  ASTUnit::RemappedFile RemappedSource(
+      FileName,
+      llvm::MemoryBuffer::getMemBufferCopy(Contents, FileName).release());
+
+  auto ArgP = &*ArgStrs.begin();
+  Unit = std::unique_ptr<ASTUnit>(ASTUnit::LoadFromCommandLine(
+      ArgP, ArgP + ArgStrs.size(), PCHs, Diags, ResourceDir,
+      /*OnlyLocalDecls=*/false, /*CaptureDiagnostics=*/true, RemappedSource,
+      /*RemappedFilesKeepOriginalName=*/true,
+      /*PrecompilePreambleAfterNParses=*/1, /*TUKind=*/TU_Prefix,
+      /*CacheCodeCompletionResults=*/true,
+      /*IncludeBriefCommentsInCodeCompletion=*/true,
+      /*AllowPCHWithCompilerErrors=*/true,
+      /*SkipFunctionBodies=*/false,
+      /*SingleFileParse=*/false,
+      /*UserFilesAreVolatile=*/false, /*ForSerialization=*/false,
+      /*ModuleFormat=*/llvm::None,
+      /*ErrAST=*/nullptr, VFS));
+  assert(Unit && "Unit wasn't created");
+}
+
+void ClangdUnit::reparse(StringRef Contents,
+                         IntrusiveRefCntPtr<vfs::FileSystem> VFS) {
+  // Do a reparse if this wasn't the first parse.
+  // FIXME: This might have the wrong working directory if it changed in the
+  // meantime.
+  ASTUnit::RemappedFile RemappedSource(
+      FileName,
+      llvm::MemoryBuffer::getMemBufferCopy(Contents, FileName).release());
+
+  Unit->Reparse(PCHs, RemappedSource, VFS);
+}
+
+namespace {
+
+CompletionItemKind getKind(CXCursorKind K) {
+  switch (K) {
+  case CXCursor_MacroInstantiation:
+  case CXCursor_MacroDefinition:
+    return CompletionItemKind::Text;
+  case CXCursor_CXXMethod:
+    return CompletionItemKind::Method;
+  case CXCursor_FunctionDecl:
+  case CXCursor_FunctionTemplate:
+    return CompletionItemKind::Function;
+  case CXCursor_Constructor:
+  case CXCursor_Destructor:
+    return CompletionItemKind::Constructor;
+  case CXCursor_FieldDecl:
+    return CompletionItemKind::Field;
+  case CXCursor_VarDecl:
+  case CXCursor_ParmDecl:
+    return CompletionItemKind::Variable;
+  case CXCursor_ClassDecl:
+  case CXCursor_StructDecl:
+  case CXCursor_UnionDecl:
+  case CXCursor_ClassTemplate:
+  case CXCursor_ClassTemplatePartialSpecialization:
+    return CompletionItemKind::Class;
+  case CXCursor_Namespace:
+  case CXCursor_NamespaceAlias:
+  case CXCursor_NamespaceRef:
+    return CompletionItemKind::Module;
+  case CXCursor_EnumConstantDecl:
+    return CompletionItemKind::Value;
+  case CXCursor_EnumDecl:
+    return CompletionItemKind::Enum;
+  case CXCursor_TypeAliasDecl:
+  case CXCursor_TypeAliasTemplateDecl:
+  case CXCursor_TypedefDecl:
+  case CXCursor_MemberRef:
+  case CXCursor_TypeRef:
+    return CompletionItemKind::Reference;
+  default:
+    return CompletionItemKind::Missing;
+  }
+}
+
+class CompletionItemsCollector : public CodeCompleteConsumer {
+  std::vector<CompletionItem> *Items;
+  std::shared_ptr<clang::GlobalCodeCompletionAllocator> Allocator;
+  CodeCompletionTUInfo CCTUInfo;
+
+public:
+  CompletionItemsCollector(std::vector<CompletionItem> *Items,
+                           const CodeCompleteOptions &CodeCompleteOpts)
+      : CodeCompleteConsumer(CodeCompleteOpts, /*OutputIsBinary=*/false),
+        Items(Items),
+        Allocator(std::make_shared<clang::GlobalCodeCompletionAllocator>()),
+        CCTUInfo(Allocator) {}
+
+  void ProcessCodeCompleteResults(Sema &S, CodeCompletionContext Context,
+                                  CodeCompletionResult *Results,
+                                  unsigned NumResults) override {
+    for (unsigned I = 0; I != NumResults; ++I) {
+      CodeCompletionResult &Result = Results[I];
+      CodeCompletionString *CCS = Result.CreateCodeCompletionString(
+          S, Context, *Allocator, CCTUInfo,
+          CodeCompleteOpts.IncludeBriefComments);
+      if (CCS) {
+        CompletionItem Item;
+        for (CodeCompletionString::Chunk C : *CCS) {
+          switch (C.Kind) {
+          case CodeCompletionString::CK_ResultType:
+            Item.detail = C.Text;
+            break;
+          case CodeCompletionString::CK_Optional:
+            break;
+          default:
+            Item.label += C.Text;
+            break;
+          }
+        }
+        assert(CCS->getTypedText());
+        Item.kind = getKind(Result.CursorKind);
+        // Priority is a 16-bit integer, hence at most 5 digits.
+        // Since identifiers with higher priority need to come first,
+        // we subtract the priority from 99999.
+        // For example, the sort text of the identifier 'a' with priority 35
+        // is 99964a.
+        assert(CCS->getPriority() < 99999 && "Expecting code completion result "
+                                             "priority to have at most "
+                                             "5-digits");
+        llvm::raw_string_ostream(Item.sortText) << llvm::format(
+            "%05d%s", 99999 - CCS->getPriority(), CCS->getTypedText());
+        Item.insertText = Item.filterText = CCS->getTypedText();
+        if (CCS->getBriefComment())
+          Item.documentation = CCS->getBriefComment();
+        Items->push_back(std::move(Item));
+      }
+    }
+  }
+
+  GlobalCodeCompletionAllocator &getAllocator() override { return *Allocator; }
+
+  CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo; }
+};
+} // namespace
+
+std::vector<CompletionItem>
+ClangdUnit::codeComplete(StringRef Contents, Position Pos,
+                         IntrusiveRefCntPtr<vfs::FileSystem> VFS) {
+  CodeCompleteOptions CCO;
+  CCO.IncludeBriefComments = 1;
+  // This is where code completion stores dirty buffers. Need to free after
+  // completion.
+  SmallVector<const llvm::MemoryBuffer *, 4> OwnedBuffers;
+  SmallVector<StoredDiagnostic, 4> StoredDiagnostics;
+  IntrusiveRefCntPtr<DiagnosticsEngine> DiagEngine(
+      new DiagnosticsEngine(new DiagnosticIDs, new DiagnosticOptions));
+  std::vector<CompletionItem> Items;
+  CompletionItemsCollector Collector(&Items, CCO);
+
+  ASTUnit::RemappedFile RemappedSource(
+      FileName,
+      llvm::MemoryBuffer::getMemBufferCopy(Contents, FileName).release());
+
+  IntrusiveRefCntPtr<FileManager> FileMgr(
+      new FileManager(Unit->getFileSystemOpts(), VFS));
+  IntrusiveRefCntPtr<SourceManager> SourceMgr(
+      new SourceManager(*DiagEngine, *FileMgr));
+  // CodeComplete seems to require fresh LangOptions.
+  LangOptions LangOpts = Unit->getLangOpts();
+  // The language server protocol uses zero-based line and column numbers.
+  // The clang code completion uses one-based numbers.
+  Unit->CodeComplete(FileName, Pos.line + 1, Pos.character + 1, RemappedSource,
+                     CCO.IncludeMacros, CCO.IncludeCodePatterns,
+                     CCO.IncludeBriefComments, Collector, PCHs, *DiagEngine,
+                     LangOpts, *SourceMgr, *FileMgr, StoredDiagnostics,
+                     OwnedBuffers);
+  for (const llvm::MemoryBuffer *Buffer : OwnedBuffers)
+    delete Buffer;
+  return Items;
+}
+
+namespace {
+/// Convert from clang diagnostic level to LSP severity.
+static int getSeverity(DiagnosticsEngine::Level L) {
+  switch (L) {
+  case DiagnosticsEngine::Remark:
+    return 4;
+  case DiagnosticsEngine::Note:
+    return 3;
+  case DiagnosticsEngine::Warning:
+    return 2;
+  case DiagnosticsEngine::Fatal:
+  case DiagnosticsEngine::Error:
+    return 1;
+  case DiagnosticsEngine::Ignored:
+    return 0;
+  }
+  llvm_unreachable("Unknown diagnostic level!");
+}
+} // namespace
+
+std::vector<DiagWithFixIts> ClangdUnit::getLocalDiagnostics() const {
+  std::vector<DiagWithFixIts> Result;
+  for (ASTUnit::stored_diag_iterator D = Unit->stored_diag_begin(),
+                                     DEnd = Unit->stored_diag_end();
+       D != DEnd; ++D) {
+    if (!D->getLocation().isValid() ||
+        !D->getLocation().getManager().isInMainFile(D->getLocation()))
+      continue;
+    Position P;
+    P.line = D->getLocation().getSpellingLineNumber() - 1;
+    P.character = D->getLocation().getSpellingColumnNumber();
+    Range R = {P, P};
+    clangd::Diagnostic Diag = {R, getSeverity(D->getLevel()), D->getMessage()};
+
+    llvm::SmallVector<tooling::Replacement, 1> FixItsForDiagnostic;
+    for (const FixItHint &Fix : D->getFixIts()) {
+      FixItsForDiagnostic.push_back(clang::tooling::Replacement(
+          Unit->getSourceManager(), Fix.RemoveRange, Fix.CodeToInsert));
+    }
+    Result.push_back({Diag, std::move(FixItsForDiagnostic)});
+  }
+  return Result;
+}
+
+void ClangdUnit::dumpAST(llvm::raw_ostream &OS) const {
+  Unit->getASTContext().getTranslationUnitDecl()->dump(OS, true);
+}
+
+namespace {
+/// Finds declarations locations that a given source location refers to.
+class DeclarationLocationsFinder : public index::IndexDataConsumer {
+  std::vector<Location> DeclarationLocations;
+  const SourceLocation &SearchedLocation;
+  ASTUnit &Unit;
+public:
+  DeclarationLocationsFinder(raw_ostream &OS,
+      const SourceLocation &SearchedLocation, ASTUnit &Unit) :
+      SearchedLocation(SearchedLocation), Unit(Unit) {}
+
+  std::vector<Location> takeLocations() {
+    // Don't keep the same location multiple times.
+    // This can happen when nodes in the AST are visited twice.
+    std::sort(DeclarationLocations.begin(), DeclarationLocations.end());
+    auto last =
+        std::unique(DeclarationLocations.begin(), DeclarationLocations.end());
+    DeclarationLocations.erase(last, DeclarationLocations.end());
+    return std::move(DeclarationLocations);
+  }
+
+  bool handleDeclOccurence(const Decl* D, index::SymbolRoleSet Roles,
+      ArrayRef<index::SymbolRelation> Relations, FileID FID, unsigned Offset,
+      index::IndexDataConsumer::ASTNodeInfo ASTNode) override
+      {
+    if (isSearchedLocation(FID, Offset)) {
+      addDeclarationLocation(D->getSourceRange());
+    }
+    return true;
+  }
+
+private:
+  bool isSearchedLocation(FileID FID, unsigned Offset) const {
+    const SourceManager &SourceMgr = Unit.getSourceManager();
+    return SourceMgr.getFileOffset(SearchedLocation) == Offset
+        && SourceMgr.getFileID(SearchedLocation) == FID;
+  }
+
+  void addDeclarationLocation(const SourceRange& ValSourceRange) {
+    const SourceManager& SourceMgr = Unit.getSourceManager();
+    const LangOptions& LangOpts = Unit.getLangOpts();
+    SourceLocation LocStart = ValSourceRange.getBegin();
+    SourceLocation LocEnd = Lexer::getLocForEndOfToken(ValSourceRange.getEnd(),
+        0, SourceMgr, LangOpts);
+    Position Begin;
+    Begin.line = SourceMgr.getSpellingLineNumber(LocStart) - 1;
+    Begin.character = SourceMgr.getSpellingColumnNumber(LocStart) - 1;
+    Position End;
+    End.line = SourceMgr.getSpellingLineNumber(LocEnd) - 1;
+    End.character = SourceMgr.getSpellingColumnNumber(LocEnd) - 1;
+    Range R = {Begin, End};
+    Location L;
+    L.uri = URI::fromFile(
+        SourceMgr.getFilename(SourceMgr.getSpellingLoc(LocStart)));
+    L.range = R;
+    DeclarationLocations.push_back(L);
+  }
+
+  void finish() override {
+    // Also handle possible macro at the searched location.
+    Token Result;
+    if (!Lexer::getRawToken(SearchedLocation, Result, Unit.getSourceManager(),
+        Unit.getASTContext().getLangOpts(), false)) {
+      if (Result.is(tok::raw_identifier)) {
+        Unit.getPreprocessor().LookUpIdentifierInfo(Result);
+      }
+      IdentifierInfo* IdentifierInfo = Result.getIdentifierInfo();
+      if (IdentifierInfo && IdentifierInfo->hadMacroDefinition()) {
+        std::pair<FileID, unsigned int> DecLoc =
+            Unit.getSourceManager().getDecomposedExpansionLoc(SearchedLocation);
+        // Get the definition just before the searched location so that a macro
+        // referenced in a '#undef MACRO' can still be found.
+        SourceLocation BeforeSearchedLocation = Unit.getLocation(
+            Unit.getSourceManager().getFileEntryForID(DecLoc.first),
+            DecLoc.second - 1);
+        MacroDefinition MacroDef =
+            Unit.getPreprocessor().getMacroDefinitionAtLoc(IdentifierInfo,
+                BeforeSearchedLocation);
+        MacroInfo* MacroInf = MacroDef.getMacroInfo();
+        if (MacroInf) {
+          addDeclarationLocation(
+              SourceRange(MacroInf->getDefinitionLoc(),
+                  MacroInf->getDefinitionEndLoc()));
+        }
+      }
+    }
+  }
+};
+} // namespace
+
+std::vector<Location> ClangdUnit::findDefinitions(Position Pos) {
+  const FileEntry *FE = Unit->getFileManager().getFile(Unit->getMainFileName());
+  if (!FE)
+    return {};
+
+  SourceLocation SourceLocationBeg = getBeginningOfIdentifier(Pos, FE);
+
+  auto DeclLocationsFinder = std::make_shared<DeclarationLocationsFinder>(
+      llvm::errs(), SourceLocationBeg, *Unit);
+  index::IndexingOptions IndexOpts;
+  IndexOpts.SystemSymbolFilter =
+      index::IndexingOptions::SystemSymbolFilterKind::All;
+  IndexOpts.IndexFunctionLocals = true;
+  index::indexASTUnit(*Unit, DeclLocationsFinder, IndexOpts);
+
+  return DeclLocationsFinder->takeLocations();
+}
+
+SourceLocation ClangdUnit::getBeginningOfIdentifier(const Position &Pos,
+    const FileEntry *FE) const {
+  // The language server protocol uses zero-based line and column numbers.
+  // Clang uses one-based numbers.
+  SourceLocation InputLocation = Unit->getLocation(FE, Pos.line + 1,
+      Pos.character + 1);
+
+  if (Pos.character == 0) {
+    return InputLocation;
+  }
+
+  // This handle cases where the position is in the middle of a token or right
+  // after the end of a token. In theory we could just use GetBeginningOfToken
+  // to find the start of the token at the input position, but this doesn't
+  // work when right after the end, i.e. foo|.
+  // So try to go back by one and see if we're still inside the an identifier
+  // token. If so, Take the beginning of this token.
+  // (It should be the same identifier because you can't have two adjacent
+  // identifiers without another token in between.)
+  SourceLocation PeekBeforeLocation = Unit->getLocation(FE, Pos.line + 1,
+      Pos.character);
+  const SourceManager &SourceMgr = Unit->getSourceManager();
+  Token Result;
+  if (Lexer::getRawToken(PeekBeforeLocation, Result, SourceMgr,
+                         Unit->getASTContext().getLangOpts(), false)) {
+    // getRawToken failed, just use InputLocation.
+    return InputLocation;
+  }
+
+  if (Result.is(tok::raw_identifier)) {
+    return Lexer::GetBeginningOfToken(PeekBeforeLocation, SourceMgr,
+        Unit->getASTContext().getLangOpts());
+  }
+
+  return InputLocation;
+}
diff --git a/clangd/ClangdUnit.h b/clangd/ClangdUnit.h
new file mode 100644 (file)
index 0000000..81d1ba8
--- /dev/null
@@ -0,0 +1,82 @@
+//===--- ClangdUnit.h -------------------------------------------*- C++-*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===---------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDUNIT_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDUNIT_H
+
+#include "Path.h"
+#include "Protocol.h"
+#include "clang/Frontend/ASTUnit.h"
+#include "clang/Tooling/Core/Replacement.h"
+#include <memory>
+
+namespace llvm {
+class raw_ostream;
+}
+
+namespace clang {
+class ASTUnit;
+class PCHContainerOperations;
+
+namespace vfs {
+class FileSystem;
+}
+
+namespace tooling {
+struct CompileCommand;
+}
+
+namespace clangd {
+
+/// A diagnostic with its FixIts.
+struct DiagWithFixIts {
+  clangd::Diagnostic Diag;
+  llvm::SmallVector<tooling::Replacement, 1> FixIts;
+};
+
+/// Stores parsed C++ AST and provides implementations of all operations clangd
+/// would want to perform on parsed C++ files.
+class ClangdUnit {
+public:
+  ClangdUnit(PathRef FileName, StringRef Contents, StringRef ResourceDir,
+             std::shared_ptr<PCHContainerOperations> PCHs,
+             std::vector<tooling::CompileCommand> Commands,
+             IntrusiveRefCntPtr<vfs::FileSystem> VFS);
+
+  /// Reparse with new contents.
+  void reparse(StringRef Contents, IntrusiveRefCntPtr<vfs::FileSystem> VFS);
+
+  /// Get code completions at a specified \p Line and \p Column in \p File.
+  ///
+  /// This function is thread-safe and returns completion items that own the
+  /// data they contain.
+  std::vector<CompletionItem>
+  codeComplete(StringRef Contents, Position Pos,
+               IntrusiveRefCntPtr<vfs::FileSystem> VFS);
+  /// Get definition of symbol at a specified \p Line and \p Column in \p File.
+  std::vector<Location> findDefinitions(Position Pos);
+  /// Returns diagnostics and corresponding FixIts for each diagnostic that are
+  /// located in the current file.
+  std::vector<DiagWithFixIts> getLocalDiagnostics() const;
+
+  /// For testing/debugging purposes. Note that this method deserializes all
+  /// unserialized Decls, so use with care.
+  void dumpAST(llvm::raw_ostream &OS) const;
+
+private:
+  Path FileName;
+  std::unique_ptr<ASTUnit> Unit;
+  std::shared_ptr<PCHContainerOperations> PCHs;
+
+  SourceLocation getBeginningOfIdentifier(const Position& Pos, const FileEntry* FE) const;
+};
+
+} // namespace clangd
+} // namespace clang
+#endif
diff --git a/clangd/ClangdUnitStore.cpp b/clangd/ClangdUnitStore.cpp
new file mode 100644 (file)
index 0000000..c892f99
--- /dev/null
@@ -0,0 +1,33 @@
+//===--- ClangdUnitStore.cpp - A ClangdUnits container -----------*-C++-*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ClangdUnitStore.h"
+#include "llvm/Support/Path.h"
+
+using namespace clang::clangd;
+using namespace clang;
+
+void ClangdUnitStore::removeUnitIfPresent(PathRef File) {
+  std::lock_guard<std::mutex> Lock(Mutex);
+
+  auto It = OpenedFiles.find(File);
+  if (It == OpenedFiles.end())
+    return;
+  OpenedFiles.erase(It);
+}
+
+std::vector<tooling::CompileCommand>
+ClangdUnitStore::getCompileCommands(GlobalCompilationDatabase &CDB,
+                                    PathRef File) {
+  std::vector<tooling::CompileCommand> Commands = CDB.getCompileCommands(File);
+  if (Commands.empty())
+    // Add a fake command line if we know nothing.
+    Commands.push_back(getDefaultCompileCommand(File));
+  return Commands;
+}
diff --git a/clangd/ClangdUnitStore.h b/clangd/ClangdUnitStore.h
new file mode 100644 (file)
index 0000000..c980ba7
--- /dev/null
@@ -0,0 +1,109 @@
+//===--- ClangdUnitStore.h - A ClangdUnits container -------------*-C++-*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===---------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDUNITSTORE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDUNITSTORE_H
+
+#include <mutex>
+
+#include "ClangdUnit.h"
+#include "GlobalCompilationDatabase.h"
+#include "Path.h"
+#include "clang/Tooling/CompilationDatabase.h"
+
+namespace clang {
+namespace clangd {
+
+/// Thread-safe collection of ASTs built for specific files. Provides
+/// synchronized access to ASTs.
+class ClangdUnitStore {
+public:
+  /// Run specified \p Action on the ClangdUnit for \p File.
+  /// If the file is not present in ClangdUnitStore, a new ClangdUnit will be
+  /// created from the \p FileContents. If the file is already present in the
+  /// store, ClangdUnit::reparse will be called with the new contents before
+  /// running \p Action.
+  template <class Func>
+  void runOnUnit(PathRef File, StringRef FileContents, StringRef ResourceDir,
+                 GlobalCompilationDatabase &CDB,
+                 std::shared_ptr<PCHContainerOperations> PCHs,
+                 IntrusiveRefCntPtr<vfs::FileSystem> VFS, Func Action) {
+    runOnUnitImpl(File, FileContents, ResourceDir, CDB, PCHs,
+                  /*ReparseBeforeAction=*/true, VFS,
+                  std::forward<Func>(Action));
+  }
+
+  /// Run specified \p Action on the ClangdUnit for \p File.
+  /// If the file is not present in ClangdUnitStore, a new ClangdUnit will be
+  /// created from the \p FileContents. If the file is already present in the
+  /// store, the \p Action will be run directly on it.
+  template <class Func>
+  void runOnUnitWithoutReparse(PathRef File, StringRef FileContents,
+                               StringRef ResourceDir,
+                               GlobalCompilationDatabase &CDB,
+                               std::shared_ptr<PCHContainerOperations> PCHs,
+                               IntrusiveRefCntPtr<vfs::FileSystem> VFS,
+                               Func Action) {
+    runOnUnitImpl(File, FileContents, ResourceDir, CDB, PCHs,
+                  /*ReparseBeforeAction=*/false, VFS,
+                  std::forward<Func>(Action));
+  }
+
+  /// Run the specified \p Action on the ClangdUnit for \p File.
+  /// Unit for \p File should exist in the store.
+  template <class Func> void runOnExistingUnit(PathRef File, Func Action) {
+    std::lock_guard<std::mutex> Lock(Mutex);
+
+    auto It = OpenedFiles.find(File);
+    assert(It != OpenedFiles.end() && "File is not in OpenedFiles");
+
+    Action(It->second);
+  }
+
+  /// Remove ClangdUnit for \p File, if any
+  void removeUnitIfPresent(PathRef File);
+
+private:
+  /// Run specified \p Action on the ClangdUnit for \p File.
+  template <class Func>
+  void runOnUnitImpl(PathRef File, StringRef FileContents,
+                     StringRef ResourceDir, GlobalCompilationDatabase &CDB,
+                     std::shared_ptr<PCHContainerOperations> PCHs,
+                     bool ReparseBeforeAction,
+                     IntrusiveRefCntPtr<vfs::FileSystem> VFS, Func Action) {
+    std::lock_guard<std::mutex> Lock(Mutex);
+
+    auto Commands = getCompileCommands(CDB, File);
+    assert(!Commands.empty() &&
+           "getCompileCommands should add default command");
+    VFS->setCurrentWorkingDirectory(Commands.front().Directory);
+
+    auto It = OpenedFiles.find(File);
+    if (It == OpenedFiles.end()) {
+      It = OpenedFiles
+               .insert(std::make_pair(File, ClangdUnit(File, FileContents,
+                                                       ResourceDir, PCHs,
+                                                       Commands, VFS)))
+               .first;
+    } else if (ReparseBeforeAction) {
+      It->second.reparse(FileContents, VFS);
+    }
+    return Action(It->second);
+  }
+
+  std::vector<tooling::CompileCommand>
+  getCompileCommands(GlobalCompilationDatabase &CDB, PathRef File);
+
+  std::mutex Mutex;
+  llvm::StringMap<ClangdUnit> OpenedFiles;
+};
+} // namespace clangd
+} // namespace clang
+
+#endif
diff --git a/clangd/DraftStore.cpp b/clangd/DraftStore.cpp
new file mode 100644 (file)
index 0000000..f5ceff2
--- /dev/null
@@ -0,0 +1,49 @@
+//===--- DraftStore.cpp - File contents container ---------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "DraftStore.h"
+
+using namespace clang;
+using namespace clang::clangd;
+
+VersionedDraft DraftStore::getDraft(PathRef File) const {
+  std::lock_guard<std::mutex> Lock(Mutex);
+
+  auto It = Drafts.find(File);
+  if (It == Drafts.end())
+    return {0, llvm::None};
+  return It->second;
+}
+
+DocVersion DraftStore::getVersion(PathRef File) const {
+  std::lock_guard<std::mutex> Lock(Mutex);
+
+  auto It = Drafts.find(File);
+  if (It == Drafts.end())
+    return 0;
+  return It->second.Version;
+}
+
+DocVersion DraftStore::updateDraft(PathRef File, StringRef Contents) {
+  std::lock_guard<std::mutex> Lock(Mutex);
+
+  auto &Entry = Drafts[File];
+  DocVersion NewVersion = ++Entry.Version;
+  Entry.Draft = Contents;
+  return NewVersion;
+}
+
+DocVersion DraftStore::removeDraft(PathRef File) {
+  std::lock_guard<std::mutex> Lock(Mutex);
+
+  auto &Entry = Drafts[File];
+  DocVersion NewVersion = ++Entry.Version;
+  Entry.Draft = llvm::None;
+  return NewVersion;
+}
diff --git a/clangd/DraftStore.h b/clangd/DraftStore.h
new file mode 100644 (file)
index 0000000..c4e31e7
--- /dev/null
@@ -0,0 +1,61 @@
+//===--- DraftStore.h - File contents container -----------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_DRAFTSTORE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_DRAFTSTORE_H
+
+#include "Path.h"
+#include "clang/Basic/LLVM.h"
+#include "llvm/ADT/StringMap.h"
+#include <mutex>
+#include <string>
+#include <vector>
+
+namespace clang {
+namespace clangd {
+
+/// Using 'unsigned' here to avoid undefined behaviour on overflow.
+typedef unsigned DocVersion;
+
+/// Document draft with a version of this draft.
+struct VersionedDraft {
+  DocVersion Version;
+  /// If the value of the field is None, draft is now deleted
+  llvm::Optional<std::string> Draft;
+};
+
+/// A thread-safe container for files opened in a workspace, addressed by
+/// filenames. The contents are owned by the DraftStore. Versions are mantained
+/// for the all added documents, including removed ones. The document version is
+/// incremented on each update and removal of the document.
+class DraftStore {
+public:
+  /// \return version and contents of the stored document.
+  /// For untracked files, a (0, None) pair is returned.
+  VersionedDraft getDraft(PathRef File) const;
+  /// \return version of the tracked document.
+  /// For untracked files, 0 is returned.
+  DocVersion getVersion(PathRef File) const;
+
+  /// Replace contents of the draft for \p File with \p Contents.
+  /// \return The new version of the draft for \p File.
+  DocVersion updateDraft(PathRef File, StringRef Contents);
+  /// Remove the contents of the draft
+  /// \return The new version of the draft for \p File.
+  DocVersion removeDraft(PathRef File);
+
+private:
+  mutable std::mutex Mutex;
+  llvm::StringMap<VersionedDraft> Drafts;
+};
+
+} // namespace clangd
+} // namespace clang
+
+#endif
diff --git a/clangd/GlobalCompilationDatabase.cpp b/clangd/GlobalCompilationDatabase.cpp
new file mode 100644 (file)
index 0000000..9cf8572
--- /dev/null
@@ -0,0 +1,104 @@
+//===--- GlobalCompilationDatabase.cpp --------------------------*- C++-*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===---------------------------------------------------------------------===//
+
+#include "GlobalCompilationDatabase.h"
+#include "clang/Tooling/CompilationDatabase.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+
+namespace clang {
+namespace clangd {
+
+static void addExtraFlags(tooling::CompileCommand &Command,
+                          const std::vector<std::string> &ExtraFlags) {
+  if (ExtraFlags.empty())
+    return;
+  assert(Command.CommandLine.size() >= 2 &&
+         "Expected a command line containing at least 2 arguments, the "
+         "compiler binary and the output file");
+  // The last argument of CommandLine is the name of the input file.
+  // Add ExtraFlags before it.
+  auto It = Command.CommandLine.end();
+  --It;
+  Command.CommandLine.insert(It, ExtraFlags.begin(), ExtraFlags.end());
+}
+
+tooling::CompileCommand getDefaultCompileCommand(PathRef File) {
+  std::vector<std::string> CommandLine{"clang", "-fsyntax-only", File.str()};
+  return tooling::CompileCommand(llvm::sys::path::parent_path(File),
+                                 llvm::sys::path::filename(File), CommandLine,
+                                 /*Output=*/"");
+}
+
+std::vector<tooling::CompileCommand>
+DirectoryBasedGlobalCompilationDatabase::getCompileCommands(PathRef File) {
+  std::vector<tooling::CompileCommand> Commands;
+
+  auto CDB = getCompilationDatabase(File);
+  if (CDB)
+    Commands = CDB->getCompileCommands(File);
+  if (Commands.empty())
+    Commands.push_back(getDefaultCompileCommand(File));
+
+  auto It = ExtraFlagsForFile.find(File);
+  if (It != ExtraFlagsForFile.end()) {
+    // Append the user-specified flags to the compile commands.
+    for (tooling::CompileCommand &Command : Commands)
+      addExtraFlags(Command, It->second);
+  }
+
+  return Commands;
+}
+
+void DirectoryBasedGlobalCompilationDatabase::setExtraFlagsForFile(
+    PathRef File, std::vector<std::string> ExtraFlags) {
+  ExtraFlagsForFile[File] = std::move(ExtraFlags);
+}
+
+tooling::CompilationDatabase *
+DirectoryBasedGlobalCompilationDatabase::getCompilationDatabase(PathRef File) {
+  std::lock_guard<std::mutex> Lock(Mutex);
+
+  namespace path = llvm::sys::path;
+
+  assert((path::is_absolute(File, path::Style::posix) ||
+          path::is_absolute(File, path::Style::windows)) &&
+         "path must be absolute");
+
+  for (auto Path = path::parent_path(File); !Path.empty();
+       Path = path::parent_path(Path)) {
+
+    auto CachedIt = CompilationDatabases.find(Path);
+    if (CachedIt != CompilationDatabases.end())
+      return CachedIt->second.get();
+    std::string Error;
+    auto CDB = tooling::CompilationDatabase::loadFromDirectory(Path, Error);
+    if (!CDB) {
+      if (!Error.empty()) {
+        // FIXME(ibiryukov): logging
+        // Output.log("Error when trying to load compilation database from " +
+        //            Twine(Path) + ": " + Twine(Error) + "\n");
+      }
+      continue;
+    }
+
+    // FIXME(ibiryukov): Invalidate cached compilation databases on changes
+    auto result = CDB.get();
+    CompilationDatabases.insert(std::make_pair(Path, std::move(CDB)));
+    return result;
+  }
+
+  // FIXME(ibiryukov): logging
+  // Output.log("Failed to find compilation database for " + Twine(File) +
+  // "\n");
+  return nullptr;
+}
+
+} // namespace clangd
+} // namespace clang
diff --git a/clangd/GlobalCompilationDatabase.h b/clangd/GlobalCompilationDatabase.h
new file mode 100644 (file)
index 0000000..36c130c
--- /dev/null
@@ -0,0 +1,68 @@
+//===--- GlobalCompilationDatabase.h ----------------------------*- C++-*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===---------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_GLOBALCOMPILATIONDATABASE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_GLOBALCOMPILATIONDATABASE_H
+
+#include "Path.h"
+#include "llvm/ADT/StringMap.h"
+#include <memory>
+#include <mutex>
+#include <vector>
+
+namespace clang {
+
+namespace tooling {
+class CompilationDatabase;
+struct CompileCommand;
+} // namespace tooling
+
+namespace clangd {
+
+/// Returns a default compile command to use for \p File.
+tooling::CompileCommand getDefaultCompileCommand(PathRef File);
+
+/// Provides compilation arguments used for building ClangdUnit.
+class GlobalCompilationDatabase {
+public:
+  virtual ~GlobalCompilationDatabase() = default;
+
+  virtual std::vector<tooling::CompileCommand>
+  getCompileCommands(PathRef File) = 0;
+
+  /// FIXME(ibiryukov): add facilities to track changes to compilation flags of
+  /// existing targets.
+};
+
+/// Gets compile args from tooling::CompilationDatabases built for parent
+/// directories.
+class DirectoryBasedGlobalCompilationDatabase
+    : public GlobalCompilationDatabase {
+public:
+  std::vector<tooling::CompileCommand>
+  getCompileCommands(PathRef File) override;
+
+  void setExtraFlagsForFile(PathRef File, std::vector<std::string> ExtraFlags);
+
+private:
+  tooling::CompilationDatabase *getCompilationDatabase(PathRef File);
+
+  std::mutex Mutex;
+  /// Caches compilation databases loaded from directories(keys are
+  /// directories).
+  llvm::StringMap<std::unique_ptr<clang::tooling::CompilationDatabase>>
+      CompilationDatabases;
+
+  /// Stores extra flags per file.
+  llvm::StringMap<std::vector<std::string>> ExtraFlagsForFile;
+};
+} // namespace clangd
+} // namespace clang
+
+#endif
diff --git a/clangd/JSONRPCDispatcher.cpp b/clangd/JSONRPCDispatcher.cpp
new file mode 100644 (file)
index 0000000..5deb75a
--- /dev/null
@@ -0,0 +1,191 @@
+//===--- JSONRPCDispatcher.cpp - Main JSON parser entry point -------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "JSONRPCDispatcher.h"
+#include "ProtocolHandlers.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/YAMLParser.h"
+#include <istream>
+
+using namespace clang;
+using namespace clangd;
+
+void JSONOutput::writeMessage(const Twine &Message) {
+  llvm::SmallString<128> Storage;
+  StringRef M = Message.toStringRef(Storage);
+
+  std::lock_guard<std::mutex> Guard(StreamMutex);
+  // Log without headers.
+  Logs << "--> " << M << '\n';
+  Logs.flush();
+
+  // Emit message with header.
+  Outs << "Content-Length: " << M.size() << "\r\n\r\n" << M;
+  Outs.flush();
+}
+
+void JSONOutput::log(const Twine &Message) {
+  std::lock_guard<std::mutex> Guard(StreamMutex);
+  Logs << Message;
+  Logs.flush();
+}
+
+void Handler::handleMethod(llvm::yaml::MappingNode *Params, StringRef ID) {
+  Output.log("Method ignored.\n");
+  // Return that this method is unsupported.
+  writeMessage(
+      R"({"jsonrpc":"2.0","id":)" + ID +
+      R"(,"error":{"code":-32601}})");
+}
+
+void Handler::handleNotification(llvm::yaml::MappingNode *Params) {
+  Output.log("Notification ignored.\n");
+}
+
+void JSONRPCDispatcher::registerHandler(StringRef Method,
+                                        std::unique_ptr<Handler> H) {
+  assert(!Handlers.count(Method) && "Handler already registered!");
+  Handlers[Method] = std::move(H);
+}
+
+static void
+callHandler(const llvm::StringMap<std::unique_ptr<Handler>> &Handlers,
+            llvm::yaml::ScalarNode *Method, llvm::yaml::ScalarNode *Id,
+            llvm::yaml::MappingNode *Params, Handler *UnknownHandler) {
+  llvm::SmallString<10> MethodStorage;
+  auto I = Handlers.find(Method->getValue(MethodStorage));
+  auto *Handler = I != Handlers.end() ? I->second.get() : UnknownHandler;
+  if (Id)
+    Handler->handleMethod(Params, Id->getRawValue());
+  else
+    Handler->handleNotification(Params);
+}
+
+bool JSONRPCDispatcher::call(StringRef Content) const {
+  llvm::SourceMgr SM;
+  llvm::yaml::Stream YAMLStream(Content, SM);
+
+  auto Doc = YAMLStream.begin();
+  if (Doc == YAMLStream.end())
+    return false;
+
+  auto *Root = Doc->getRoot();
+  if (!Root)
+    return false;
+
+  auto *Object = dyn_cast<llvm::yaml::MappingNode>(Root);
+  if (!Object)
+    return false;
+
+  llvm::yaml::ScalarNode *Version = nullptr;
+  llvm::yaml::ScalarNode *Method = nullptr;
+  llvm::yaml::MappingNode *Params = nullptr;
+  llvm::yaml::ScalarNode *Id = nullptr;
+  for (auto &NextKeyValue : *Object) {
+    auto *KeyString = dyn_cast<llvm::yaml::ScalarNode>(NextKeyValue.getKey());
+    if (!KeyString)
+      return false;
+
+    llvm::SmallString<10> KeyStorage;
+    StringRef KeyValue = KeyString->getValue(KeyStorage);
+    llvm::yaml::Node *Value = NextKeyValue.getValue();
+    if (!Value)
+      return false;
+
+    if (KeyValue == "jsonrpc") {
+      // This should be "2.0". Always.
+      Version = dyn_cast<llvm::yaml::ScalarNode>(Value);
+      if (!Version || Version->getRawValue() != "\"2.0\"")
+        return false;
+    } else if (KeyValue == "method") {
+      Method = dyn_cast<llvm::yaml::ScalarNode>(Value);
+    } else if (KeyValue == "id") {
+      Id = dyn_cast<llvm::yaml::ScalarNode>(Value);
+    } else if (KeyValue == "params") {
+      if (!Method)
+        return false;
+      // We have to interleave the call of the function here, otherwise the
+      // YAMLParser will die because it can't go backwards. This is unfortunate
+      // because it will break clients that put the id after params. A possible
+      // fix would be to split the parsing and execution phases.
+      Params = dyn_cast<llvm::yaml::MappingNode>(Value);
+      callHandler(Handlers, Method, Id, Params, UnknownHandler.get());
+      return true;
+    } else {
+      return false;
+    }
+  }
+
+  // In case there was a request with no params, call the handler on the
+  // leftovers.
+  if (!Method)
+    return false;
+  callHandler(Handlers, Method, Id, nullptr, UnknownHandler.get());
+
+  return true;
+}
+
+void clangd::runLanguageServerLoop(std::istream &In, JSONOutput &Out,
+                                   JSONRPCDispatcher &Dispatcher,
+                                   bool &IsDone) {
+  while (In.good()) {
+    // A Language Server Protocol message starts with a HTTP header, delimited
+    // by \r\n.
+    std::string Line;
+    std::getline(In, Line);
+    if (!In.good() && errno == EINTR) {
+      In.clear();
+      continue;
+    }
+
+    // Skip empty lines.
+    llvm::StringRef LineRef(Line);
+    if (LineRef.trim().empty())
+      continue;
+
+    // We allow YAML-style comments. Technically this isn't part of the
+    // LSP specification, but makes writing tests easier.
+    if (LineRef.startswith("#"))
+      continue;
+
+    unsigned long long Len = 0;
+    // FIXME: Content-Type is a specified header, but does nothing.
+    // Content-Length is a mandatory header. It specifies the length of the
+    // following JSON.
+    if (LineRef.consume_front("Content-Length: "))
+      llvm::getAsUnsignedInteger(LineRef.trim(), 0, Len);
+
+    // Check if the next line only contains \r\n. If not this is another header,
+    // which we ignore.
+    char NewlineBuf[2];
+    In.read(NewlineBuf, 2);
+    if (std::memcmp(NewlineBuf, "\r\n", 2) != 0)
+      continue;
+
+    // Now read the JSON. Insert a trailing null byte as required by the YAML
+    // parser.
+    std::vector<char> JSON(Len + 1, '\0');
+    In.read(JSON.data(), Len);
+
+    if (Len > 0) {
+      llvm::StringRef JSONRef(JSON.data(), Len);
+      // Log the message.
+      Out.log("<-- " + JSONRef + "\n");
+
+      // Finally, execute the action for this JSON message.
+      if (!Dispatcher.call(JSONRef))
+        Out.log("JSON dispatch failed!\n");
+
+      // If we're done, exit the loop.
+      if (IsDone)
+        break;
+    }
+  }
+}
diff --git a/clangd/JSONRPCDispatcher.h b/clangd/JSONRPCDispatcher.h
new file mode 100644 (file)
index 0000000..349ac90
--- /dev/null
@@ -0,0 +1,95 @@
+//===--- JSONRPCDispatcher.h - Main JSON parser entry point -----*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_JSONRPCDISPATCHER_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_JSONRPCDISPATCHER_H
+
+#include "clang/Basic/LLVM.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/Support/YAMLParser.h"
+#include <iosfwd>
+#include <mutex>
+
+namespace clang {
+namespace clangd {
+
+/// Encapsulates output and logs streams and provides thread-safe access to
+/// them.
+class JSONOutput {
+public:
+  JSONOutput(llvm::raw_ostream &Outs, llvm::raw_ostream &Logs)
+      : Outs(Outs), Logs(Logs) {}
+
+  /// Emit a JSONRPC message.
+  void writeMessage(const Twine &Message);
+
+  /// Write to the logging stream.
+  void log(const Twine &Message);
+
+private:
+  llvm::raw_ostream &Outs;
+  llvm::raw_ostream &Logs;
+
+  std::mutex StreamMutex;
+};
+
+/// Callback for messages sent to the server, called by the JSONRPCDispatcher.
+class Handler {
+public:
+  Handler(JSONOutput &Output) : Output(Output) {}
+  virtual ~Handler() = default;
+
+  /// Called when the server receives a method call. This is supposed to return
+  /// a result on Outs. The default implementation returns an "unknown method"
+  /// error to the client and logs a warning.
+  virtual void handleMethod(llvm::yaml::MappingNode *Params, StringRef ID);
+  /// Called when the server receives a notification. No result should be
+  /// written to Outs. The default implemetation logs a warning.
+  virtual void handleNotification(llvm::yaml::MappingNode *Params);
+
+protected:
+  JSONOutput &Output;
+
+  /// Helper to write a JSONRPC result to Output.
+  void writeMessage(const Twine &Message) { Output.writeMessage(Message); }
+};
+
+/// Main JSONRPC entry point. This parses the JSONRPC "header" and calls the
+/// registered Handler for the method received.
+class JSONRPCDispatcher {
+public:
+  /// Create a new JSONRPCDispatcher. UnknownHandler is called when an unknown
+  /// method is received.
+  JSONRPCDispatcher(std::unique_ptr<Handler> UnknownHandler)
+      : UnknownHandler(std::move(UnknownHandler)) {}
+
+  /// Registers a Handler for the specified Method.
+  void registerHandler(StringRef Method, std::unique_ptr<Handler> H);
+
+  /// Parses a JSONRPC message and calls the Handler for it.
+  bool call(StringRef Content) const;
+
+private:
+  llvm::StringMap<std::unique_ptr<Handler>> Handlers;
+  std::unique_ptr<Handler> UnknownHandler;
+};
+
+/// Parses input queries from LSP client (coming from \p In) and runs call
+/// method of \p Dispatcher for each query.
+/// After handling each query checks if \p IsDone is set true and exits the loop
+/// if it is.
+/// Input stream(\p In) must be opened in binary mode to avoid preliminary
+/// replacements of \r\n with \n.
+void runLanguageServerLoop(std::istream &In, JSONOutput &Out,
+                           JSONRPCDispatcher &Dispatcher, bool &IsDone);
+
+} // namespace clangd
+} // namespace clang
+
+#endif
diff --git a/clangd/Path.h b/clangd/Path.h
new file mode 100644 (file)
index 0000000..b4c9335
--- /dev/null
@@ -0,0 +1,29 @@
+//===--- Path.h - Helper typedefs --------------------------------*- C++-*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_PATH_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_PATH_H
+
+#include "llvm/ADT/StringRef.h"
+#include <string>
+
+namespace clang {
+namespace clangd {
+
+/// A typedef to represent a file path. Used solely for more descriptive
+/// signatures.
+using Path = std::string;
+/// A typedef to represent a ref to file path. Used solely for more descriptive
+/// signatures.
+using PathRef = llvm::StringRef;
+
+} // namespace clangd
+} // namespace clang
+
+#endif
diff --git a/clangd/Protocol.cpp b/clangd/Protocol.cpp
new file mode 100644 (file)
index 0000000..c370e0d
--- /dev/null
@@ -0,0 +1,748 @@
+//===--- Protocol.cpp - Language Server Protocol Implementation -----------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains the serialization code for the LSP structs.
+// FIXME: This is extremely repetetive and ugly. Is there a better way?
+//
+//===----------------------------------------------------------------------===//
+
+#include "Protocol.h"
+#include "clang/Basic/LLVM.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Support/Path.h"
+using namespace clang::clangd;
+
+
+URI URI::fromUri(llvm::StringRef uri) {
+  URI Result;
+  Result.uri = uri;
+  uri.consume_front("file://");
+  // Also trim authority-less URIs
+  uri.consume_front("file:");
+  // For Windows paths e.g. /X:
+  if (uri.size() > 2 && uri[0] == '/' && uri[2] == ':')
+    uri.consume_front("/");
+  // Make sure that file paths are in native separators
+  Result.file = llvm::sys::path::convert_to_slash(uri);
+  return Result;
+}
+
+URI URI::fromFile(llvm::StringRef file) {
+  using namespace llvm::sys;
+  URI Result;
+  Result.file = file;
+  Result.uri = "file://";
+  // For Windows paths e.g. X:
+  if (file.size() > 1 && file[1] == ':')
+    Result.uri += "/";
+  // Make sure that uri paths are with posix separators
+  Result.uri += path::convert_to_slash(file, path::Style::posix);
+  return Result;
+}
+
+URI URI::parse(llvm::yaml::ScalarNode *Param) {
+  llvm::SmallString<10> Storage;
+  return URI::fromUri(Param->getValue(Storage));
+}
+
+std::string URI::unparse(const URI &U) {
+  return "\"" + U.uri + "\"";
+}
+
+llvm::Optional<TextDocumentIdentifier>
+TextDocumentIdentifier::parse(llvm::yaml::MappingNode *Params) {
+  TextDocumentIdentifier Result;
+  for (auto &NextKeyValue : *Params) {
+    auto *KeyString = dyn_cast<llvm::yaml::ScalarNode>(NextKeyValue.getKey());
+    if (!KeyString)
+      return llvm::None;
+
+    llvm::SmallString<10> KeyStorage;
+    StringRef KeyValue = KeyString->getValue(KeyStorage);
+    auto *Value =
+        dyn_cast_or_null<llvm::yaml::ScalarNode>(NextKeyValue.getValue());
+    if (!Value)
+      return llvm::None;
+
+    if (KeyValue == "uri") {
+      Result.uri = URI::parse(Value);
+    } else if (KeyValue == "version") {
+      // FIXME: parse version, but only for VersionedTextDocumentIdentifiers.
+    } else {
+      return llvm::None;
+    }
+  }
+  return Result;
+}
+
+llvm::Optional<Position> Position::parse(llvm::yaml::MappingNode *Params) {
+  Position Result;
+  for (auto &NextKeyValue : *Params) {
+    auto *KeyString = dyn_cast<llvm::yaml::ScalarNode>(NextKeyValue.getKey());
+    if (!KeyString)
+      return llvm::None;
+
+    llvm::SmallString<10> KeyStorage;
+    StringRef KeyValue = KeyString->getValue(KeyStorage);
+    auto *Value =
+        dyn_cast_or_null<llvm::yaml::ScalarNode>(NextKeyValue.getValue());
+    if (!Value)
+      return llvm::None;
+
+    llvm::SmallString<10> Storage;
+    if (KeyValue == "line") {
+      long long Val;
+      if (llvm::getAsSignedInteger(Value->getValue(Storage), 0, Val))
+        return llvm::None;
+      Result.line = Val;
+    } else if (KeyValue == "character") {
+      long long Val;
+      if (llvm::getAsSignedInteger(Value->getValue(Storage), 0, Val))
+        return llvm::None;
+      Result.character = Val;
+    } else {
+      return llvm::None;
+    }
+  }
+  return Result;
+}
+
+std::string Position::unparse(const Position &P) {
+  std::string Result;
+  llvm::raw_string_ostream(Result)
+      << llvm::format(R"({"line": %d, "character": %d})", P.line, P.character);
+  return Result;
+}
+
+llvm::Optional<Range> Range::parse(llvm::yaml::MappingNode *Params) {
+  Range Result;
+  for (auto &NextKeyValue : *Params) {
+    auto *KeyString = dyn_cast<llvm::yaml::ScalarNode>(NextKeyValue.getKey());
+    if (!KeyString)
+      return llvm::None;
+
+    llvm::SmallString<10> KeyStorage;
+    StringRef KeyValue = KeyString->getValue(KeyStorage);
+    auto *Value =
+        dyn_cast_or_null<llvm::yaml::MappingNode>(NextKeyValue.getValue());
+    if (!Value)
+      return llvm::None;
+
+    llvm::SmallString<10> Storage;
+    if (KeyValue == "start") {
+      auto Parsed = Position::parse(Value);
+      if (!Parsed)
+        return llvm::None;
+      Result.start = std::move(*Parsed);
+    } else if (KeyValue == "end") {
+      auto Parsed = Position::parse(Value);
+      if (!Parsed)
+        return llvm::None;
+      Result.end = std::move(*Parsed);
+    } else {
+      return llvm::None;
+    }
+  }
+  return Result;
+}
+
+std::string Range::unparse(const Range &P) {
+  std::string Result;
+  llvm::raw_string_ostream(Result) << llvm::format(
+      R"({"start": %s, "end": %s})", Position::unparse(P.start).c_str(),
+      Position::unparse(P.end).c_str());
+  return Result;
+}
+
+std::string Location::unparse(const Location &P) {
+  std::string Result;
+  llvm::raw_string_ostream(Result) << llvm::format(
+      R"({"uri": %s, "range": %s})", URI::unparse(P.uri).c_str(),
+      Range::unparse(P.range).c_str());
+  return Result;
+}
+
+llvm::Optional<TextDocumentItem>
+TextDocumentItem::parse(llvm::yaml::MappingNode *Params) {
+  TextDocumentItem Result;
+  for (auto &NextKeyValue : *Params) {
+    auto *KeyString = dyn_cast<llvm::yaml::ScalarNode>(NextKeyValue.getKey());
+    if (!KeyString)
+      return llvm::None;
+
+    llvm::SmallString<10> KeyStorage;
+    StringRef KeyValue = KeyString->getValue(KeyStorage);
+    auto *Value =
+        dyn_cast_or_null<llvm::yaml::ScalarNode>(NextKeyValue.getValue());
+    if (!Value)
+      return llvm::None;
+
+    llvm::SmallString<10> Storage;
+    if (KeyValue == "uri") {
+      Result.uri = URI::parse(Value);
+    } else if (KeyValue == "languageId") {
+      Result.languageId = Value->getValue(Storage);
+    } else if (KeyValue == "version") {
+      long long Val;
+      if (llvm::getAsSignedInteger(Value->getValue(Storage), 0, Val))
+        return llvm::None;
+      Result.version = Val;
+    } else if (KeyValue == "text") {
+      Result.text = Value->getValue(Storage);
+    } else {
+      return llvm::None;
+    }
+  }
+  return Result;
+}
+
+llvm::Optional<Metadata> Metadata::parse(llvm::yaml::MappingNode *Params) {
+  Metadata Result;
+  for (auto &NextKeyValue : *Params) {
+    auto *KeyString = dyn_cast<llvm::yaml::ScalarNode>(NextKeyValue.getKey());
+    if (!KeyString)
+      return llvm::None;
+
+    llvm::SmallString<10> KeyStorage;
+    StringRef KeyValue = KeyString->getValue(KeyStorage);
+    auto *Value = NextKeyValue.getValue();
+
+    llvm::SmallString<10> Storage;
+    if (KeyValue == "extraFlags") {
+      auto *Seq = dyn_cast<llvm::yaml::SequenceNode>(Value);
+      if (!Seq)
+        return llvm::None;
+      for (auto &Item : *Seq) {
+        auto *Node = dyn_cast<llvm::yaml::ScalarNode>(&Item);
+        if (!Node)
+          return llvm::None;
+        Result.extraFlags.push_back(Node->getValue(Storage));
+      }
+    }
+  }
+  return Result;
+}
+
+llvm::Optional<TextEdit> TextEdit::parse(llvm::yaml::MappingNode *Params) {
+  TextEdit Result;
+  for (auto &NextKeyValue : *Params) {
+    auto *KeyString = dyn_cast<llvm::yaml::ScalarNode>(NextKeyValue.getKey());
+    if (!KeyString)
+      return llvm::None;
+
+    llvm::SmallString<10> KeyStorage;
+    StringRef KeyValue = KeyString->getValue(KeyStorage);
+    auto *Value = NextKeyValue.getValue();
+
+    llvm::SmallString<10> Storage;
+    if (KeyValue == "range") {
+      auto *Map = dyn_cast<llvm::yaml::MappingNode>(Value);
+      if (!Map)
+        return llvm::None;
+      auto Parsed = Range::parse(Map);
+      if (!Parsed)
+        return llvm::None;
+      Result.range = std::move(*Parsed);
+    } else if (KeyValue == "newText") {
+      auto *Node = dyn_cast<llvm::yaml::ScalarNode>(Value);
+      if (!Node)
+        return llvm::None;
+      Result.newText = Node->getValue(Storage);
+    } else {
+      return llvm::None;
+    }
+  }
+  return Result;
+}
+
+std::string TextEdit::unparse(const TextEdit &P) {
+  std::string Result;
+  llvm::raw_string_ostream(Result) << llvm::format(
+      R"({"range": %s, "newText": "%s"})", Range::unparse(P.range).c_str(),
+      llvm::yaml::escape(P.newText).c_str());
+  return Result;
+}
+
+llvm::Optional<DidOpenTextDocumentParams>
+DidOpenTextDocumentParams::parse(llvm::yaml::MappingNode *Params) {
+  DidOpenTextDocumentParams Result;
+  for (auto &NextKeyValue : *Params) {
+    auto *KeyString = dyn_cast<llvm::yaml::ScalarNode>(NextKeyValue.getKey());
+    if (!KeyString)
+      return llvm::None;
+
+    llvm::SmallString<10> KeyStorage;
+    StringRef KeyValue = KeyString->getValue(KeyStorage);
+    auto *Value =
+        dyn_cast_or_null<llvm::yaml::MappingNode>(NextKeyValue.getValue());
+    if (!Value)
+      return llvm::None;
+
+    llvm::SmallString<10> Storage;
+    if (KeyValue == "textDocument") {
+      auto Parsed = TextDocumentItem::parse(Value);
+      if (!Parsed)
+        return llvm::None;
+      Result.textDocument = std::move(*Parsed);
+    } else if (KeyValue == "metadata") {
+      auto Parsed = Metadata::parse(Value);
+      if (!Parsed)
+        return llvm::None;
+      Result.metadata = std::move(*Parsed);
+    } else {
+      return llvm::None;
+    }
+  }
+  return Result;
+}
+
+llvm::Optional<DidCloseTextDocumentParams>
+DidCloseTextDocumentParams::parse(llvm::yaml::MappingNode *Params) {
+  DidCloseTextDocumentParams Result;
+  for (auto &NextKeyValue : *Params) {
+    auto *KeyString = dyn_cast<llvm::yaml::ScalarNode>(NextKeyValue.getKey());
+    if (!KeyString)
+      return llvm::None;
+
+    llvm::SmallString<10> KeyStorage;
+    StringRef KeyValue = KeyString->getValue(KeyStorage);
+    auto *Value = NextKeyValue.getValue();
+
+    if (KeyValue == "textDocument") {
+      auto *Map = dyn_cast<llvm::yaml::MappingNode>(Value);
+      if (!Map)
+        return llvm::None;
+      auto Parsed = TextDocumentIdentifier::parse(Map);
+      if (!Parsed)
+        return llvm::None;
+      Result.textDocument = std::move(*Parsed);
+    } else {
+      return llvm::None;
+    }
+  }
+  return Result;
+}
+
+llvm::Optional<DidChangeTextDocumentParams>
+DidChangeTextDocumentParams::parse(llvm::yaml::MappingNode *Params) {
+  DidChangeTextDocumentParams Result;
+  for (auto &NextKeyValue : *Params) {
+    auto *KeyString = dyn_cast<llvm::yaml::ScalarNode>(NextKeyValue.getKey());
+    if (!KeyString)
+      return llvm::None;
+
+    llvm::SmallString<10> KeyStorage;
+    StringRef KeyValue = KeyString->getValue(KeyStorage);
+    auto *Value = NextKeyValue.getValue();
+
+    llvm::SmallString<10> Storage;
+    if (KeyValue == "textDocument") {
+      auto *Map = dyn_cast<llvm::yaml::MappingNode>(Value);
+      if (!Map)
+        return llvm::None;
+      auto Parsed = TextDocumentIdentifier::parse(Map);
+      if (!Parsed)
+        return llvm::None;
+      Result.textDocument = std::move(*Parsed);
+    } else if (KeyValue == "contentChanges") {
+      auto *Seq = dyn_cast<llvm::yaml::SequenceNode>(Value);
+      if (!Seq)
+        return llvm::None;
+      for (auto &Item : *Seq) {
+        auto *I = dyn_cast<llvm::yaml::MappingNode>(&Item);
+        if (!I)
+          return llvm::None;
+        auto Parsed = TextDocumentContentChangeEvent::parse(I);
+        if (!Parsed)
+          return llvm::None;
+        Result.contentChanges.push_back(std::move(*Parsed));
+      }
+    } else {
+      return llvm::None;
+    }
+  }
+  return Result;
+}
+
+llvm::Optional<TextDocumentContentChangeEvent>
+TextDocumentContentChangeEvent::parse(llvm::yaml::MappingNode *Params) {
+  TextDocumentContentChangeEvent Result;
+  for (auto &NextKeyValue : *Params) {
+    auto *KeyString = dyn_cast<llvm::yaml::ScalarNode>(NextKeyValue.getKey());
+    if (!KeyString)
+      return llvm::None;
+
+    llvm::SmallString<10> KeyStorage;
+    StringRef KeyValue = KeyString->getValue(KeyStorage);
+    auto *Value =
+        dyn_cast_or_null<llvm::yaml::ScalarNode>(NextKeyValue.getValue());
+    if (!Value)
+      return llvm::None;
+
+    llvm::SmallString<10> Storage;
+    if (KeyValue == "text") {
+      Result.text = Value->getValue(Storage);
+    } else {
+      return llvm::None;
+    }
+  }
+  return Result;
+}
+
+llvm::Optional<FormattingOptions>
+FormattingOptions::parse(llvm::yaml::MappingNode *Params) {
+  FormattingOptions Result;
+  for (auto &NextKeyValue : *Params) {
+    auto *KeyString = dyn_cast<llvm::yaml::ScalarNode>(NextKeyValue.getKey());
+    if (!KeyString)
+      return llvm::None;
+
+    llvm::SmallString<10> KeyStorage;
+    StringRef KeyValue = KeyString->getValue(KeyStorage);
+    auto *Value =
+        dyn_cast_or_null<llvm::yaml::ScalarNode>(NextKeyValue.getValue());
+    if (!Value)
+      return llvm::None;
+
+    llvm::SmallString<10> Storage;
+    if (KeyValue == "tabSize") {
+      long long Val;
+      if (llvm::getAsSignedInteger(Value->getValue(Storage), 0, Val))
+        return llvm::None;
+      Result.tabSize = Val;
+    } else if (KeyValue == "insertSpaces") {
+      long long Val;
+      StringRef Str = Value->getValue(Storage);
+      if (llvm::getAsSignedInteger(Str, 0, Val)) {
+        if (Str == "true")
+          Val = 1;
+        else if (Str == "false")
+          Val = 0;
+        else
+          return llvm::None;
+      }
+      Result.insertSpaces = Val;
+    } else {
+      return llvm::None;
+    }
+  }
+  return Result;
+}
+
+std::string FormattingOptions::unparse(const FormattingOptions &P) {
+  std::string Result;
+  llvm::raw_string_ostream(Result) << llvm::format(
+      R"({"tabSize": %d, "insertSpaces": %d})", P.tabSize, P.insertSpaces);
+  return Result;
+}
+
+llvm::Optional<DocumentRangeFormattingParams>
+DocumentRangeFormattingParams::parse(llvm::yaml::MappingNode *Params) {
+  DocumentRangeFormattingParams Result;
+  for (auto &NextKeyValue : *Params) {
+    auto *KeyString = dyn_cast<llvm::yaml::ScalarNode>(NextKeyValue.getKey());
+    if (!KeyString)
+      return llvm::None;
+
+    llvm::SmallString<10> KeyStorage;
+    StringRef KeyValue = KeyString->getValue(KeyStorage);
+    auto *Value =
+        dyn_cast_or_null<llvm::yaml::MappingNode>(NextKeyValue.getValue());
+    if (!Value)
+      return llvm::None;
+
+    llvm::SmallString<10> Storage;
+    if (KeyValue == "textDocument") {
+      auto Parsed = TextDocumentIdentifier::parse(Value);
+      if (!Parsed)
+        return llvm::None;
+      Result.textDocument = std::move(*Parsed);
+    } else if (KeyValue == "range") {
+      auto Parsed = Range::parse(Value);
+      if (!Parsed)
+        return llvm::None;
+      Result.range = std::move(*Parsed);
+    } else if (KeyValue == "options") {
+      auto Parsed = FormattingOptions::parse(Value);
+      if (!Parsed)
+        return llvm::None;
+      Result.options = std::move(*Parsed);
+    } else {
+      return llvm::None;
+    }
+  }
+  return Result;
+}
+
+llvm::Optional<DocumentOnTypeFormattingParams>
+DocumentOnTypeFormattingParams::parse(llvm::yaml::MappingNode *Params) {
+  DocumentOnTypeFormattingParams Result;
+  for (auto &NextKeyValue : *Params) {
+    auto *KeyString = dyn_cast<llvm::yaml::ScalarNode>(NextKeyValue.getKey());
+    if (!KeyString)
+      return llvm::None;
+
+    llvm::SmallString<10> KeyStorage;
+    StringRef KeyValue = KeyString->getValue(KeyStorage);
+
+    if (KeyValue == "ch") {
+      auto *ScalarValue =
+          dyn_cast_or_null<llvm::yaml::ScalarNode>(NextKeyValue.getValue());
+      if (!ScalarValue)
+        return llvm::None;
+      llvm::SmallString<10> Storage;
+      Result.ch = ScalarValue->getValue(Storage);
+      continue;
+    }
+
+    auto *Value =
+        dyn_cast_or_null<llvm::yaml::MappingNode>(NextKeyValue.getValue());
+    if (!Value)
+      return llvm::None;
+    if (KeyValue == "textDocument") {
+      auto Parsed = TextDocumentIdentifier::parse(Value);
+      if (!Parsed)
+        return llvm::None;
+      Result.textDocument = std::move(*Parsed);
+    } else if (KeyValue == "position") {
+      auto Parsed = Position::parse(Value);
+      if (!Parsed)
+        return llvm::None;
+      Result.position = std::move(*Parsed);
+    } else if (KeyValue == "options") {
+      auto Parsed = FormattingOptions::parse(Value);
+      if (!Parsed)
+        return llvm::None;
+      Result.options = std::move(*Parsed);
+    } else {
+      return llvm::None;
+    }
+  }
+  return Result;
+}
+
+llvm::Optional<DocumentFormattingParams>
+DocumentFormattingParams::parse(llvm::yaml::MappingNode *Params) {
+  DocumentFormattingParams Result;
+  for (auto &NextKeyValue : *Params) {
+    auto *KeyString = dyn_cast<llvm::yaml::ScalarNode>(NextKeyValue.getKey());
+    if (!KeyString)
+      return llvm::None;
+
+    llvm::SmallString<10> KeyStorage;
+    StringRef KeyValue = KeyString->getValue(KeyStorage);
+    auto *Value =
+        dyn_cast_or_null<llvm::yaml::MappingNode>(NextKeyValue.getValue());
+    if (!Value)
+      return llvm::None;
+
+    llvm::SmallString<10> Storage;
+    if (KeyValue == "textDocument") {
+      auto Parsed = TextDocumentIdentifier::parse(Value);
+      if (!Parsed)
+        return llvm::None;
+      Result.textDocument = std::move(*Parsed);
+    } else if (KeyValue == "options") {
+      auto Parsed = FormattingOptions::parse(Value);
+      if (!Parsed)
+        return llvm::None;
+      Result.options = std::move(*Parsed);
+    } else {
+      return llvm::None;
+    }
+  }
+  return Result;
+}
+
+llvm::Optional<Diagnostic> Diagnostic::parse(llvm::yaml::MappingNode *Params) {
+  Diagnostic Result;
+  for (auto &NextKeyValue : *Params) {
+    auto *KeyString = dyn_cast<llvm::yaml::ScalarNode>(NextKeyValue.getKey());
+    if (!KeyString)
+      return llvm::None;
+
+    llvm::SmallString<10> KeyStorage;
+    StringRef KeyValue = KeyString->getValue(KeyStorage);
+
+    llvm::SmallString<10> Storage;
+    if (KeyValue == "range") {
+      auto *Value =
+          dyn_cast_or_null<llvm::yaml::MappingNode>(NextKeyValue.getValue());
+      if (!Value)
+        return llvm::None;
+      auto Parsed = Range::parse(Value);
+      if (!Parsed)
+        return llvm::None;
+      Result.range = std::move(*Parsed);
+    } else if (KeyValue == "severity") {
+      auto *Value =
+          dyn_cast_or_null<llvm::yaml::ScalarNode>(NextKeyValue.getValue());
+      if (!Value)
+        return llvm::None;
+      long long Val;
+      if (llvm::getAsSignedInteger(Value->getValue(Storage), 0, Val))
+        return llvm::None;
+      Result.severity = Val;
+    } else if (KeyValue == "message") {
+      auto *Value =
+          dyn_cast_or_null<llvm::yaml::ScalarNode>(NextKeyValue.getValue());
+      if (!Value)
+        return llvm::None;
+      Result.message = Value->getValue(Storage);
+    } else {
+      return llvm::None;
+    }
+  }
+  return Result;
+}
+
+llvm::Optional<CodeActionContext>
+CodeActionContext::parse(llvm::yaml::MappingNode *Params) {
+  CodeActionContext Result;
+  for (auto &NextKeyValue : *Params) {
+    auto *KeyString = dyn_cast<llvm::yaml::ScalarNode>(NextKeyValue.getKey());
+    if (!KeyString)
+      return llvm::None;
+
+    llvm::SmallString<10> KeyStorage;
+    StringRef KeyValue = KeyString->getValue(KeyStorage);
+    auto *Value = NextKeyValue.getValue();
+
+    llvm::SmallString<10> Storage;
+    if (KeyValue == "diagnostics") {
+      auto *Seq = dyn_cast<llvm::yaml::SequenceNode>(Value);
+      if (!Seq)
+        return llvm::None;
+      for (auto &Item : *Seq) {
+        auto *I = dyn_cast<llvm::yaml::MappingNode>(&Item);
+        if (!I)
+          return llvm::None;
+        auto Parsed = Diagnostic::parse(I);
+        if (!Parsed)
+          return llvm::None;
+        Result.diagnostics.push_back(std::move(*Parsed));
+      }
+    } else {
+      return llvm::None;
+    }
+  }
+  return Result;
+}
+
+llvm::Optional<CodeActionParams>
+CodeActionParams::parse(llvm::yaml::MappingNode *Params) {
+  CodeActionParams Result;
+  for (auto &NextKeyValue : *Params) {
+    auto *KeyString = dyn_cast<llvm::yaml::ScalarNode>(NextKeyValue.getKey());
+    if (!KeyString)
+      return llvm::None;
+
+    llvm::SmallString<10> KeyStorage;
+    StringRef KeyValue = KeyString->getValue(KeyStorage);
+    auto *Value =
+        dyn_cast_or_null<llvm::yaml::MappingNode>(NextKeyValue.getValue());
+    if (!Value)
+      return llvm::None;
+
+    llvm::SmallString<10> Storage;
+    if (KeyValue == "textDocument") {
+      auto Parsed = TextDocumentIdentifier::parse(Value);
+      if (!Parsed)
+        return llvm::None;
+      Result.textDocument = std::move(*Parsed);
+    } else if (KeyValue == "range") {
+      auto Parsed = Range::parse(Value);
+      if (!Parsed)
+        return llvm::None;
+      Result.range = std::move(*Parsed);
+    } else if (KeyValue == "context") {
+      auto Parsed = CodeActionContext::parse(Value);
+      if (!Parsed)
+        return llvm::None;
+      Result.context = std::move(*Parsed);
+    } else {
+      return llvm::None;
+    }
+  }
+  return Result;
+}
+
+llvm::Optional<TextDocumentPositionParams>
+TextDocumentPositionParams::parse(llvm::yaml::MappingNode *Params) {
+  TextDocumentPositionParams Result;
+  for (auto &NextKeyValue : *Params) {
+    auto *KeyString = dyn_cast<llvm::yaml::ScalarNode>(NextKeyValue.getKey());
+    if (!KeyString)
+      return llvm::None;
+
+    llvm::SmallString<10> KeyStorage;
+    StringRef KeyValue = KeyString->getValue(KeyStorage);
+    auto *Value =
+        dyn_cast_or_null<llvm::yaml::MappingNode>(NextKeyValue.getValue());
+    if (!Value)
+      continue;
+
+    llvm::SmallString<10> Storage;
+    if (KeyValue == "textDocument") {
+      auto Parsed = TextDocumentIdentifier::parse(Value);
+      if (!Parsed)
+        return llvm::None;
+      Result.textDocument = std::move(*Parsed);
+    } else if (KeyValue == "position") {
+      auto Parsed = Position::parse(Value);
+      if (!Parsed)
+        return llvm::None;
+      Result.position = std::move(*Parsed);
+    } else {
+      return llvm::None;
+    }
+  }
+  return Result;
+}
+
+std::string CompletionItem::unparse(const CompletionItem &CI) {
+  std::string Result = "{";
+  llvm::raw_string_ostream Os(Result);
+  assert(!CI.label.empty() && "completion item label is required");
+  Os << R"("label":")" << llvm::yaml::escape(CI.label) << R"(",)";
+  if (CI.kind != CompletionItemKind::Missing)
+    Os << R"("kind":)" << static_cast<int>(CI.kind) << R"(,)";
+  if (!CI.detail.empty())
+    Os << R"("detail":")" << llvm::yaml::escape(CI.detail) << R"(",)";
+  if (!CI.documentation.empty())
+    Os << R"("documentation":")" << llvm::yaml::escape(CI.documentation)
+       << R"(",)";
+  if (!CI.sortText.empty())
+    Os << R"("sortText":")" << llvm::yaml::escape(CI.sortText) << R"(",)";
+  if (!CI.filterText.empty())
+    Os << R"("filterText":")" << llvm::yaml::escape(CI.filterText) << R"(",)";
+  if (!CI.insertText.empty())
+    Os << R"("insertText":")" << llvm::yaml::escape(CI.insertText) << R"(",)";
+  if (CI.insertTextFormat != InsertTextFormat::Missing) {
+    Os << R"("insertTextFormat":")" << static_cast<int>(CI.insertTextFormat)
+       << R"(",)";
+  }
+  if (CI.textEdit)
+    Os << R"("textEdit":)" << TextEdit::unparse(*CI.textEdit) << ',';
+  if (!CI.additionalTextEdits.empty()) {
+    Os << R"("additionalTextEdits":[)";
+    for (const auto &Edit : CI.additionalTextEdits)
+      Os << TextEdit::unparse(Edit) << ",";
+    Os.flush();
+    // The list additionalTextEdits is guaranteed nonempty at this point.
+    // Replace the trailing comma with right brace.
+    Result.back() = ']';
+  }
+  Os.flush();
+  // Label is required, so Result is guaranteed to have a trailing comma.
+  Result.back() = '}';
+  return Result;
+}
diff --git a/clangd/Protocol.h b/clangd/Protocol.h
new file mode 100644 (file)
index 0000000..8239d8b
--- /dev/null
@@ -0,0 +1,406 @@
+//===--- Protocol.h - Language Server Protocol Implementation ---*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains structs based on the LSP specification at
+// https://github.com/Microsoft/language-server-protocol/blob/master/protocol.md
+//
+// This is not meant to be a complete implementation, new interfaces are added
+// when they're needed.
+//
+// Each struct has a parse and unparse function, that converts back and forth
+// between the struct and a JSON representation.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_PROTOCOL_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_PROTOCOL_H
+
+#include "llvm/ADT/Optional.h"
+#include "llvm/Support/YAMLParser.h"
+#include <string>
+#include <vector>
+
+namespace clang {
+namespace clangd {
+
+struct URI {
+  std::string uri;
+  std::string file;
+
+  static URI fromUri(llvm::StringRef uri);
+  static URI fromFile(llvm::StringRef file);
+
+  static URI parse(llvm::yaml::ScalarNode *Param);
+  static std::string unparse(const URI &U);
+
+  friend bool operator==(const URI &LHS, const URI &RHS) {
+    return LHS.uri == RHS.uri;
+  }
+
+  friend bool operator!=(const URI &LHS, const URI &RHS) {
+    return !(LHS == RHS);
+  }
+
+  friend bool operator<(const URI &LHS, const URI &RHS) {
+    return LHS.uri < RHS.uri;
+  }
+};
+
+struct TextDocumentIdentifier {
+  /// The text document's URI.
+  URI uri;
+
+  static llvm::Optional<TextDocumentIdentifier>
+  parse(llvm::yaml::MappingNode *Params);
+};
+
+struct Position {
+  /// Line position in a document (zero-based).
+  int line;
+
+  /// Character offset on a line in a document (zero-based).
+  int character;
+
+  friend bool operator==(const Position &LHS, const Position &RHS) {
+    return std::tie(LHS.line, LHS.character) ==
+           std::tie(RHS.line, RHS.character);
+  }
+  friend bool operator<(const Position &LHS, const Position &RHS) {
+    return std::tie(LHS.line, LHS.character) <
+           std::tie(RHS.line, RHS.character);
+  }
+
+  static llvm::Optional<Position> parse(llvm::yaml::MappingNode *Params);
+  static std::string unparse(const Position &P);
+};
+
+struct Range {
+  /// The range's start position.
+  Position start;
+
+  /// The range's end position.
+  Position end;
+
+  friend bool operator==(const Range &LHS, const Range &RHS) {
+    return std::tie(LHS.start, LHS.end) == std::tie(RHS.start, RHS.end);
+  }
+  friend bool operator<(const Range &LHS, const Range &RHS) {
+    return std::tie(LHS.start, LHS.end) < std::tie(RHS.start, RHS.end);
+  }
+
+  static llvm::Optional<Range> parse(llvm::yaml::MappingNode *Params);
+  static std::string unparse(const Range &P);
+};
+
+struct Location {
+  /// The text document's URI.
+  URI uri;
+  Range range;
+
+  friend bool operator==(const Location &LHS, const Location &RHS) {
+    return LHS.uri == RHS.uri && LHS.range == RHS.range;
+  }
+
+  friend bool operator!=(const Location &LHS, const Location &RHS) {
+    return !(LHS == RHS);
+  }
+
+  friend bool operator<(const Location &LHS, const Location &RHS) {
+    return std::tie(LHS.uri, LHS.range) < std::tie(RHS.uri, RHS.range);
+  }
+
+  static std::string unparse(const Location &P);
+};
+
+struct Metadata {
+  std::vector<std::string> extraFlags;
+
+  static llvm::Optional<Metadata> parse(llvm::yaml::MappingNode *Params);
+};
+
+struct TextEdit {
+  /// The range of the text document to be manipulated. To insert
+  /// text into a document create a range where start === end.
+  Range range;
+
+  /// The string to be inserted. For delete operations use an
+  /// empty string.
+  std::string newText;
+
+  static llvm::Optional<TextEdit> parse(llvm::yaml::MappingNode *Params);
+  static std::string unparse(const TextEdit &P);
+};
+
+struct TextDocumentItem {
+  /// The text document's URI.
+  URI uri;
+
+  /// The text document's language identifier.
+  std::string languageId;
+
+  /// The version number of this document (it will strictly increase after each
+  int version;
+
+  /// The content of the opened text document.
+  std::string text;
+
+  static llvm::Optional<TextDocumentItem>
+  parse(llvm::yaml::MappingNode *Params);
+};
+
+struct DidOpenTextDocumentParams {
+  /// The document that was opened.
+  TextDocumentItem textDocument;
+
+  /// Extension storing per-file metadata, such as compilation flags.
+  llvm::Optional<Metadata> metadata;
+
+  static llvm::Optional<DidOpenTextDocumentParams>
+  parse(llvm::yaml::MappingNode *Params);
+};
+
+struct DidCloseTextDocumentParams {
+  /// The document that was closed.
+  TextDocumentIdentifier textDocument;
+
+  static llvm::Optional<DidCloseTextDocumentParams>
+  parse(llvm::yaml::MappingNode *Params);
+};
+
+struct TextDocumentContentChangeEvent {
+  /// The new text of the document.
+  std::string text;
+
+  static llvm::Optional<TextDocumentContentChangeEvent>
+  parse(llvm::yaml::MappingNode *Params);
+};
+
+struct DidChangeTextDocumentParams {
+  /// The document that did change. The version number points
+  /// to the version after all provided content changes have
+  /// been applied.
+  TextDocumentIdentifier textDocument;
+
+  /// The actual content changes.
+  std::vector<TextDocumentContentChangeEvent> contentChanges;
+
+  static llvm::Optional<DidChangeTextDocumentParams>
+  parse(llvm::yaml::MappingNode *Params);
+};
+
+struct FormattingOptions {
+  /// Size of a tab in spaces.
+  int tabSize;
+
+  /// Prefer spaces over tabs.
+  bool insertSpaces;
+
+  static llvm::Optional<FormattingOptions>
+  parse(llvm::yaml::MappingNode *Params);
+  static std::string unparse(const FormattingOptions &P);
+};
+
+struct DocumentRangeFormattingParams {
+  /// The document to format.
+  TextDocumentIdentifier textDocument;
+
+  /// The range to format
+  Range range;
+
+  /// The format options
+  FormattingOptions options;
+
+  static llvm::Optional<DocumentRangeFormattingParams>
+  parse(llvm::yaml::MappingNode *Params);
+};
+
+struct DocumentOnTypeFormattingParams {
+  /// The document to format.
+  TextDocumentIdentifier textDocument;
+
+  /// The position at which this request was sent.
+  Position position;
+
+  /// The character that has been typed.
+  std::string ch;
+
+  /// The format options.
+  FormattingOptions options;
+
+  static llvm::Optional<DocumentOnTypeFormattingParams>
+  parse(llvm::yaml::MappingNode *Params);
+};
+
+struct DocumentFormattingParams {
+  /// The document to format.
+  TextDocumentIdentifier textDocument;
+
+  /// The format options
+  FormattingOptions options;
+
+  static llvm::Optional<DocumentFormattingParams>
+  parse(llvm::yaml::MappingNode *Params);
+};
+
+struct Diagnostic {
+  /// The range at which the message applies.
+  Range range;
+
+  /// The diagnostic's severity. Can be omitted. If omitted it is up to the
+  /// client to interpret diagnostics as error, warning, info or hint.
+  int severity;
+
+  /// The diagnostic's message.
+  std::string message;
+
+  friend bool operator==(const Diagnostic &LHS, const Diagnostic &RHS) {
+    return std::tie(LHS.range, LHS.severity, LHS.message) ==
+           std::tie(RHS.range, RHS.severity, RHS.message);
+  }
+  friend bool operator<(const Diagnostic &LHS, const Diagnostic &RHS) {
+    return std::tie(LHS.range, LHS.severity, LHS.message) <
+           std::tie(RHS.range, RHS.severity, RHS.message);
+  }
+
+  static llvm::Optional<Diagnostic> parse(llvm::yaml::MappingNode *Params);
+};
+
+struct CodeActionContext {
+  /// An array of diagnostics.
+  std::vector<Diagnostic> diagnostics;
+
+  static llvm::Optional<CodeActionContext>
+  parse(llvm::yaml::MappingNode *Params);
+};
+
+struct CodeActionParams {
+  /// The document in which the command was invoked.
+  TextDocumentIdentifier textDocument;
+
+  /// The range for which the command was invoked.
+  Range range;
+
+  /// Context carrying additional information.
+  CodeActionContext context;
+
+  static llvm::Optional<CodeActionParams>
+  parse(llvm::yaml::MappingNode *Params);
+};
+
+struct TextDocumentPositionParams {
+  /// The text document.
+  TextDocumentIdentifier textDocument;
+
+  /// The position inside the text document.
+  Position position;
+
+  static llvm::Optional<TextDocumentPositionParams>
+  parse(llvm::yaml::MappingNode *Params);
+};
+
+/// The kind of a completion entry.
+enum class CompletionItemKind {
+  Missing = 0,
+  Text = 1,
+  Method = 2,
+  Function = 3,
+  Constructor = 4,
+  Field = 5,
+  Variable = 6,
+  Class = 7,
+  Interface = 8,
+  Module = 9,
+  Property = 10,
+  Unit = 11,
+  Value = 12,
+  Enum = 13,
+  Keyword = 14,
+  Snippet = 15,
+  Color = 16,
+  File = 17,
+  Reference = 18,
+};
+
+/// Defines whether the insert text in a completion item should be interpreted
+/// as plain text or a snippet.
+enum class InsertTextFormat {
+  Missing = 0,
+  /// The primary text to be inserted is treated as a plain string.
+  PlainText = 1,
+  /// The primary text to be inserted is treated as a snippet.
+  ///
+  /// A snippet can define tab stops and placeholders with `$1`, `$2`
+  /// and `${3:foo}`. `$0` defines the final tab stop, it defaults to the end
+  /// of the snippet. Placeholders with equal identifiers are linked, that is
+  /// typing in one will update others too.
+  ///
+  /// See also:
+  /// https//github.com/Microsoft/vscode/blob/master/src/vs/editor/contrib/snippet/common/snippet.md
+  Snippet = 2,
+};
+
+struct CompletionItem {
+  /// The label of this completion item. By default also the text that is
+  /// inserted when selecting this completion.
+  std::string label;
+
+  /// The kind of this completion item. Based of the kind an icon is chosen by
+  /// the editor.
+  CompletionItemKind kind = CompletionItemKind::Missing;
+
+  /// A human-readable string with additional information about this item, like
+  /// type or symbol information.
+  std::string detail;
+
+  /// A human-readable string that represents a doc-comment.
+  std::string documentation;
+
+  /// A string that should be used when comparing this item with other items.
+  /// When `falsy` the label is used.
+  std::string sortText;
+
+  /// A string that should be used when filtering a set of completion items.
+  /// When `falsy` the label is used.
+  std::string filterText;
+
+  /// A string that should be inserted to a document when selecting this
+  /// completion. When `falsy` the label is used.
+  std::string insertText;
+
+  /// The format of the insert text. The format applies to both the `insertText`
+  /// property and the `newText` property of a provided `textEdit`.
+  InsertTextFormat insertTextFormat = InsertTextFormat::Missing;
+
+  /// An edit which is applied to a document when selecting this completion.
+  /// When an edit is provided `insertText` is ignored.
+  ///
+  /// Note: The range of the edit must be a single line range and it must
+  /// contain the position at which completion has been requested.
+  llvm::Optional<TextEdit> textEdit;
+
+  /// An optional array of additional text edits that are applied when selecting
+  /// this completion. Edits must not overlap with the main edit nor with
+  /// themselves.
+  std::vector<TextEdit> additionalTextEdits;
+
+  // TODO(krasimir): The following optional fields defined by the language
+  // server protocol are unsupported:
+  //
+  // command?: Command - An optional command that is executed *after* inserting
+  //                     this completion.
+  //
+  // data?: any - A data entry field that is preserved on a completion item
+  //              between a completion and a completion resolve request.
+  static std::string unparse(const CompletionItem &P);
+};
+
+} // namespace clangd
+} // namespace clang
+
+#endif
diff --git a/clangd/ProtocolHandlers.cpp b/clangd/ProtocolHandlers.cpp
new file mode 100644 (file)
index 0000000..b67ea0a
--- /dev/null
@@ -0,0 +1,242 @@
+//===--- ProtocolHandlers.cpp - LSP callbacks -----------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ProtocolHandlers.h"
+#include "ClangdLSPServer.h"
+#include "ClangdServer.h"
+#include "DraftStore.h"
+using namespace clang;
+using namespace clangd;
+
+namespace {
+
+struct InitializeHandler : Handler {
+  InitializeHandler(JSONOutput &Output, ProtocolCallbacks &Callbacks)
+      : Handler(Output), Callbacks(Callbacks) {}
+
+  void handleMethod(llvm::yaml::MappingNode *Params, StringRef ID) override {
+    Callbacks.onInitialize(ID, Output);
+  }
+
+private:
+  ProtocolCallbacks &Callbacks;
+};
+
+struct ShutdownHandler : Handler {
+  ShutdownHandler(JSONOutput &Output, ProtocolCallbacks &Callbacks)
+      : Handler(Output), Callbacks(Callbacks) {}
+
+  void handleMethod(llvm::yaml::MappingNode *Params, StringRef ID) override {
+    Callbacks.onShutdown(Output);
+  }
+
+private:
+  ProtocolCallbacks &Callbacks;
+};
+
+struct TextDocumentDidOpenHandler : Handler {
+  TextDocumentDidOpenHandler(JSONOutput &Output, ProtocolCallbacks &Callbacks)
+      : Handler(Output), Callbacks(Callbacks) {}
+
+  void handleNotification(llvm::yaml::MappingNode *Params) override {
+    auto DOTDP = DidOpenTextDocumentParams::parse(Params);
+    if (!DOTDP) {
+      Output.log("Failed to decode DidOpenTextDocumentParams!\n");
+      return;
+    }
+    Callbacks.onDocumentDidOpen(*DOTDP, Output);
+  }
+
+private:
+  ProtocolCallbacks &Callbacks;
+};
+
+struct TextDocumentDidChangeHandler : Handler {
+  TextDocumentDidChangeHandler(JSONOutput &Output, ProtocolCallbacks &Callbacks)
+      : Handler(Output), Callbacks(Callbacks) {}
+
+  void handleNotification(llvm::yaml::MappingNode *Params) override {
+    auto DCTDP = DidChangeTextDocumentParams::parse(Params);
+    if (!DCTDP || DCTDP->contentChanges.size() != 1) {
+      Output.log("Failed to decode DidChangeTextDocumentParams!\n");
+      return;
+    }
+
+    Callbacks.onDocumentDidChange(*DCTDP, Output);
+  }
+
+private:
+  ProtocolCallbacks &Callbacks;
+};
+
+struct TextDocumentDidCloseHandler : Handler {
+  TextDocumentDidCloseHandler(JSONOutput &Output, ProtocolCallbacks &Callbacks)
+      : Handler(Output), Callbacks(Callbacks) {}
+
+  void handleNotification(llvm::yaml::MappingNode *Params) override {
+    auto DCTDP = DidCloseTextDocumentParams::parse(Params);
+    if (!DCTDP) {
+      Output.log("Failed to decode DidCloseTextDocumentParams!\n");
+      return;
+    }
+
+    Callbacks.onDocumentDidClose(*DCTDP, Output);
+  }
+
+private:
+  ProtocolCallbacks &Callbacks;
+};
+
+struct TextDocumentOnTypeFormattingHandler : Handler {
+  TextDocumentOnTypeFormattingHandler(JSONOutput &Output,
+                                      ProtocolCallbacks &Callbacks)
+      : Handler(Output), Callbacks(Callbacks) {}
+
+  void handleMethod(llvm::yaml::MappingNode *Params, StringRef ID) override {
+    auto DOTFP = DocumentOnTypeFormattingParams::parse(Params);
+    if (!DOTFP) {
+      Output.log("Failed to decode DocumentOnTypeFormattingParams!\n");
+      return;
+    }
+
+    Callbacks.onDocumentOnTypeFormatting(*DOTFP, ID, Output);
+  }
+
+private:
+  ProtocolCallbacks &Callbacks;
+};
+
+struct TextDocumentRangeFormattingHandler : Handler {
+  TextDocumentRangeFormattingHandler(JSONOutput &Output,
+                                     ProtocolCallbacks &Callbacks)
+      : Handler(Output), Callbacks(Callbacks) {}
+
+  void handleMethod(llvm::yaml::MappingNode *Params, StringRef ID) override {
+    auto DRFP = DocumentRangeFormattingParams::parse(Params);
+    if (!DRFP) {
+      Output.log("Failed to decode DocumentRangeFormattingParams!\n");
+      return;
+    }
+
+    Callbacks.onDocumentRangeFormatting(*DRFP, ID, Output);
+  }
+
+private:
+  ProtocolCallbacks &Callbacks;
+};
+
+struct TextDocumentFormattingHandler : Handler {
+  TextDocumentFormattingHandler(JSONOutput &Output,
+                                ProtocolCallbacks &Callbacks)
+      : Handler(Output), Callbacks(Callbacks) {}
+
+  void handleMethod(llvm::yaml::MappingNode *Params, StringRef ID) override {
+    auto DFP = DocumentFormattingParams::parse(Params);
+    if (!DFP) {
+      Output.log("Failed to decode DocumentFormattingParams!\n");
+      return;
+    }
+
+    Callbacks.onDocumentFormatting(*DFP, ID, Output);
+  }
+
+private:
+  ProtocolCallbacks &Callbacks;
+};
+
+struct CodeActionHandler : Handler {
+  CodeActionHandler(JSONOutput &Output, ProtocolCallbacks &Callbacks)
+      : Handler(Output), Callbacks(Callbacks) {}
+
+  void handleMethod(llvm::yaml::MappingNode *Params, StringRef ID) override {
+    auto CAP = CodeActionParams::parse(Params);
+    if (!CAP) {
+      Output.log("Failed to decode CodeActionParams!\n");
+      return;
+    }
+
+    Callbacks.onCodeAction(*CAP, ID, Output);
+  }
+
+private:
+  ProtocolCallbacks &Callbacks;
+};
+
+struct CompletionHandler : Handler {
+  CompletionHandler(JSONOutput &Output, ProtocolCallbacks &Callbacks)
+      : Handler(Output), Callbacks(Callbacks) {}
+
+  void handleMethod(llvm::yaml::MappingNode *Params, StringRef ID) override {
+    auto TDPP = TextDocumentPositionParams::parse(Params);
+    if (!TDPP) {
+      Output.log("Failed to decode TextDocumentPositionParams!\n");
+      return;
+    }
+
+    Callbacks.onCompletion(*TDPP, ID, Output);
+  }
+
+private:
+  ProtocolCallbacks &Callbacks;
+};
+
+struct GotoDefinitionHandler : Handler {
+  GotoDefinitionHandler(JSONOutput &Output, ProtocolCallbacks &Callbacks)
+      : Handler(Output), Callbacks(Callbacks) {}
+
+  void handleMethod(llvm::yaml::MappingNode *Params, StringRef ID) override {
+    auto TDPP = TextDocumentPositionParams::parse(Params);
+    if (!TDPP) {
+      Output.log("Failed to decode TextDocumentPositionParams!\n");
+      return;
+    }
+
+    Callbacks.onGoToDefinition(*TDPP, ID, Output);
+  }
+
+private:
+  ProtocolCallbacks &Callbacks;
+};
+
+} // namespace
+
+void clangd::regiterCallbackHandlers(JSONRPCDispatcher &Dispatcher,
+                                     JSONOutput &Out,
+                                     ProtocolCallbacks &Callbacks) {
+  Dispatcher.registerHandler(
+      "initialize", llvm::make_unique<InitializeHandler>(Out, Callbacks));
+  Dispatcher.registerHandler(
+      "shutdown", llvm::make_unique<ShutdownHandler>(Out, Callbacks));
+  Dispatcher.registerHandler(
+      "textDocument/didOpen",
+      llvm::make_unique<TextDocumentDidOpenHandler>(Out, Callbacks));
+  Dispatcher.registerHandler(
+      "textDocument/didClose",
+      llvm::make_unique<TextDocumentDidCloseHandler>(Out, Callbacks));
+  Dispatcher.registerHandler(
+      "textDocument/didChange",
+      llvm::make_unique<TextDocumentDidChangeHandler>(Out, Callbacks));
+  Dispatcher.registerHandler(
+      "textDocument/rangeFormatting",
+      llvm::make_unique<TextDocumentRangeFormattingHandler>(Out, Callbacks));
+  Dispatcher.registerHandler(
+      "textDocument/onTypeFormatting",
+      llvm::make_unique<TextDocumentOnTypeFormattingHandler>(Out, Callbacks));
+  Dispatcher.registerHandler(
+      "textDocument/formatting",
+      llvm::make_unique<TextDocumentFormattingHandler>(Out, Callbacks));
+  Dispatcher.registerHandler(
+      "textDocument/codeAction",
+      llvm::make_unique<CodeActionHandler>(Out, Callbacks));
+  Dispatcher.registerHandler(
+      "textDocument/completion",
+      llvm::make_unique<CompletionHandler>(Out, Callbacks));
+  Dispatcher.registerHandler("textDocument/definition",
+      llvm::make_unique<GotoDefinitionHandler>(Out, Callbacks));
+}
diff --git a/clangd/ProtocolHandlers.h b/clangd/ProtocolHandlers.h
new file mode 100644 (file)
index 0000000..0ee0383
--- /dev/null
@@ -0,0 +1,59 @@
+//===--- ProtocolHandlers.h - LSP callbacks ---------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains the actions performed when the server gets a specific
+// request.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_PROTOCOLHANDLERS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_PROTOCOLHANDLERS_H
+
+#include "JSONRPCDispatcher.h"
+#include "Protocol.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/Support/raw_ostream.h"
+
+namespace clang {
+namespace clangd {
+
+class ProtocolCallbacks {
+public:
+  virtual ~ProtocolCallbacks() = default;
+
+  virtual void onInitialize(StringRef ID, JSONOutput &Out) = 0;
+  virtual void onShutdown(JSONOutput &Out) = 0;
+  virtual void onDocumentDidOpen(DidOpenTextDocumentParams Params,
+                                 JSONOutput &Out) = 0;
+  virtual void onDocumentDidChange(DidChangeTextDocumentParams Params,
+                                   JSONOutput &Out) = 0;
+
+  virtual void onDocumentDidClose(DidCloseTextDocumentParams Params,
+                                  JSONOutput &Out) = 0;
+  virtual void onDocumentFormatting(DocumentFormattingParams Params,
+                                    StringRef ID, JSONOutput &Out) = 0;
+  virtual void onDocumentOnTypeFormatting(DocumentOnTypeFormattingParams Params,
+                                          StringRef ID, JSONOutput &Out) = 0;
+  virtual void onDocumentRangeFormatting(DocumentRangeFormattingParams Params,
+                                         StringRef ID, JSONOutput &Out) = 0;
+  virtual void onCodeAction(CodeActionParams Params, StringRef ID,
+                            JSONOutput &Out) = 0;
+  virtual void onCompletion(TextDocumentPositionParams Params, StringRef ID,
+                            JSONOutput &Out) = 0;
+  virtual void onGoToDefinition(TextDocumentPositionParams Params, StringRef ID,
+                            JSONOutput &Out) = 0;
+};
+
+void regiterCallbackHandlers(JSONRPCDispatcher &Dispatcher, JSONOutput &Out,
+                             ProtocolCallbacks &Callbacks);
+
+} // namespace clangd
+} // namespace clang
+
+#endif
diff --git a/clangd/clients/clangd-vscode/.gitignore b/clangd/clients/clangd-vscode/.gitignore
new file mode 100644 (file)
index 0000000..8e5962e
--- /dev/null
@@ -0,0 +1,2 @@
+out
+node_modules
\ No newline at end of file
diff --git a/clangd/clients/clangd-vscode/.vscode/launch.json b/clangd/clients/clangd-vscode/.vscode/launch.json
new file mode 100644 (file)
index 0000000..cd6b87b
--- /dev/null
@@ -0,0 +1,28 @@
+// A launch configuration that compiles the extension and then opens it inside a new window
+{
+    "version": "0.1.0",
+    "configurations": [
+        {
+            "name": "Launch Extension",
+            "type": "extensionHost",
+            "request": "launch",
+            "runtimeExecutable": "${execPath}",
+            "args": ["--extensionDevelopmentPath=${workspaceRoot}" ],
+            "stopOnEntry": false,
+            "sourceMaps": true,
+            "outFiles": [ "${workspaceRoot}/out/src/**/*.js" ],
+            "preLaunchTask": "npm"
+        },
+        {
+            "name": "Launch Tests",
+            "type": "extensionHost",
+            "request": "launch",
+            "runtimeExecutable": "${execPath}",
+            "args": ["--extensionDevelopmentPath=${workspaceRoot}", "--extensionTestsPath=${workspaceRoot}/out/test" ],
+            "stopOnEntry": false,
+            "sourceMaps": true,
+            "outFiles": [ "${workspaceRoot}/out/test/**/*.js" ],
+            "preLaunchTask": "npm"
+        }
+    ]
+}
diff --git a/clangd/clients/clangd-vscode/.vscode/settings.json b/clangd/clients/clangd-vscode/.vscode/settings.json
new file mode 100644 (file)
index 0000000..d137133
--- /dev/null
@@ -0,0 +1,9 @@
+// Place your settings in this file to overwrite default and user settings.
+{
+    "files.exclude": {
+        "out": false // set this to true to hide the "out" folder with the compiled JS files
+    },
+    "search.exclude": {
+        "out": true // set this to false to include "out" folder in search results
+    }
+}
\ No newline at end of file
diff --git a/clangd/clients/clangd-vscode/.vscode/tasks.json b/clangd/clients/clangd-vscode/.vscode/tasks.json
new file mode 100644 (file)
index 0000000..fb7f662
--- /dev/null
@@ -0,0 +1,30 @@
+// Available variables which can be used inside of strings.
+// ${workspaceRoot}: the root folder of the team
+// ${file}: the current opened file
+// ${fileBasename}: the current opened file's basename
+// ${fileDirname}: the current opened file's dirname
+// ${fileExtname}: the current opened file's extension
+// ${cwd}: the current working directory of the spawned process
+
+// A task runner that calls a custom npm script that compiles the extension.
+{
+    "version": "0.1.0",
+
+    // we want to run npm
+    "command": "npm",
+
+    // the command is a shell script
+    "isShellCommand": true,
+
+    // show the output window only if unrecognized errors occur.
+    "showOutput": "silent",
+
+    // we run the custom script "compile" as defined in package.json
+    "args": ["run", "compile", "--loglevel", "silent"],
+
+    // The tsc compiler is started in watching mode
+    "isWatching": true,
+
+    // use the standard tsc in watch mode problem matcher to find compile problems in the output.
+    "problemMatcher": "$tsc-watch"
+}
\ No newline at end of file
diff --git a/clangd/clients/clangd-vscode/.vscodeignore b/clangd/clients/clangd-vscode/.vscodeignore
new file mode 100644 (file)
index 0000000..5ff3c19
--- /dev/null
@@ -0,0 +1,9 @@
+.vscode/**
+.vscode-test/**
+out/test/**
+test/**
+src/**
+**/*.map
+.gitignore
+tsconfig.json
+vsc-extension-quickstart.md
diff --git a/clangd/clients/clangd-vscode/README.txt b/clangd/clients/clangd-vscode/README.txt
new file mode 100644 (file)
index 0000000..f1ce44d
--- /dev/null
@@ -0,0 +1,11 @@
+A *toy* VS Code integration for development purposes.
+
+Steps:
+1. Make sure you have clangd in /usr/bin/clangd or edit src/extension.ts to
+point to the binary.
+2. Make sure you have nodejs and npm installed.
+3. Make sure you have VS Code installed.
+4. In order to start a development instance of VS code extended with this, run:
+   $ npm install
+   $ code .
+   When VS Code starts, press <F5>.
diff --git a/clangd/clients/clangd-vscode/package.json b/clangd/clients/clangd-vscode/package.json
new file mode 100644 (file)
index 0000000..6443b11
--- /dev/null
@@ -0,0 +1,58 @@
+{
+    "name": "clangd-vscode",
+    "displayName": "clangd-vscode",
+    "description": "Clang Language Server",
+    "version": "0.0.1",
+    "publisher": "Unpublished",
+    "engines": {
+        "vscode": "^1.8.0"
+    },
+    "categories": [
+        "Languages",
+        "Linters",
+        "Snippets"
+    ],
+    "activationEvents": [
+        "onLanguage:cpp",
+        "onLanguage:c"
+    ],
+    "main": "./out/src/extension",
+    "scripts": {
+        "vscode:prepublish": "tsc -p ./",
+        "compile": "tsc -watch -p ./",
+        "postinstall": "node ./node_modules/vscode/bin/install",
+        "test": "node ./node_modules/vscode/bin/test"
+    },
+    "dependencies": {
+        "vscode-languageclient": "^2.6.3",
+        "vscode-languageserver": "^2.6.2"
+    },
+    "devDependencies": {
+        "typescript": "^2.0.3",
+        "vscode": "^1.0.3",
+        "mocha": "^2.3.3",
+        "@types/node": "^6.0.40",
+        "@types/mocha": "^2.2.32"
+    },
+    "contributes": {
+        "configuration": {
+            "type": "object",
+            "title": "clangd configuration",
+            "properties": {
+                "clangd.path": {
+                    "type": "string",
+                    "default": "clangd",
+                    "description": "The path to clangd executable, e.g.: /usr/bin/clangd"
+                },
+                "clangd.arguments": {
+                    "type": "array",
+                    "default": [],
+                    "items": {
+                        "type": "string"
+                    },
+                    "description": "Arguments for clangd server"
+                }
+            }
+        }
+    }
+}
diff --git a/clangd/clients/clangd-vscode/src/extension.ts b/clangd/clients/clangd-vscode/src/extension.ts
new file mode 100644 (file)
index 0000000..7915fe9
--- /dev/null
@@ -0,0 +1,60 @@
+import * as vscode from 'vscode';
+import * as vscodelc from 'vscode-languageclient';
+
+/**
+ * Method to get workspace configuration option
+ * @param option name of the option (e.g. for clangd.path should be path)
+ * @param defaultValue default value to return if option is not set
+ */
+function getConfig<T>(option: string, defaultValue?: any) : T {
+    const config = vscode.workspace.getConfiguration('clangd');
+    return config.get<T>(option, defaultValue);
+}
+
+/**
+ *  this method is called when your extension is activate
+ *  your extension is activated the very first time the command is executed
+ */
+export function activate(context: vscode.ExtensionContext) {
+    const clangdPath = getConfig<string>('path');
+    const clangdArgs = getConfig<string[]>('arguments');
+
+    const serverOptions: vscodelc.ServerOptions = { command: clangdPath, args: clangdArgs };
+
+    const clientOptions: vscodelc.LanguageClientOptions = {
+        // Register the server for C/C++ files
+        documentSelector: ['c', 'cc', 'cpp', 'h', 'hh', 'hpp'],
+        uriConverters: {
+            // FIXME: by default the URI sent over the protocol will be percent encoded (see rfc3986#section-2.1)
+            //        the "workaround" below disables temporarily the encoding until decoding
+            //        is implemented properly in clangd
+            code2Protocol: (uri: vscode.Uri) : string => uri.toString(true),
+            protocol2Code: (uri: string) : vscode.Uri => vscode.Uri.parse(uri)
+        }
+    };
+
+    const clangdClient = new vscodelc.LanguageClient('Clang Language Server', serverOptions, clientOptions);
+
+    function applyTextEdits(uri: string, edits: vscodelc.TextEdit[]) {
+        let textEditor = vscode.window.activeTextEditor;
+
+        // FIXME: vscode expects that uri will be percent encoded
+        if (textEditor && textEditor.document.uri.toString(true) === uri) {
+            textEditor.edit(mutator => {
+                for (const edit of edits) {
+                    mutator.replace(vscodelc.Protocol2Code.asRange(edit.range), edit.newText);
+                }
+            }).then((success) => {
+                if (!success) {
+                    vscode.window.showErrorMessage('Failed to apply fixes to the document.');
+                }
+            });
+        }
+    }
+
+    console.log('Clang Language Server is now active!');
+
+    const disposable = clangdClient.start();
+
+    context.subscriptions.push(disposable, vscode.commands.registerCommand('clangd.applyFix', applyTextEdits));
+}
diff --git a/clangd/clients/clangd-vscode/test/extension.test.ts b/clangd/clients/clangd-vscode/test/extension.test.ts
new file mode 100644 (file)
index 0000000..de4e068
--- /dev/null
@@ -0,0 +1,15 @@
+/** The module 'assert' provides assertion methods from node */
+import * as assert from 'assert';
+
+import * as vscode from 'vscode';
+import * as myExtension from '../src/extension';
+
+// TODO: add tests
+suite("Extension Tests", () => {
+
+    // Defines a Mocha unit test
+    test("Something 1", () => {
+        assert.equal(-1, [1, 2, 3].indexOf(5));
+        assert.equal(-1, [1, 2, 3].indexOf(0));
+    });
+});
\ No newline at end of file
diff --git a/clangd/clients/clangd-vscode/test/index.ts b/clangd/clients/clangd-vscode/test/index.ts
new file mode 100644 (file)
index 0000000..50bae45
--- /dev/null
@@ -0,0 +1,22 @@
+//
+// PLEASE DO NOT MODIFY / DELETE UNLESS YOU KNOW WHAT YOU ARE DOING
+//
+// This file is providing the test runner to use when running extension tests.
+// By default the test runner in use is Mocha based.
+//
+// You can provide your own test runner if you want to override it by exporting
+// a function run(testRoot: string, clb: (error:Error) => void) that the extension
+// host can call to run the tests. The test runner is expected to use console.log
+// to report the results back to the caller. When the tests are finished, return
+// a possible error to the callback or null if none.
+
+var testRunner = require('vscode/lib/testrunner');
+
+// You can directly control Mocha options by uncommenting the following lines
+// See https://github.com/mochajs/mocha/wiki/Using-mocha-programmatically#set-options for more info
+testRunner.configure({
+    ui: 'tdd',                 // the TDD UI is being used in extension.test.ts (suite, test, etc.)
+    useColors: true // colored output from test results
+});
+
+module.exports = testRunner;
\ No newline at end of file
diff --git a/clangd/clients/clangd-vscode/tsconfig.json b/clangd/clients/clangd-vscode/tsconfig.json
new file mode 100644 (file)
index 0000000..0b05f30
--- /dev/null
@@ -0,0 +1,29 @@
+{
+    "compilerOptions": {
+        "module": "commonjs",
+        "target": "es6",
+        "outDir": "out",
+        "lib": [
+            "es6",
+            "es2015.core",
+            "es2015.collection",
+            "es2015.generator",
+            "es2015.iterable",
+            "es2015.promise",
+            "es2015.symbol",
+            "es2016.array.include"
+        ],
+        "sourceMap": true,
+        "rootDir": ".",
+        "alwaysStrict": true,
+        "noEmitOnError": true,
+        "noFallthroughCasesInSwitch": true,
+        "noImplicitAny": true,
+        "noImplicitReturns": true,
+        "noImplicitThis": true
+    },
+    "exclude": [
+        "node_modules",
+        ".vscode-test"
+    ]
+}
\ No newline at end of file
diff --git a/clangd/clients/clangd-vscode/vsc-extension-quickstart.md b/clangd/clients/clangd-vscode/vsc-extension-quickstart.md
new file mode 100644 (file)
index 0000000..24675ec
--- /dev/null
@@ -0,0 +1,33 @@
+# Toy VS Code Extension for clangd
+
+## What's in the folder
+* This folder contains all of the files necessary for your extension
+* `package.json` - this is the manifest file in which you declare your extension and command.
+The sample plugin registers a command and defines its title and command name. With this information
+VS Code can show the command in the command palette. It doesn’t yet need to load the plugin.
+* `src/extension.ts` - this is the main file where you will provide the implementation of your command.
+The file exports one function, `activate`, which is called the very first time your extension is
+activated (in this case by executing the command). Inside the `activate` function we call `registerCommand`.
+We pass the function containing the implementation of the command as the second parameter to
+`registerCommand`.
+
+## Get up and running straight away
+* press `F5` to open a new window with your extension loaded
+* run your command from the command palette by pressing (`Ctrl+Shift+P` or `Cmd+Shift+P` on Mac) and typing `Hello World`
+* set breakpoints in your code inside `src/extension.ts` to debug your extension
+* find output from your extension in the debug console
+
+## Make changes
+* you can relaunch the extension from the debug toolbar after changing code in `src/extension.ts`
+* you can also reload (`Ctrl+R` or `Cmd+R` on Mac) the VS Code window with your extension to load your changes
+
+## Explore the API
+* you can open the full set of our API when you open the file `node_modules/vscode/vscode.d.ts`
+
+## Run tests
+* open the debug viewlet (`Ctrl+Shift+D` or `Cmd+Shift+D` on Mac) and from the launch configuration dropdown pick `Launch Tests`
+* press `F5` to run the tests in a new window with your extension loaded
+* see the output of the test result in the debug console
+* make changes to `test/extension.test.ts` or create new test files inside the `test` folder
+    * by convention, the test runner will only consider files matching the name pattern `**.test.ts`
+    * you can create folders inside the `test` folder to structure your tests any way you want
\ No newline at end of file
diff --git a/clangd/tool/CMakeLists.txt b/clangd/tool/CMakeLists.txt
new file mode 100644 (file)
index 0000000..7d275a7
--- /dev/null
@@ -0,0 +1,18 @@
+include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..)
+
+add_clang_tool(clangd
+  ClangdMain.cpp
+  )
+
+install(TARGETS clangd RUNTIME DESTINATION bin)
+
+target_link_libraries(clangd
+  clangBasic
+  clangDaemon
+  clangFormat
+  clangFrontend
+  clangSema
+  clangTooling
+  clangToolingCore
+  LLVMSupport
+  )
diff --git a/clangd/tool/ClangdMain.cpp b/clangd/tool/ClangdMain.cpp
new file mode 100644 (file)
index 0000000..d67f75b
--- /dev/null
@@ -0,0 +1,40 @@
+//===--- ClangdMain.cpp - clangd server loop ------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ClangdLSPServer.h"
+#include "JSONRPCDispatcher.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Program.h"
+
+#include <iostream>
+#include <memory>
+#include <string>
+
+using namespace clang;
+using namespace clang::clangd;
+
+static llvm::cl::opt<bool>
+    RunSynchronously("run-synchronously",
+                     llvm::cl::desc("parse on main thread"),
+                     llvm::cl::init(false), llvm::cl::Hidden);
+
+int main(int argc, char *argv[]) {
+  llvm::cl::ParseCommandLineOptions(argc, argv, "clangd");
+
+  llvm::raw_ostream &Outs = llvm::outs();
+  llvm::raw_ostream &Logs = llvm::errs();
+  JSONOutput Out(Outs, Logs);
+
+  // Change stdin to binary to not lose \r\n on windows.
+  llvm::sys::ChangeStdinToBinary();
+
+  ClangdLSPServer LSPServer(Out, RunSynchronously);
+  LSPServer.run(std::cin);
+}
diff --git a/docs/CMakeLists.txt b/docs/CMakeLists.txt
new file mode 100644 (file)
index 0000000..8f442e1
--- /dev/null
@@ -0,0 +1,102 @@
+if (DOXYGEN_FOUND)
+  if (LLVM_ENABLE_DOXYGEN)
+    set(abs_srcdir ${CMAKE_CURRENT_SOURCE_DIR})
+    set(abs_builddir ${CMAKE_CURRENT_BINARY_DIR})
+
+    if (HAVE_DOT)
+      set(DOT ${LLVM_PATH_DOT})
+    endif()
+
+    if (LLVM_DOXYGEN_EXTERNAL_SEARCH)
+      set(enable_searchengine "YES")
+      set(searchengine_url "${LLVM_DOXYGEN_SEARCHENGINE_URL}")
+      set(enable_server_based_search "YES")
+      set(enable_external_search "YES")
+      set(extra_search_mappings "${LLVM_DOXYGEN_SEARCH_MAPPINGS}")
+    else()
+      set(enable_searchengine "NO")
+      set(searchengine_url "")
+      set(enable_server_based_search "NO")
+      set(enable_external_search "NO")
+      set(extra_search_mappings "")
+    endif()
+
+    # If asked, configure doxygen for the creation of a Qt Compressed Help file.
+    if (LLVM_ENABLE_DOXYGEN_QT_HELP)
+      set(CLANG_TOOLS_DOXYGEN_QCH_FILENAME "org.llvm.clang.qch" CACHE STRING
+        "Filename of the Qt Compressed help file")
+      set(CLANG_TOOLS_DOXYGEN_QHP_NAMESPACE "org.llvm.clang" CACHE STRING
+        "Namespace under which the intermediate Qt Help Project file lives")
+      set(CLANG_TOOLS_DOXYGEN_QHP_CUST_FILTER_NAME "Clang ${CLANG_VERSION}" CACHE STRING
+        "See http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-filters")
+      set(CLANG_TOOLS_DOXYGEN_QHP_CUST_FILTER_ATTRS "Clang,${CLANG_VERSION}" CACHE STRING
+        "See http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes")
+      set(clang_tools_doxygen_generate_qhp "YES")
+      set(clang_tools_doxygen_qch_filename "${CLANG_DOXYGEN_QCH_FILENAME}")
+      set(clang_tools_doxygen_qhp_namespace "${CLANG_DOXYGEN_QHP_NAMESPACE}")
+      set(clang_tools_doxygen_qhelpgenerator_path "${LLVM_DOXYGEN_QHELPGENERATOR_PATH}")
+      set(clang_tools_doxygen_qhp_cust_filter_name "${CLANG_DOXYGEN_QHP_CUST_FILTER_NAME}")
+      set(clang_tools_doxygen_qhp_cust_filter_attrs "${CLANG_DOXYGEN_QHP_CUST_FILTER_ATTRS}")
+    else()
+      set(clang_tools_doxygen_generate_qhp "NO")
+      set(clang_tools_doxygen_qch_filename "")
+      set(clang_tools_doxygen_qhp_namespace "")
+      set(clang_tools_doxygen_qhelpgenerator_path "")
+      set(clang_tools_doxygen_qhp_cust_filter_name "")
+      set(clang_tools_doxygen_qhp_cust_filter_attrs "")
+    endif()
+
+    option(LLVM_DOXYGEN_SVG
+      "Use svg instead of png files for doxygen graphs." OFF)
+    if (LLVM_DOXYGEN_SVG)
+      set(DOT_IMAGE_FORMAT "svg")
+    else()
+      set(DOT_IMAGE_FORMAT "png")
+    endif()
+
+    configure_file(${CMAKE_CURRENT_SOURCE_DIR}/doxygen.cfg.in
+      ${CMAKE_CURRENT_BINARY_DIR}/doxygen.cfg @ONLY)
+
+    set(abs_top_srcdir)
+    set(abs_top_builddir)
+    set(DOT)
+    set(enable_searchengine)
+    set(searchengine_url)
+    set(enable_server_based_search)
+    set(enable_external_search)
+    set(extra_search_mappings)
+    set(clang_tools_doxygen_generate_qhp)
+    set(clang_tools_doxygen_qch_filename)
+    set(clang_tools_doxygen_qhp_namespace)
+    set(clang_tools_doxygen_qhelpgenerator_path)
+    set(clang_tools_doxygen_qhp_cust_filter_name)
+    set(clang_tools_doxygen_qhp_cust_filter_attrs)
+    set(DOT_IMAGE_FORMAT)
+
+    add_custom_target(doxygen-clang-tools
+      COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/doxygen.cfg
+      WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
+      COMMENT "Generating clang doxygen documentation." VERBATIM)
+
+    if (LLVM_BUILD_DOCS)
+      add_dependencies(doxygen doxygen-clang-tools)
+    endif()
+
+    if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY)
+      install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/doxygen/html
+        DESTINATION docs/html)
+    endif()
+  endif()
+endif()
+
+if (LLVM_ENABLE_SPHINX)
+  include(AddSphinxTarget)
+  if (SPHINX_FOUND)
+    if (${SPHINX_OUTPUT_HTML})
+      add_sphinx_target(html clang-tools)
+    endif()
+    if (${SPHINX_OUTPUT_MAN})
+      add_sphinx_target(man clang-tools)
+    endif()
+  endif()
+endif()
diff --git a/docs/Doxyfile b/docs/Doxyfile
new file mode 100644 (file)
index 0000000..d674390
--- /dev/null
@@ -0,0 +1,1808 @@
+# Doxyfile 1.7.6.1
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+#       TAG = value [value, ...]
+# For lists items can also be appended using:
+#       TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING      = UTF-8
+
+# The PROJECT_NAME tag is a single word (or sequence of words) that should
+# identify the project. Note that if you do not use Doxywizard you need
+# to put quotes around the project name if it contains spaces.
+
+PROJECT_NAME           = clang-tools-extra
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER         =
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer
+# a quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF          =
+
+# With the PROJECT_LOGO tag one can specify an logo or icon that is
+# included in the documentation. The maximum height of the logo should not
+# exceed 55 pixels and the maximum width should not exceed 200 pixels.
+# Doxygen will copy the logo to the output directory.
+
+PROJECT_LOGO           =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+# Same directory that Sphinx uses.
+OUTPUT_DIRECTORY       = ./_build/
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS         = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
+# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
+# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
+# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak,
+# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
+
+OUTPUT_LANGUAGE        = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC      = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF           = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF       =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC    = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB  = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES        = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH        =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH    =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful if your file system
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES            = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF      = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF           = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS           = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES  = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE               = 2
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES                =
+
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding
+# "class=itcl::class" will allow you to use the command class in the
+# itcl::class meaning.
+
+TCL_SUBST              =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C  = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA   = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN   = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL   = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given extension.
+# Doxygen has a built-in mapping, but you can override or extend it using this
+# tag. The format is ext=language, where ext is a file extension, and language
+# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C,
+# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make
+# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
+# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions
+# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen.
+
+EXTENSION_MAPPING      =
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also makes the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT    = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT        = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT            = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate getter
+# and setter methods for a property. Setting this option to YES (the default)
+# will make doxygen replace the get and set methods by a property in the
+# documentation. This will only work if the methods are indeed getting or
+# setting a simple type. If this is not the case, or you want to show the
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT   = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC   = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING            = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and
+# unions are shown inside the group in which they are included (e.g. using
+# @ingroup) instead of on a separate page (for HTML and Man pages) or
+# section (for LaTeX and RTF).
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and
+# unions with only public data fields will be shown inline in the documentation
+# of the scope in which they are defined (i.e. file, namespace, or group
+# documentation), provided this scope is documented. If set to NO (the default),
+# structs, classes, and unions are shown on a separate page (for HTML and Man
+# pages) or section (for LaTeX and RTF).
+
+INLINE_SIMPLE_STRUCTS  = NO
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT   = NO
+
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
+# determine which symbols to keep in memory and which to flush to disk.
+# When the cache is full, less often used symbols will be written to disk.
+# For small to medium size projects (<1000 input files) the default value is
+# probably good enough. For larger projects a too small cache size can cause
+# doxygen to be busy swapping symbols to and from disk most of the time
+# causing a significant performance penalty.
+# If the system has enough physical memory increasing the cache will improve the
+# performance by keeping more symbols in memory. Note that the value works on
+# a logarithmic scale so increasing the size by one will roughly double the
+# memory usage. The cache size is given by this formula:
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols.
+
+SYMBOL_CACHE_SIZE      = 0
+
+# Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be
+# set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given
+# their name and scope. Since this can be an expensive process and often the
+# same symbol appear multiple times in the code, doxygen keeps a cache of
+# pre-resolved symbols. If the cache is too small doxygen will become slower.
+# If the cache is too large, memory is wasted. The cache size is given by this
+# formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols.
+
+LOOKUP_CACHE_SIZE      = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL            = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE        = YES
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC         = YES
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES  = YES
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS  = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
+# anonymous namespaces are hidden.
+
+EXTRACT_ANON_NSPACES   = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS     = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES     = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS  = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS      = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS          = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES       = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES       = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES     = YES
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
+# will list include files with double quotes in the documentation
+# rather than with sharp brackets.
+
+FORCE_LOCAL_INCLUDES   = NO
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO            = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS       = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS        = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen
+# will sort the (brief and detailed) documentation of class members so that
+# constructors and destructors are listed first. If set to NO (the default)
+# the constructors will appear in the respective orders defined by
+# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS.
+# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO
+# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES       = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME     = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to
+# do proper type resolution of all parameters of a function it will reject a
+# match between the prototype and the implementation of a member function even
+# if there is only one candidate or it is obvious which candidate to choose
+# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen
+# will still accept a match between prototype and implementation in such cases.
+
+STRICT_PROTO_MATCHING  = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST      = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST      = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST       = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS       =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or macro consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and macros in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES  = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES        = YES
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES       = YES
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES             = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
+# Namespaces page.  This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES        = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER    =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. The create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option.
+# You can optionally specify a file name after the option, if omitted
+# DoxygenLayout.xml will be used as the name of the layout file.
+
+LAYOUT_FILE            =
+
+# The CITE_BIB_FILES tag can be used to specify one or more bib files
+# containing the references data. This must be a list of .bib files. The
+# .bib extension is automatically appended if omitted. Using this command
+# requires the bibtex tool to be installed. See also
+# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style
+# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this
+# feature you need bibtex and perl available in the search path.
+
+CITE_BIB_FILES         =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET                  = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS               = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED   = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR      = YES
+
+# The WARN_NO_PARAMDOC option can be enabled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC       = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT            = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE           =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT                  = ../clang-modernize ../clang-apply-replacements ../clang-tidy
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
+# also the default input encoding. Doxygen uses libiconv (or the iconv built
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
+# the list of possible encodings.
+
+INPUT_ENCODING         = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh
+# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py
+# *.f90 *.f *.for *.vhd *.vhdl
+
+FILE_PATTERNS          = *.c \
+                         *.cc \
+                         *.cxx \
+                         *.cpp \
+                         *.c++ \
+                         *.d \
+                         *.java \
+                         *.ii \
+                         *.ixx \
+                         *.ipp \
+                         *.i++ \
+                         *.inl \
+                         *.h \
+                         *.hh \
+                         *.hxx \
+                         *.hpp \
+                         *.h++ \
+                         *.idl \
+                         *.odl \
+                         *.cs \
+                         *.php \
+                         *.php3 \
+                         *.inc \
+                         *.m \
+                         *.mm \
+                         *.dox \
+                         *.py \
+                         *.f90 \
+                         *.f \
+                         *.for \
+                         *.vhd \
+                         *.vhdl
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE              = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
+
+EXCLUDE                =
+
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS       = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS       =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS        =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH           =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS       = *
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE      = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH             =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.  If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER           =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis.  Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match.  The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty or if
+# none of the patterns match the file name, INPUT_FILTER is applied.
+
+FILTER_PATTERNS        =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES    = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any)
+# and it is also possible to disable source filtering for a specific pattern
+# using *.ext= (so without naming a filter). This option only has effect when
+# FILTER_SOURCE_FILES is enabled.
+
+FILTER_SOURCE_PATTERNS =
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER         = YES
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES         = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS    = NO
+
+# If the REFERENCED_BY_RELATION tag is set to YES
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION    = YES
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code.  Otherwise they will link to the documentation.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS              = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS       = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX     = YES
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX    = 4
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX          = clang::
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML          = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+# This directory nicely fits with the way the Sphinx outputs html, so that
+# the doxygen documentation will be visible in the doxygen/ path in the web
+# output (e.g. github pages).
+HTML_OUTPUT            = html/doxygen/
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION    = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header. Note that when using a custom header you are responsible
+# for the proper inclusion of any scripts and style sheets that doxygen
+# needs, which is dependent on the configuration options used.
+# It is advised to generate a default header using "doxygen -w html
+# header.html footer.html stylesheet.css YourConfigFile" and then modify
+# that header. Note that the header is subject to change so you typically
+# have to redo this when upgrading to a newer version of doxygen or when
+# changing the value of configuration settings such as GENERATE_TREEVIEW!
+
+HTML_HEADER            =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER            =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET        =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that
+# the files will be copied as-is; there are no commands or markers available.
+
+HTML_EXTRA_FILES       =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output.
+# Doxygen will adjust the colors in the style sheet and background images
+# according to this color. Hue is specified as an angle on a colorwheel,
+# see http://en.wikipedia.org/wiki/Hue for more information.
+# For instance the value 0 represents red, 60 is yellow, 120 is green,
+# 180 is cyan, 240 is blue, 300 purple, and 360 is red again.
+# The allowed range is 0 to 359.
+
+HTML_COLORSTYLE_HUE    = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of
+# the colors in the HTML output. For a value of 0 the output will use
+# grayscales only. A value of 255 will produce the most vivid colors.
+
+HTML_COLORSTYLE_SAT    = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to
+# the luminance component of the colors in the HTML output. Values below
+# 100 gradually make the output lighter, whereas values above 100 make
+# the output darker. The value divided by 100 is the actual gamma applied,
+# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2,
+# and 100 does not change the gamma.
+
+HTML_COLORSTYLE_GAMMA  = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting
+# this to NO can help when comparing the output of multiple runs.
+
+HTML_TIMESTAMP         = YES
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS     = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded. For this to work a browser that supports
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS  = NO
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+
+GENERATE_DOCSET        = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
+# can be grouped.
+
+DOCSET_FEEDNAME        = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID       = org.doxygen.Project
+
+# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+
+DOCSET_PUBLISHER_ID    = org.doxygen.Publisher
+
+# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher.
+
+DOCSET_PUBLISHER_NAME  = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP      = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE               =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION           =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI           = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING     =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC             = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND             = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated
+# that can be used as input for Qt's qhelpgenerator to generate a
+# Qt Compressed Help (.qch) of the generated HTML documentation.
+
+GENERATE_QHP           = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
+# be used to specify the file name of the resulting .qch file.
+# The path specified is relative to the HTML output folder.
+
+QCH_FILE               =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#namespace
+
+QHP_NAMESPACE          = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+
+QHP_VIRTUAL_FOLDER     = doc
+
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to
+# add. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#custom-filters
+
+QHP_CUST_FILTER_NAME   =
+
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">
+# Qt Help Project / Custom Filters</a>.
+
+QHP_CUST_FILTER_ATTRS  =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's
+# filter section matches.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">
+# Qt Help Project / Filter Attributes</a>.
+
+QHP_SECT_FILTER_ATTRS  =
+
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
+# be used to specify the location of Qt's qhelpgenerator.
+# If non-empty doxygen will try to run qhelpgenerator on the generated
+# .qhp file.
+
+QHG_LOCATION           =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
+# will be generated, which together with the HTML files, form an Eclipse help
+# plugin. To install this plugin and make it available under the help contents
+# menu in Eclipse, the contents of the directory containing the HTML and XML
+# files needs to be copied into the plugins directory of eclipse. The name of
+# the directory within the plugins directory should be the same as
+# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before
+# the help appears.
+
+GENERATE_ECLIPSEHELP   = NO
+
+# A unique identifier for the eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have
+# this name.
+
+ECLIPSE_DOC_ID         = org.doxygen.Project
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs)
+# at top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it. Since the tabs have the same information as the
+# navigation tree you can set this option to NO if you already set
+# GENERATE_TREEVIEW to YES.
+
+DISABLE_INDEX          = NO
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to YES, a side panel will be generated
+# containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
+# Windows users are probably better off using the HTML help feature.
+# Since the tree basically has the same information as the tab index you
+# could consider to set DISABLE_INDEX to NO when enabling this option.
+
+GENERATE_TREEVIEW      = NO
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values
+# (range [0,1..20]) that doxygen will group on one line in the generated HTML
+# documentation. Note that a value of 0 will completely suppress the enum
+# values from appearing in the overview section.
+
+ENUM_VALUES_PER_LINE   = 4
+
+# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories,
+# and Class Hierarchy pages using a tree view instead of an ordered list.
+
+USE_INLINE_TREES       = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH         = 250
+
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open
+# links to external symbols imported via tag files in a separate window.
+
+EXT_LINKS_IN_WINDOW    = NO
+
+# Use this tag to change the font size of Latex formulas included
+# as images in the HTML documentation. The default is 10. Note that
+# when you change the font size after a successful doxygen run you need
+# to manually remove any form_*.png images from the HTML output directory
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE       = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are
+# not supported properly for IE 6.0, but are supported on all modern browsers.
+# Note that when changing this option you need to delete any form_*.png files
+# in the HTML output before the changes have effect.
+
+FORMULA_TRANSPARENT    = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax
+# (see http://www.mathjax.org) which uses client side Javascript for the
+# rendering instead of using prerendered bitmaps. Use this if you do not
+# have LaTeX installed or if you want to formulas look prettier in the HTML
+# output. When enabled you also need to install MathJax separately and
+# configure the path to it using the MATHJAX_RELPATH option.
+
+USE_MATHJAX            = NO
+
+# When MathJax is enabled you need to specify the location relative to the
+# HTML output directory using the MATHJAX_RELPATH option. The destination
+# directory should contain the MathJax.js script. For instance, if the mathjax
+# directory is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the
+# mathjax.org site, so you can quickly see the result without installing
+# MathJax, but it is strongly recommended to install a local copy of MathJax
+# before deployment.
+
+MATHJAX_RELPATH        = http://www.mathjax.org/mathjax
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension
+# names that should be enabled during MathJax rendering.
+
+MATHJAX_EXTENSIONS     =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box
+# for the HTML output. The underlying search engine uses javascript
+# and DHTML and should work on any modern browser. Note that when using
+# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets
+# (GENERATE_DOCSET) there is already a search function so this one should
+# typically be disabled. For large projects the javascript based search engine
+# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
+
+SEARCHENGINE           = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a PHP enabled web server instead of at the web client
+# using Javascript. Doxygen will generate the search PHP script and index
+# file to put on the web server. The advantage of the server
+# based approach is that it scales better to large projects and allows
+# full text search. The disadvantages are that it is more difficult to setup
+# and does not have live searching capabilities.
+
+SERVER_BASED_SEARCH    = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+# For now, no latex output.
+GENERATE_LATEX         = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT           = latex/doxygen
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+# Note that when enabling USE_PDFLATEX this option is only used for
+# generating bitmaps for formulas in the HTML output, but not in the
+# Makefile that is written to the output directory.
+
+LATEX_CMD_NAME         = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX          = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE             = letter
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES         =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER           =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for
+# the generated latex document. The footer should contain everything after
+# the last chapter. If it is left blank doxygen will generate a
+# standard footer. Notice: only use this tag if you know what you are doing!
+
+LATEX_FOOTER           =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS         = NO
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX           = NO
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE        = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES     = NO
+
+# If LATEX_SOURCE_CODE is set to YES then doxygen will include
+# source code with syntax highlighting in the LaTeX output.
+# Note that which sources are shown also depends on other settings
+# such as SOURCE_BROWSER.
+
+LATEX_SOURCE_CODE      = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See
+# http://en.wikipedia.org/wiki/BibTeX for more info.
+
+LATEX_BIB_STYLE        = plain
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF           = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT             = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF            = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS         = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE    =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE    =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN           = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT             =
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION          =
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS              = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML           = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT             = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA             =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD                =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING     = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD       = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX          = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader.  This is useful
+# if you want to understand what is going on.  On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY         = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING   = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION        = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF     = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# pointed to by INCLUDE_PATH will be searched when a #include is found.
+
+SEARCH_INCLUDES        = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH           =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS  =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED             =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition that
+# overrules the definition found in the source code.
+
+EXPAND_AS_DEFINED      =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all references to function-like macros
+# that are alone on a line, have an all uppercase name, and do not end with a
+# semicolon, because these will confuse the parser if not removed.
+
+SKIP_FUNCTION_MACROS   = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+#   TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+#   TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES               =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE       =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS           = YES
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS        = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH              =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option also works with HAVE_DOT disabled, but it is recommended to
+# install and use dot, since it yields more powerful graphs.
+
+CLASS_DIAGRAMS         = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH            =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS   = NO
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT               = YES
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is
+# allowed to run in parallel. When set to 0 (the default) doxygen will
+# base this on the number of processors available in the system. You can set it
+# explicitly to a value larger than 0 to get control over the balance
+# between CPU load and processing speed.
+
+DOT_NUM_THREADS        = 0
+
+# By default doxygen will use the Helvetica font for all dot files that
+# doxygen generates. When you want a differently looking font you can specify
+# the font name using DOT_FONTNAME. You need to make sure dot is able to find
+# the font, which can be done by putting it in a standard location or by setting
+# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the
+# directory containing the font.
+
+DOT_FONTNAME           = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
+# The default size is 10pt.
+
+DOT_FONTSIZE           = 10
+
+# By default doxygen will tell dot to use the Helvetica font.
+# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to
+# set the path where dot can find it.
+
+DOT_FONTPATH           =
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH            = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH    = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS           = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK               = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS     = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH          = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH      = YES
+
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then
+# doxygen will generate a call dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable call graphs
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH             = NO
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
+# doxygen will generate a caller dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable caller
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH           = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will generate a graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY    = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH        = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are svg, png, jpg, or gif.
+# If left blank png will be used. If you choose svg you need to set
+# HTML_FILE_EXTENSION to xhtml in order to make the SVG files
+# visible in IE 9+ (other browsers do not have this requirement).
+
+DOT_IMAGE_FORMAT       = png
+
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+# Note that this requires a modern browser other than Internet Explorer.
+# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you
+# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files
+# visible. Older versions of IE do not have SVG support.
+
+INTERACTIVE_SVG        = NO
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH               =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS           =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the
+# \mscfile command).
+
+MSCFILE_DIRS           =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES    = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH    = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not
+# seem to support this out of the box. Warning: Depending on the platform used,
+# enabling this option may lead to badly anti-aliased labels on the edges of
+# a graph (i.e. they become hard to read).
+
+DOT_TRANSPARENT        = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS      = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND        = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP            = YES
diff --git a/docs/ModularizeUsage.rst b/docs/ModularizeUsage.rst
new file mode 100644 (file)
index 0000000..9f01165
--- /dev/null
@@ -0,0 +1,98 @@
+================
+Modularize Usage
+================
+
+``modularize [<modularize-options>] [<module-map>|<include-files-list>]*
+[<front-end-options>...]``
+
+``<modularize-options>`` is a place-holder for options
+specific to modularize, which are described below in
+`Modularize Command Line Options`.
+
+``<module-map>`` specifies the path of a file name for an
+existing module map. The module map must be well-formed in
+terms of syntax. Modularize will extract the header file names
+from the map. Only normal headers are checked, assuming headers
+marked "private", "textual", or "exclude" are not to be checked
+as a top-level include, assuming they either are included by
+other headers which are checked, or they are not suitable for
+modules.
+
+``<include-files-list>`` specifies the path of a file name for a
+file containing the newline-separated list of headers to check
+with respect to each other. Lines beginning with '#' and empty
+lines are ignored. Header file names followed by a colon and
+other space-separated file names will include those extra files
+as dependencies. The file names can be relative or full paths,
+but must be on the same line. For example::
+
+  header1.h
+  header2.h
+  header3.h: header1.h header2.h
+
+Note that unless a ``-prefix (header path)`` option is specified,
+non-absolute file paths in the header list file will be relative
+to the header list file directory. Use -prefix to specify a different
+directory.
+
+``<front-end-options>`` is a place-holder for regular Clang
+front-end arguments, which must follow the <include-files-list>.
+Note that by default, modularize assumes .h files
+contain C++ source, so if you are using a different language,
+you might need to use a ``-x`` option to tell Clang that the
+header contains another language, i.e.:  ``-x c``
+
+Note also that because modularize does not use the clang driver,
+you will likely need to pass in additional compiler front-end
+arguments to match those passed in by default by the driver.
+
+Modularize Command Line Options
+===============================
+
+.. option:: -prefix=<header-path>
+
+  Prepend the given path to non-absolute file paths in the header list file.
+  By default, headers are assumed to be relative to the header list file
+  directory. Use ``-prefix`` to specify a different directory.
+
+.. option:: -module-map-path=<module-map-path>
+
+  Generate a module map and output it to the given file. See the description
+  in :ref:`module-map-generation`.
+
+.. option:: -problem-files-list=<problem-files-list-file-name>
+
+  For use only with module map assistant. Input list of files that
+  have problems with respect to modules. These will still be
+  included in the generated module map, but will be marked as
+  "excluded" headers.
+
+.. option:: -root-module=<root-name>
+
+  Put modules generated by the -module-map-path option in an enclosing
+  module with the given name. See the description in :ref:`module-map-generation`.
+
+.. option:: -block-check-header-list-only
+
+  Limit the #include-inside-extern-or-namespace-block
+  check to only those headers explicitly listed in the header list.
+  This is a work-around for avoiding error messages for private includes that
+  purposefully get included inside blocks.
+
+.. option:: -no-coverage-check
+
+  Don't do the coverage check for a module map.
+
+.. option:: -coverage-check-only
+
+  Only do the coverage check for a module map.
+
+.. option:: -display-file-lists
+
+  Display lists of good files (no compile errors), problem files,
+  and a combined list with problem files preceded by a '#'.
+  This can be used to quickly determine which files have problems.
+  The latter combined list might be useful in starting to modularize
+  a set of headers. You can start with a full list of headers,
+  use -display-file-lists option, and then use the combined list as
+  your intermediate list, uncommenting-out headers as you fix them.
diff --git a/docs/README.txt b/docs/README.txt
new file mode 100644 (file)
index 0000000..4b60777
--- /dev/null
@@ -0,0 +1,11 @@
+-------------------------------------------------------------
+Documentation for the tools of clang-tools-extra repo project
+-------------------------------------------------------------
+
+Sphinx and doxygen documentation is generated by executing make.
+
+Sphinx html files can be generated separately using make html.
+
+Doxygen html files can also be generated using make doxygen.
+
+The generated documentation will be placed in _build/html.
diff --git a/docs/ReleaseNotes.rst b/docs/ReleaseNotes.rst
new file mode 100644 (file)
index 0000000..7ca8503
--- /dev/null
@@ -0,0 +1,149 @@
+=====================================
+Extra Clang Tools 5.0.0 Release Notes
+=====================================
+
+.. contents::
+   :local:
+   :depth: 3
+
+Written by the `LLVM Team <http://llvm.org/>`_
+
+Introduction
+============
+
+This document contains the release notes for the Extra Clang Tools, part of the
+Clang release 5.0.0. Here we describe the status of the Extra Clang Tools in
+some detail, including major improvements from the previous release and new
+feature work. All LLVM releases may be downloaded from the `LLVM releases web
+site <http://llvm.org/releases/>`_.
+
+For more information about Clang or LLVM, including information about
+the latest release, please see the `Clang Web Site <http://clang.llvm.org>`_ or
+the `LLVM Web Site <http://llvm.org>`_.
+
+What's New in Extra Clang Tools 5.0.0?
+======================================
+
+Improvements to clang-tidy
+--------------------------
+
+- New `android-cloexec-creat
+  <http://clang.llvm.org/extra/clang-tidy/checks/android-cloexec-creat.html>`_ check
+
+  Detect usage of ``creat()``.
+
+- New `android-cloexec-open
+  <http://clang.llvm.org/extra/clang-tidy/checks/android-cloexec-open.html>`_ check
+
+  Checks if the required file flag ``O_CLOEXEC`` exists in ``open()``,
+  ``open64()`` and ``openat()``.
+
+- New `android-cloexec-fopen
+  <http://clang.llvm.org/extra/clang-tidy/checks/android-cloexec-fopen.html>`_ check
+
+  Checks if the required mode ``e`` exists in the mode argument of ``fopen()``.
+
+- New `android-cloexec-socket
+  <http://clang.llvm.org/extra/clang-tidy/checks/android-cloexec-socket.html>`_ check
+
+  Checks if the required file flag ``SOCK_CLOEXEC`` is present in the argument of
+  ``socket()``.
+
+- New `bugprone-suspicious-memset-usage
+  <http://clang.llvm.org/extra/clang-tidy/checks/bugprone-suspicious-memset-usage.html>`_ check
+
+  Finds ``memset()`` calls with potential mistakes in their arguments.
+  Replaces and extends the ``google-runtime-memset`` check.
+
+- New `bugprone-undefined-memory-manipulation
+  <http://clang.llvm.org/extra/clang-tidy/checks/bugprone-undefined-memory-manipulation.html>`_ check
+
+  Finds calls of memory manipulation functions ``memset()``, ``memcpy()`` and
+  ``memmove()`` on not TriviallyCopyable objects resulting in undefined behavior.
+
+- New `cert-dcl21-cpp
+  <http://clang.llvm.org/extra/clang-tidy/checks/cert-dcl21-cpp.html>`_ check
+
+  Checks if the overloaded postfix ``operator++/--`` returns a constant object.
+
+- New `cert-dcl58-cpp
+  <http://clang.llvm.org/extra/clang-tidy/checks/cert-dcl58-cpp.html>`_ check
+
+  Finds modification of the ``std`` or ``posix`` namespace.
+
+- Improved `cppcoreguidelines-no-malloc
+  <http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-no-malloc.html>`_ check
+
+  Allow custom memory management functions to be considered as well.
+
+- New `misc-forwarding-reference-overload
+  <http://clang.llvm.org/extra/clang-tidy/checks/misc-forwarding-reference-overload.html>`_ check
+
+  Finds perfect forwarding constructors that can unintentionally hide copy or move constructors.
+
+- New `misc-lambda-function-name <http://clang.llvm.org/extra/clang-tidy/checks/misc-lambda-function-name.html>`_ check
+
+  Finds uses of ``__func__`` or ``__FUNCTION__`` inside lambdas.
+
+- New `modernize-replace-random-shuffle
+  <http://clang.llvm.org/extra/clang-tidy/checks/modernize-replace-random-shuffle.html>`_ check
+
+  Finds and fixes usage of ``std::random_shuffle`` as the function has been removed from C++17.
+
+- New `modernize-return-braced-init-list
+  <http://clang.llvm.org/extra/clang-tidy/checks/modernize-return-braced-init-list.html>`_ check
+
+  Finds and replaces explicit calls to the constructor in a return statement by
+  a braced initializer list so that the return type is not needlessly repeated.
+
+- New `modernize-unary-static-assert-check
+  <http://clang.llvm.org/extra/clang-tidy/checks/modernize-unary-static-assert.html>`_ check
+
+  The check diagnoses any ``static_assert`` declaration with an empty string literal
+  and provides a fix-it to replace the declaration with a single-argument ``static_assert`` declaration.
+
+- Improved `modernize-use-emplace
+  <http://clang.llvm.org/extra/clang-tidy/checks/modernize-use-emplace.html>`_ check
+
+  Removes unnecessary ``std::make_pair`` and ``std::make_tuple`` calls in
+  push_back calls and turns them into emplace_back. The check now also is able
+  to remove user-defined make functions from ``push_back`` calls on containers
+  of custom tuple-like types by providing `TupleTypes` and `TupleMakeFunctions`.
+
+- New `modernize-use-noexcept
+  <http://clang.llvm.org/extra/clang-tidy/checks/modernize-use-noexcept.html>`_ check
+
+  Replaces dynamic exception specifications with ``noexcept`` or a user defined macro.
+
+- New `performance-inefficient-vector-operation
+  <http://clang.llvm.org/extra/clang-tidy/checks/performance-inefficient-vector-operation.html>`_ check
+
+  Finds possible inefficient vector operations in for loops that may cause
+  unnecessary memory reallocations.
+
+- Added `NestingThreshold` to `readability-function-size
+  <http://clang.llvm.org/extra/clang-tidy/checks/readability-function-size.html>`_ check
+
+  Finds compound statements which create next nesting level after `NestingThreshold` and emits a warning.
+
+- Added `ParameterThreshold` to `readability-function-size
+  <http://clang.llvm.org/extra/clang-tidy/checks/readability-function-size.html>`_ check
+
+  Finds functions that have more than `ParameterThreshold` parameters and emits a warning.
+
+- New `readability-misleading-indentation
+  <http://clang.llvm.org/extra/clang-tidy/checks/readability-misleading-indentation.html>`_ check
+
+  Finds misleading indentation where braces should be introduced or the code should be reformatted.
+
+- Support clang-formatting of the code around applied fixes (``-format-style``
+  command-line option).
+
+- New `bugprone` module
+
+  Adds checks that target bugprone code constructs.
+
+- New `hicpp` module
+
+  Adds checks that implement the `High Integrity C++ Coding Standard <http://www.codingstandard.com/section/index/>`_ and other safety
+  standards. Many checks are aliased to other modules.
diff --git a/docs/clang-modernize.rst b/docs/clang-modernize.rst
new file mode 100644 (file)
index 0000000..0a42961
--- /dev/null
@@ -0,0 +1,4 @@
+:orphan:
+
+All :program:`clang-modernize` transforms have moved to :doc:`clang-tidy/index`
+(see the ``modernize`` module).
diff --git a/docs/clang-rename.rst b/docs/clang-rename.rst
new file mode 100644 (file)
index 0000000..f622779
--- /dev/null
@@ -0,0 +1,166 @@
+============
+Clang-Rename
+============
+
+.. contents::
+
+See also:
+
+.. toctree::
+   :maxdepth: 1
+
+
+:program:`clang-rename` is a C++ refactoring tool. Its purpose is to perform
+efficient renaming actions in large-scale projects such as renaming classes,
+functions, variables, arguments, namespaces etc.
+
+The tool is in a very early development stage, so you might encounter bugs and
+crashes. Submitting reports with information about how to reproduce the issue
+to `the LLVM bugtracker <https://llvm.org/bugs>`_ will definitely help the
+project. If you have any ideas or suggestions, you might want to put a feature
+request there.
+
+Using Clang-Rename
+==================
+
+:program:`clang-rename` is a `LibTooling
+<http://clang.llvm.org/docs/LibTooling.html>`_-based tool, and it's easier to
+work with if you set up a compile command database for your project (for an
+example of how to do this see `How To Setup Tooling For LLVM
+<http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html>`_). You can also
+specify compilation options on the command line after `--`:
+
+.. code-block:: console
+
+  $ clang-rename -offset=42 -new-name=foo test.cpp -- -Imy_project/include -DMY_DEFINES ...
+
+
+To get an offset of a symbol in a file run
+
+.. code-block:: console
+
+  $ grep -FUbo 'foo' file.cpp
+
+
+The tool currently supports renaming actions inside a single translation unit
+only. It is planned to extend the tool's functionality to support multi-TU
+renaming actions in the future.
+
+:program:`clang-rename` also aims to be easily integrated into popular text
+editors, such as Vim and Emacs, and improve the workflow of users.
+
+Although a command line interface exists, it is highly recommended to use the
+text editor interface instead for better experience.
+
+You can also identify one or more symbols to be renamed by giving the fully
+qualified name:
+
+.. code-block:: console
+
+  $ clang-rename -qualified-name=foo -new-name=bar test.cpp
+
+Renaming multiple symbols at once is supported, too. However,
+:program:`clang-rename` doesn't accept both `-offset` and `-qualified-name` at
+the same time. So, you can either specify multiple `-offset` or
+`-qualified-name`.
+
+.. code-block:: console
+
+  $ clang-rename -offset=42 -new-name=bar1 -offset=150 -new-name=bar2 test.cpp
+
+or
+
+.. code-block:: console
+
+  $ clang-rename -qualified-name=foo1 -new-name=bar1 -qualified-name=foo2 -new-name=bar2 test.cpp
+
+
+Alternatively, {offset | qualified-name} / new-name pairs can be put into a YAML
+file:
+
+.. code-block:: yaml
+
+  ---
+  - Offset:         42
+    NewName:        bar1
+  - Offset:         150
+    NewName:        bar2
+  ...
+
+or
+
+.. code-block:: yaml
+
+  ---
+  - QualifiedName:  foo1
+    NewName:        bar1
+  - QualifiedName:  foo2
+    NewName:        bar2
+  ...
+
+That way you can avoid spelling out all the names as command line arguments:
+
+.. code-block:: console
+
+  $ clang-rename -input=test.yaml test.cpp
+
+:program:`clang-rename` offers the following options:
+
+.. code-block:: console
+
+  $ clang-rename --help
+  USAGE: clang-rename [subcommand] [options] <source0> [... <sourceN>]
+
+  OPTIONS:
+
+  Generic Options:
+
+    -help                      - Display available options (-help-hidden for more)
+    -help-list                 - Display list of available options (-help-list-hidden for more)
+    -version                   - Display the version of this program
+
+  clang-rename common options:
+
+    -export-fixes=<filename>   - YAML file to store suggested fixes in.
+    -extra-arg=<string>        - Additional argument to append to the compiler command line
+    -extra-arg-before=<string> - Additional argument to prepend to the compiler command line
+    -force                     - Ignore nonexistent qualified names.
+    -i                         - Overwrite edited <file>s.
+    -input=<string>            - YAML file to load oldname-newname pairs from.
+    -new-name=<string>         - The new name to change the symbol to.
+    -offset=<uint>             - Locates the symbol by offset as opposed to <line>:<column>.
+    -p=<string>                - Build path
+    -pl                        - Print the locations affected by renaming to stderr.
+    -pn                        - Print the found symbol's name prior to renaming to stderr.
+    -qualified-name=<string>   - The fully qualified name of the symbol.
+
+Vim Integration
+===============
+
+You can call :program:`clang-rename` directly from Vim! To set up
+:program:`clang-rename` integration for Vim see
+`clang-rename/tool/clang-rename.py
+<http://reviews.llvm.org/diffusion/L/browse/clang-tools-extra/trunk/clang-rename/tool/clang-rename.py>`_.
+
+Please note that **you have to save all buffers, in which the replacement will
+happen before running the tool**.
+
+Once installed, you can point your cursor to symbols you want to rename, press
+`<leader>cr` and type new desired name. The `<leader> key
+<http://vim.wikia.com/wiki/Mapping_keys_in_Vim_-_Tutorial_(Part_3)#Map_leader>`_
+is a reference to a specific key defined by the mapleader variable and is bound
+to backslash by default.
+
+Emacs Integration
+=================
+
+You can also use :program:`clang-rename` while using Emacs! To set up
+:program:`clang-rename` integration for Emacs see
+`clang-rename/tool/clang-rename.el
+<http://reviews.llvm.org/diffusion/L/browse/clang-tools-extra/trunk/clang-rename/tool/clang-rename.el>`_.
+
+Once installed, you can point your cursor to symbols you want to rename, press
+`M-X`, type `clang-rename` and new desired name.
+
+Please note that **you have to save all buffers, in which the replacement will
+happen before running the tool**.
diff --git a/docs/clang-tidy.rst b/docs/clang-tidy.rst
new file mode 100644 (file)
index 0000000..bcd2bf1
--- /dev/null
@@ -0,0 +1,6 @@
+:orphan:
+
+.. meta::
+   :http-equiv=refresh: 0;URL='clang-tidy/'
+
+clang-tidy documentation has moved here: http://clang.llvm.org/extra/clang-tidy/
diff --git a/docs/clang-tidy/checks/android-cloexec-creat.rst b/docs/clang-tidy/checks/android-cloexec-creat.rst
new file mode 100644 (file)
index 0000000..d2892a0
--- /dev/null
@@ -0,0 +1,16 @@
+.. title:: clang-tidy - android-cloexec-creat
+
+android-cloexec-creat
+=====================
+
+The usage of ``creat()`` is not recommended, it's better to use ``open()``.
+
+Examples:
+
+.. code-block:: c++
+
+  int fd = creat(path, mode);
+
+  // becomes
+
+  int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, mode);
diff --git a/docs/clang-tidy/checks/android-cloexec-fopen.rst b/docs/clang-tidy/checks/android-cloexec-fopen.rst
new file mode 100644 (file)
index 0000000..0552969
--- /dev/null
@@ -0,0 +1,18 @@
+.. title:: clang-tidy - android-cloexec-fopen
+
+android-cloexec-fopen
+=====================
+
+``fopen()`` should include ``e`` in their mode string; so ``re`` would be
+valid. This is equivalent to having set ``FD_CLOEXEC on`` that descriptor.
+
+Examples:
+
+.. code-block:: c++
+
+  fopen("fn", "r");
+
+  // becomes
+
+  fopen("fn", "re");
+
diff --git a/docs/clang-tidy/checks/android-cloexec-open.rst b/docs/clang-tidy/checks/android-cloexec-open.rst
new file mode 100644 (file)
index 0000000..ee3e4b5
--- /dev/null
@@ -0,0 +1,24 @@
+.. title:: clang-tidy - android-cloexec-open
+
+android-cloexec-open
+====================
+
+A common source of security bugs is code that opens a file without using the
+``O_CLOEXEC`` flag.  Without that flag, an opened sensitive file would remain
+open across a fork+exec to a lower-privileged SELinux domain, leaking that
+sensitive data. Open-like functions including ``open()``, ``openat()``, and
+``open64()`` should include ``O_CLOEXEC`` in their flags argument.
+
+Examples:
+
+.. code-block:: c++
+
+  open("filename", O_RDWR);
+  open64("filename", O_RDWR);
+  openat(0, "filename", O_RDWR);
+
+  // becomes
+
+  open("filename", O_RDWR | O_CLOEXEC);
+  open64("filename", O_RDWR | O_CLOEXEC);
+  openat(0, "filename", O_RDWR | O_CLOEXEC);
diff --git a/docs/clang-tidy/checks/android-cloexec-socket.rst b/docs/clang-tidy/checks/android-cloexec-socket.rst
new file mode 100644 (file)
index 0000000..10b1976
--- /dev/null
@@ -0,0 +1,18 @@
+.. title:: clang-tidy - android-cloexec-socket
+
+android-cloexec-socket
+======================
+
+``socket()`` should include ``SOCK_CLOEXEC`` in its type argument to avoid the
+file descriptor leakage. Without this flag, an opened sensitive file would
+remain open across a fork+exec to a lower-privileged SELinux domain.
+
+Examples:
+
+.. code-block:: c++
+
+  socket(domain, type, SOCK_STREAM);
+
+  // becomes
+
+  socket(domain, type, SOCK_STREAM | SOCK_CLOEXEC);
diff --git a/docs/clang-tidy/checks/boost-use-to-string.rst b/docs/clang-tidy/checks/boost-use-to-string.rst
new file mode 100644 (file)
index 0000000..ebeb829
--- /dev/null
@@ -0,0 +1,22 @@
+.. title:: clang-tidy - boost-use-to-string
+
+boost-use-to-string
+===================
+
+This check finds conversion from integer type like ``int`` to ``std::string`` or
+``std::wstring`` using ``boost::lexical_cast``, and replace it with calls to
+``std::to_string`` and ``std::to_wstring``.
+
+It doesn't replace conversion from floating points despite the ``to_string``
+overloads, because it would change the behaviour.
+
+
+  .. code-block:: c++
+
+    auto str = boost::lexical_cast<std::string>(42);
+    auto wstr = boost::lexical_cast<std::wstring>(2137LL);
+
+    // Will be changed to
+    auto str = std::to_string(42);
+    auto wstr = std::to_wstring(2137LL);
+
diff --git a/docs/clang-tidy/checks/bugprone-suspicious-memset-usage.rst b/docs/clang-tidy/checks/bugprone-suspicious-memset-usage.rst
new file mode 100644 (file)
index 0000000..82609d1
--- /dev/null
@@ -0,0 +1,54 @@
+.. title:: clang-tidy - bugprone-suspicious-memset-usage
+
+bugprone-suspicious-memset-usage
+================================
+
+This check finds ``memset()`` calls with potential mistakes in their arguments.
+Considering the function as ``void* memset(void* destination, int fill_value,
+size_t byte_count)``, the following cases are covered:
+
+**Case 1: Fill value is a character ``'0'``**
+
+Filling up a memory area with ASCII code 48 characters is not customary,
+possibly integer zeroes were intended instead.
+The check offers a replacement of ``'0'`` with ``0``. Memsetting character
+pointers with ``'0'`` is allowed.
+
+**Case 2: Fill value is truncated**
+
+Memset converts ``fill_value`` to ``unsigned char`` before using it. If
+``fill_value`` is out of unsigned character range, it gets truncated
+and memory will not contain the desired pattern.
+
+**Case 3: Byte count is zero**
+
+Calling memset with a literal zero in its ``byte_count`` argument is likely
+to be unintended and swapped with ``fill_value``. The check offers to swap
+these two arguments.
+
+Corresponding cpplint.py check name: ``runtime/memset``.
+
+
+Examples:
+
+.. code-block:: c++
+
+  void foo() {
+    int i[5] = {1, 2, 3, 4, 5};
+    int *ip = i;
+    char c = '1';
+    char *cp = &c;
+    int v = 0;
+
+    // Case 1
+    memset(ip, '0', 1); // suspicious
+    memset(cp, '0', 1); // OK
+
+    // Case 2
+    memset(ip, 0xabcd, 1); // fill value gets truncated
+    memset(ip, 0x00, 1);   // OK
+
+    // Case 3
+    memset(ip, sizeof(int), v); // zero length, potentially swapped
+    memset(ip, 0, 1);           // OK
+  }
diff --git a/docs/clang-tidy/checks/bugprone-undefined-memory-manipulation.rst b/docs/clang-tidy/checks/bugprone-undefined-memory-manipulation.rst
new file mode 100644 (file)
index 0000000..2735184
--- /dev/null
@@ -0,0 +1,7 @@
+.. title:: clang-tidy - bugprone-undefined-memory-manipulation
+
+bugprone-undefined-memory-manipulation
+======================================
+
+Finds calls of memory manipulation functions ``memset()``, ``memcpy()`` and
+``memmove()`` on not TriviallyCopyable objects resulting in undefined behavior.
diff --git a/docs/clang-tidy/checks/cert-dcl03-c.rst b/docs/clang-tidy/checks/cert-dcl03-c.rst
new file mode 100644 (file)
index 0000000..2e4780b
--- /dev/null
@@ -0,0 +1,9 @@
+.. title:: clang-tidy - cert-dcl03-c
+.. meta::
+   :http-equiv=refresh: 5;URL=misc-static-assert.html
+
+cert-dcl03-c
+============
+
+The cert-dcl03-c check is an alias, please see
+`misc-static-assert <misc-static-assert.html>`_ for more information.
diff --git a/docs/clang-tidy/checks/cert-dcl21-cpp.rst b/docs/clang-tidy/checks/cert-dcl21-cpp.rst
new file mode 100644 (file)
index 0000000..d5c2948
--- /dev/null
@@ -0,0 +1,12 @@
+.. title:: clang-tidy - cert-dcl21-cpp
+
+cert-dcl21-cpp
+==============
+
+This check flags postfix ``operator++`` and ``operator--`` declarations
+if the return type is not a const object. This also warns if the return type
+is a reference type.
+
+This check corresponds to the CERT C++ Coding Standard recommendation
+`DCL21-CPP. Overloaded postfix increment and decrement operators should return a const object
+<https://www.securecoding.cert.org/confluence/display/cplusplus/DCL21-CPP.+Overloaded+postfix+increment+and+decrement+operators+should+return+a+const+object>`_.
diff --git a/docs/clang-tidy/checks/cert-dcl50-cpp.rst b/docs/clang-tidy/checks/cert-dcl50-cpp.rst
new file mode 100644 (file)
index 0000000..5fc1fbf
--- /dev/null
@@ -0,0 +1,11 @@
+.. title:: clang-tidy - cert-dcl50-cpp
+
+cert-dcl50-cpp
+==============
+
+This check flags all function definitions (but not declarations) of C-style
+variadic functions.
+
+This check corresponds to the CERT C++ Coding Standard rule
+`DCL50-CPP. Do not define a C-style variadic function
+<https://www.securecoding.cert.org/confluence/display/cplusplus/DCL50-CPP.+Do+not+define+a+C-style+variadic+function>`_.
diff --git a/docs/clang-tidy/checks/cert-dcl54-cpp.rst b/docs/clang-tidy/checks/cert-dcl54-cpp.rst
new file mode 100644 (file)
index 0000000..e0b575c
--- /dev/null
@@ -0,0 +1,10 @@
+.. title:: clang-tidy - cert-dcl54-cpp
+.. meta::
+   :http-equiv=refresh: 5;URL=misc-new-delete-overloads.html
+
+cert-dcl54-cpp
+==============
+
+The cert-dcl54-cpp check is an alias, please see
+`misc-new-delete-overloads <misc-new-delete-overloads.html>`_ for more
+information.
diff --git a/docs/clang-tidy/checks/cert-dcl58-cpp.rst b/docs/clang-tidy/checks/cert-dcl58-cpp.rst
new file mode 100644 (file)
index 0000000..a86d1b9
--- /dev/null
@@ -0,0 +1,21 @@
+.. title:: clang-tidy - cert-dcl58-cpp
+
+cert-dcl58-cpp
+==============
+
+Modification of the ``std`` or ``posix`` namespace can result in undefined
+behavior.
+This check warns for such modifications.
+
+Examples:
+
+.. code-block:: c++
+
+  namespace std {
+    int x; // May cause undefined behavior.
+  }
+
+
+This check corresponds to the CERT C++ Coding Standard rule
+`DCL58-CPP. Do not modify the standard namespaces
+<https://www.securecoding.cert.org/confluence/display/cplusplus/DCL58-CPP.+Do+not+modify+the+standard+namespaces>`_.
diff --git a/docs/clang-tidy/checks/cert-dcl59-cpp.rst b/docs/clang-tidy/checks/cert-dcl59-cpp.rst
new file mode 100644 (file)
index 0000000..9528c04
--- /dev/null
@@ -0,0 +1,9 @@
+.. title:: clang-tidy - cert-dcl59-cpp
+.. meta::
+   :http-equiv=refresh: 5;URL=google-build-namespaces.html
+
+cert-dcl59-cpp
+==============
+
+The cert-dcl59-cpp check is an alias, please see
+`google-build-namespaces <google-build-namespaces.html>`_ for more information.
diff --git a/docs/clang-tidy/checks/cert-env33-c.rst b/docs/clang-tidy/checks/cert-env33-c.rst
new file mode 100644 (file)
index 0000000..c5321b0
--- /dev/null
@@ -0,0 +1,13 @@
+.. title:: clang-tidy - cert-env33-c
+
+cert-env33-c
+============
+
+This check flags calls to ``system()``, ``popen()``, and ``_popen()``, which
+execute a command processor. It does not flag calls to ``system()`` with a null
+pointer argument, as such a call checks for the presence of a command processor
+but does not actually attempt to execute a command.
+
+This check corresponds to the CERT C Coding Standard rule
+`ENV33-C. Do not call system()
+<https://www.securecoding.cert.org/confluence/pages/viewpage.action?pageId=2130132>`_.
diff --git a/docs/clang-tidy/checks/cert-err09-cpp.rst b/docs/clang-tidy/checks/cert-err09-cpp.rst
new file mode 100644 (file)
index 0000000..1494bcc
--- /dev/null
@@ -0,0 +1,10 @@
+.. title:: clang-tidy - cert-err09-cpp
+.. meta::
+   :http-equiv=refresh: 5;URL=misc-throw-by-value-catch-by-reference.html
+
+cert-err09-cpp
+==============
+
+The cert-err09-cpp check is an alias, please see
+`misc-throw-by-value-catch-by-reference <misc-throw-by-value-catch-by-reference.html>`_
+for more information.
diff --git a/docs/clang-tidy/checks/cert-err34-c.rst b/docs/clang-tidy/checks/cert-err34-c.rst
new file mode 100644 (file)
index 0000000..362aef2
--- /dev/null
@@ -0,0 +1,28 @@
+.. title:: clang-tidy - cert-err34-c
+
+cert-err34-c
+============
+
+This check flags calls to string-to-number conversion functions that do not
+verify the validity of the conversion, such as ``atoi()`` or ``scanf()``. It
+does not flag calls to ``strtol()``, or other, related conversion functions that
+do perform better error checking.
+
+.. code-block:: c
+
+  #include <stdlib.h>
+
+  void func(const char *buff) {
+    int si;
+
+    if (buff) {
+      si = atoi(buff); /* 'atoi' used to convert a string to an integer, but function will
+                           not report conversion errors; consider using 'strtol' instead. */
+    } else {
+      /* Handle error */
+    }
+  }
+
+This check corresponds to the CERT C Coding Standard rule
+`ERR34-C. Detect errors when converting a string to a number
+<https://www.securecoding.cert.org/confluence/display/c/ERR34-C.+Detect+errors+when+converting+a+string+to+a+number>`_.
diff --git a/docs/clang-tidy/checks/cert-err52-cpp.rst b/docs/clang-tidy/checks/cert-err52-cpp.rst
new file mode 100644 (file)
index 0000000..a29dc7b
--- /dev/null
@@ -0,0 +1,10 @@
+.. title:: clang-tidy - cert-err52-cpp
+
+cert-err52-cpp
+==============
+
+This check flags all call expressions involving ``setjmp()`` and ``longjmp()``.
+
+This check corresponds to the CERT C++ Coding Standard rule
+`ERR52-CPP. Do not use setjmp() or longjmp()
+<https://www.securecoding.cert.org/confluence/pages/viewpage.action?pageId=1834>`_.
diff --git a/docs/clang-tidy/checks/cert-err58-cpp.rst b/docs/clang-tidy/checks/cert-err58-cpp.rst
new file mode 100644 (file)
index 0000000..6a7f615
--- /dev/null
@@ -0,0 +1,11 @@
+.. title:: clang-tidy - cert-err58-cpp
+
+cert-err58-cpp
+==============
+
+This check flags all ``static`` or ``thread_local`` variable declarations where
+the initializer for the object may throw an exception.
+
+This check corresponds to the CERT C++ Coding Standard rule
+`ERR58-CPP. Handle all exceptions thrown before main() begins executing
+<https://www.securecoding.cert.org/confluence/display/cplusplus/ERR58-CPP.+Handle+all+exceptions+thrown+before+main%28%29+begins+executing>`_.
diff --git a/docs/clang-tidy/checks/cert-err60-cpp.rst b/docs/clang-tidy/checks/cert-err60-cpp.rst
new file mode 100644 (file)
index 0000000..9fcb840
--- /dev/null
@@ -0,0 +1,11 @@
+.. title:: clang-tidy - cert-err60-cpp
+
+cert-err60-cpp
+==============
+
+This check flags all throw expressions where the exception object is not nothrow
+copy constructible.
+
+This check corresponds to the CERT C++ Coding Standard rule
+`ERR60-CPP. Exception objects must be nothrow copy constructible
+<https://www.securecoding.cert.org/confluence/display/cplusplus/ERR60-CPP.+Exception+objects+must+be+nothrow+copy+constructible>`_.
diff --git a/docs/clang-tidy/checks/cert-err61-cpp.rst b/docs/clang-tidy/checks/cert-err61-cpp.rst
new file mode 100644 (file)
index 0000000..f0cd0fe
--- /dev/null
@@ -0,0 +1,10 @@
+.. title:: clang-tidy - cert-err61-cpp
+.. meta::
+   :http-equiv=refresh: 5;URL=misc-throw-by-value-catch-by-reference.html
+
+cert-err61-cpp
+==============
+
+The cert-err61-cpp check is an alias, please see
+`misc-throw-by-value-catch-by-reference <misc-throw-by-value-catch-by-reference.html>`_
+for more information.
diff --git a/docs/clang-tidy/checks/cert-fio38-c.rst b/docs/clang-tidy/checks/cert-fio38-c.rst
new file mode 100644 (file)
index 0000000..5ce37f4
--- /dev/null
@@ -0,0 +1,10 @@
+.. title:: clang-tidy - cert-fio38-c
+.. meta::
+   :http-equiv=refresh: 5;URL=misc-non-copyable-objects.html
+
+cert-fio38-c
+============
+
+The cert-fio38-c check is an alias, please see
+`misc-non-copyable-objects <misc-non-copyable-objects.html>`_ for more
+information.
diff --git a/docs/clang-tidy/checks/cert-flp30-c.rst b/docs/clang-tidy/checks/cert-flp30-c.rst
new file mode 100644 (file)
index 0000000..c37b639
--- /dev/null
@@ -0,0 +1,11 @@
+.. title:: clang-tidy - cert-flp30-c
+
+cert-flp30-c
+============
+
+This check flags ``for`` loops where the induction expression has a
+floating-point type.
+
+This check corresponds to the CERT C Coding Standard rule
+`FLP30-C. Do not use floating-point variables as loop counters
+<https://www.securecoding.cert.org/confluence/display/c/FLP30-C.+Do+not+use+floating-point+variables+as+loop+counters>`_.
diff --git a/docs/clang-tidy/checks/cert-msc30-c.rst b/docs/clang-tidy/checks/cert-msc30-c.rst
new file mode 100644 (file)
index 0000000..afd9b1a
--- /dev/null
@@ -0,0 +1,9 @@
+.. title:: clang-tidy - cert-msc30-c
+.. meta::
+   :http-equiv=refresh: 5;URL=cert-msc50-cpp.html
+
+cert-msc30-c
+============
+
+The cert-msc30-c check is an alias, please see
+`cert-msc50-cpp <cert-msc50-cpp.html>`_ for more information.
diff --git a/docs/clang-tidy/checks/cert-msc50-cpp.rst b/docs/clang-tidy/checks/cert-msc50-cpp.rst
new file mode 100644 (file)
index 0000000..debf01c
--- /dev/null
@@ -0,0 +1,11 @@
+.. title:: clang-tidy - cert-msc50-cpp
+
+cert-msc50-cpp
+==============
+
+Pseudorandom number generators use mathematical algorithms to produce a sequence
+of numbers with good statistical properties, but the numbers produced are not
+genuinely random. The ``std::rand()`` function takes a seed (number), runs a
+mathematical operation on it and returns the result. By manipulating the seed
+the result can be predictable. This check warns for the usage of
+``std::rand()``.
diff --git a/docs/clang-tidy/checks/cert-oop11-cpp.rst b/docs/clang-tidy/checks/cert-oop11-cpp.rst
new file mode 100644 (file)
index 0000000..c824528
--- /dev/null
@@ -0,0 +1,10 @@
+.. title:: clang-tidy - cert-oop11-cpp\r
+.. meta::\r
+   :http-equiv=refresh: 5;URL=misc-move-constructor-init.html\r
+\r
+cert-oop11-cpp\r
+==============\r
+\r
+The cert-oop11-cpp check is an alias, please see\r
+`misc-move-constructor-init <misc-move-constructor-init.html>`_ for more\r
+information.\r
diff --git a/docs/clang-tidy/checks/cppcoreguidelines-interfaces-global-init.rst b/docs/clang-tidy/checks/cppcoreguidelines-interfaces-global-init.rst
new file mode 100644 (file)
index 0000000..4907858
--- /dev/null
@@ -0,0 +1,14 @@
+.. title:: clang-tidy - cppcoreguidelines-interfaces-global-init
+
+cppcoreguidelines-interfaces-global-init
+========================================
+
+This check flags initializers of globals that access extern objects,
+and therefore can lead to order-of-initialization problems.
+
+This rule is part of the "Interfaces" profile of the C++ Core Guidelines, see
+https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Ri-global-init
+
+Note that currently this does not flag calls to non-constexpr functions, and
+therefore globals could still be accessed from functions themselves.
+
diff --git a/docs/clang-tidy/checks/cppcoreguidelines-no-malloc.rst b/docs/clang-tidy/checks/cppcoreguidelines-no-malloc.rst
new file mode 100644 (file)
index 0000000..d481a67
--- /dev/null
@@ -0,0 +1,46 @@
+.. title:: clang-tidy - cppcoreguidelines-no-malloc
+
+cppcoreguidelines-no-malloc
+===========================
+
+This check handles C-Style memory management using ``malloc()``, ``realloc()``,
+``calloc()`` and ``free()``. It warns about its use and tries to suggest the use
+of an appropriate RAII object.
+Furthermore, it can be configured to check against a user-specified list of functions 
+that are used for memory management (e.g. ``posix_memalign()``).
+See `C++ Core Guidelines <https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Rr-mallocfree>`_.
+
+There is no attempt made to provide fix-it hints, since manual resource
+management isn't easily transformed automatically into RAII.
+
+.. code-block:: c++
+
+  // Warns each of the following lines.
+  // Containers like std::vector or std::string should be used.
+  char* some_string = (char*) malloc(sizeof(char) * 20);
+  char* some_string = (char*) realloc(sizeof(char) * 30);
+  free(some_string);
+
+  int* int_array = (int*) calloc(30, sizeof(int));
+
+  // Rather use a smartpointer or stack variable.
+  struct some_struct* s = (struct some_struct*) malloc(sizeof(struct some_struct));
+
+Options
+-------
+
+.. option:: Allocations
+
+   Semicolon-separated list of fully qualified names of memory allocation functions. 
+   Defaults to ``::malloc;::calloc``.
+
+.. option:: Deallocations
+
+   Semicolon-separated list of fully qualified names of memory allocation functions. 
+   Defaults to ``::free``.
+
+.. option:: Reallocations
+
+   Semicolon-separated list of fully qualified names of memory allocation functions. 
+   Defaults to ``::realloc``.
+
diff --git a/docs/clang-tidy/checks/cppcoreguidelines-pro-bounds-array-to-pointer-decay.rst b/docs/clang-tidy/checks/cppcoreguidelines-pro-bounds-array-to-pointer-decay.rst
new file mode 100644 (file)
index 0000000..172df2b
--- /dev/null
@@ -0,0 +1,12 @@
+.. title:: clang-tidy - cppcoreguidelines-pro-bounds-array-to-pointer-decay
+
+cppcoreguidelines-pro-bounds-array-to-pointer-decay
+===================================================
+
+This check flags all array to pointer decays.
+
+Pointers should not be used as arrays. ``span<T>`` is a bounds-checked, safe
+alternative to using pointers to access arrays.
+
+This rule is part of the "Bounds safety" profile of the C++ Core Guidelines, see
+https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Pro-bounds-decay.
diff --git a/docs/clang-tidy/checks/cppcoreguidelines-pro-bounds-constant-array-index.rst b/docs/clang-tidy/checks/cppcoreguidelines-pro-bounds-constant-array-index.rst
new file mode 100644 (file)
index 0000000..4528f2a
--- /dev/null
@@ -0,0 +1,25 @@
+.. title:: clang-tidy - cppcoreguidelines-pro-bounds-constant-array-index
+
+cppcoreguidelines-pro-bounds-constant-array-index
+=================================================
+
+This check flags all array subscript expressions on static arrays and
+``std::arrays`` that either do not have a constant integer expression index or
+are out of bounds (for ``std::array``). For out-of-bounds checking of static
+arrays, see the `-Warray-bounds` Clang diagnostic.
+
+This rule is part of the "Bounds safety" profile of the C++ Core Guidelines, see
+https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Pro-bounds-arrayindex.
+
+Options
+-------
+
+.. option:: GslHeader
+
+   The check can generate fixes after this option has been set to the name of
+   the include file that contains ``gsl::at()``, e.g. `"gsl/gsl.h"`.
+
+.. option:: IncludeStyle
+
+   A string specifying which include-style is used, `llvm` or `google`. Default
+   is `llvm`.
diff --git a/docs/clang-tidy/checks/cppcoreguidelines-pro-bounds-pointer-arithmetic.rst b/docs/clang-tidy/checks/cppcoreguidelines-pro-bounds-pointer-arithmetic.rst
new file mode 100644 (file)
index 0000000..e0660df
--- /dev/null
@@ -0,0 +1,14 @@
+.. title:: clang-tidy - cppcoreguidelines-pro-bounds-pointer-arithmetic
+
+cppcoreguidelines-pro-bounds-pointer-arithmetic
+===============================================
+
+This check flags all usage of pointer arithmetic, because it could lead to an
+invalid pointer. Subtraction of two pointers is not flagged by this check.
+
+Pointers should only refer to single objects, and pointer arithmetic is fragile
+and easy to get wrong. ``span<T>`` is a bounds-checked, safe type for accessing
+arrays of data.
+
+This rule is part of the "Bounds safety" profile of the C++ Core Guidelines, see
+https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Pro-bounds-arithmetic.
diff --git a/docs/clang-tidy/checks/cppcoreguidelines-pro-type-const-cast.rst b/docs/clang-tidy/checks/cppcoreguidelines-pro-type-const-cast.rst
new file mode 100644 (file)
index 0000000..f3f0fb4
--- /dev/null
@@ -0,0 +1,12 @@
+.. title:: clang-tidy - cppcoreguidelines-pro-type-const-cast
+
+cppcoreguidelines-pro-type-const-cast
+=====================================
+
+This check flags all uses of ``const_cast`` in C++ code.
+
+Modifying a variable that was declared const is undefined behavior, even with
+``const_cast``.
+
+This rule is part of the "Type safety" profile of the C++ Core Guidelines, see
+https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Pro-type-constcast.
diff --git a/docs/clang-tidy/checks/cppcoreguidelines-pro-type-cstyle-cast.rst b/docs/clang-tidy/checks/cppcoreguidelines-pro-type-cstyle-cast.rst
new file mode 100644 (file)
index 0000000..1c21dd0
--- /dev/null
@@ -0,0 +1,18 @@
+.. title:: clang-tidy - cppcoreguidelines-pro-type-cstyle-cast
+
+cppcoreguidelines-pro-type-cstyle-cast
+======================================
+
+This check flags all use of C-style casts that perform a ``static_cast``
+downcast, ``const_cast``, or ``reinterpret_cast``.
+
+Use of these casts can violate type safety and cause the program to access a
+variable that is actually of type X to be accessed as if it were of an unrelated
+type Z. Note that a C-style ``(T)expression`` cast means to perform the first of
+the following that is possible: a ``const_cast``, a ``static_cast``, a
+``static_cast`` followed by a ``const_cast``, a ``reinterpret_cast``, or a
+``reinterpret_cast`` followed by a ``const_cast``. This rule bans
+``(T)expression`` only when used to perform an unsafe cast.
+
+This rule is part of the "Type safety" profile of the C++ Core Guidelines, see
+https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Pro-type-cstylecast.
diff --git a/docs/clang-tidy/checks/cppcoreguidelines-pro-type-member-init.rst b/docs/clang-tidy/checks/cppcoreguidelines-pro-type-member-init.rst
new file mode 100644 (file)
index 0000000..2fdb4e3
--- /dev/null
@@ -0,0 +1,38 @@
+.. title:: clang-tidy - cppcoreguidelines-pro-type-member-init
+
+cppcoreguidelines-pro-type-member-init
+======================================
+
+The check flags user-defined constructor definitions that do not
+initialize all fields that would be left in an undefined state by
+default construction, e.g. builtins, pointers and record types without
+user-provided default constructors containing at least one such
+type. If these fields aren't initialized, the constructor will leave
+some of the memory in an undefined state.
+
+For C++11 it suggests fixes to add in-class field initializers. For
+older versions it inserts the field initializers into the constructor
+initializer list. It will also initialize any direct base classes that
+need to be zeroed in the constructor initializer list.
+
+The check takes assignment of fields in the constructor body into
+account but generates false positives for fields initialized in
+methods invoked in the constructor body.
+
+The check also flags variables with automatic storage duration that have record
+types without a user-provided constructor and are not initialized. The suggested
+fix is to zero initialize the variable via ``{}`` for C++11 and beyond or ``=
+{}`` for older language versions.
+
+Options
+-------
+
+.. option:: IgnoreArrays
+
+   If set to non-zero, the check will not warn about array members that are not
+   zero-initialized during construction. For performance critical code, it may
+   be important to not initialize fixed-size array members. Default is `0`.
+
+This rule is part of the "Type safety" profile of the C++ Core
+Guidelines, corresponding to rule Type.6. See
+https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Pro-type-memberinit.
diff --git a/docs/clang-tidy/checks/cppcoreguidelines-pro-type-reinterpret-cast.rst b/docs/clang-tidy/checks/cppcoreguidelines-pro-type-reinterpret-cast.rst
new file mode 100644 (file)
index 0000000..2ef76f2
--- /dev/null
@@ -0,0 +1,13 @@
+.. title:: clang-tidy - cppcoreguidelines-pro-type-reinterpret-cast
+
+cppcoreguidelines-pro-type-reinterpret-cast
+===========================================
+
+This check flags all uses of ``reinterpret_cast`` in C++ code.
+
+Use of these casts can violate type safety and cause the program to access a
+variable that is actually of type ``X`` to be accessed as if it were of an
+unrelated type ``Z``.
+
+This rule is part of the "Type safety" profile of the C++ Core Guidelines, see
+https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Pro-type-reinterpretcast.
diff --git a/docs/clang-tidy/checks/cppcoreguidelines-pro-type-static-cast-downcast.rst b/docs/clang-tidy/checks/cppcoreguidelines-pro-type-static-cast-downcast.rst
new file mode 100644 (file)
index 0000000..880b8b0
--- /dev/null
@@ -0,0 +1,15 @@
+.. title:: clang-tidy - cppcoreguidelines-pro-type-static-cast-downcast
+
+cppcoreguidelines-pro-type-static-cast-downcast
+===============================================
+
+This check flags all usages of ``static_cast``, where a base class is casted to
+a derived class. In those cases, a fix-it is provided to convert the cast to a
+``dynamic_cast``.
+
+Use of these casts can violate type safety and cause the program to access a
+variable that is actually of type ``X`` to be accessed as if it were of an
+unrelated type ``Z``.
+
+This rule is part of the "Type safety" profile of the C++ Core Guidelines, see
+https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Pro-type-downcast.
diff --git a/docs/clang-tidy/checks/cppcoreguidelines-pro-type-union-access.rst b/docs/clang-tidy/checks/cppcoreguidelines-pro-type-union-access.rst
new file mode 100644 (file)
index 0000000..cdcf713
--- /dev/null
@@ -0,0 +1,16 @@
+.. title:: clang-tidy - cppcoreguidelines-pro-type-union-access
+
+cppcoreguidelines-pro-type-union-access
+=======================================
+
+This check flags all access to members of unions. Passing unions as a whole is
+not flagged.
+
+Reading from a union member assumes that member was the last one written, and
+writing to a union member assumes another member with a nontrivial destructor
+had its destructor called. This is fragile because it cannot generally be
+enforced to be safe in the language and so relies on programmer discipline to
+get it right.
+
+This rule is part of the "Type safety" profile of the C++ Core Guidelines, see
+https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Pro-type-unions.
diff --git a/docs/clang-tidy/checks/cppcoreguidelines-pro-type-vararg.rst b/docs/clang-tidy/checks/cppcoreguidelines-pro-type-vararg.rst
new file mode 100644 (file)
index 0000000..26fcd0c
--- /dev/null
@@ -0,0 +1,17 @@
+.. title:: clang-tidy - cppcoreguidelines-pro-type-vararg
+
+cppcoreguidelines-pro-type-vararg
+=================================
+
+This check flags all calls to c-style vararg functions and all use of
+``va_arg``.
+
+To allow for SFINAE use of vararg functions, a call is not flagged if a literal
+0 is passed as the only vararg argument.
+
+Passing to varargs assumes the correct type will be read. This is fragile
+because it cannot generally be enforced to be safe in the language and so relies
+on programmer discipline to get it right.
+
+This rule is part of the "Type safety" profile of the C++ Core Guidelines, see
+https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Pro-type-varargs.
diff --git a/docs/clang-tidy/checks/cppcoreguidelines-slicing.rst b/docs/clang-tidy/checks/cppcoreguidelines-slicing.rst
new file mode 100644 (file)
index 0000000..44f3dca
--- /dev/null
@@ -0,0 +1,25 @@
+.. title:: clang-tidy - cppcoreguidelines-slicing
+
+cppcoreguidelines-slicing
+=========================
+
+Flags slicing of member variables or vtable. Slicing happens when copying a
+derived object into a base object: the members of the derived object (both
+member variables and virtual member functions) will be discarded. This can be
+misleading especially for member function slicing, for example:
+
+.. code-block:: c++
+
+  struct B { int a; virtual int f(); };
+  struct D : B { int b; int f() override; };
+
+  void use(B b) {  // Missing reference, intended?
+    b.f();  // Calls B::f.
+  }
+
+  D d;
+  use(d);  // Slice.
+
+See the relevant C++ Core Guidelines sections for details:
+https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#es63-dont-slice
+https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#c145-access-polymorphic-objects-through-pointers-and-references
diff --git a/docs/clang-tidy/checks/cppcoreguidelines-special-member-functions.rst b/docs/clang-tidy/checks/cppcoreguidelines-special-member-functions.rst
new file mode 100644 (file)
index 0000000..28a5e8e
--- /dev/null
@@ -0,0 +1,49 @@
+.. title:: clang-tidy - cppcoreguidelines-special-member-functions
+
+cppcoreguidelines-special-member-functions
+==========================================
+
+The check finds classes where some but not all of the special member functions
+are defined.
+
+By default the compiler defines a copy constructor, copy assignment operator,
+move constructor, move assignment operator and destructor. The default can be
+suppressed by explicit user-definitions. The relationship between which
+functions will be suppressed by definitions of other functions is complicated
+and it is advised that all five are defaulted or explicitly defined.
+
+Note that defining a function with ``= delete`` is considered to be a
+definition.
+
+This rule is part of the "Constructors, assignments, and destructors" profile of the C++ Core
+Guidelines, corresponding to rule C.21. See
+
+https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#c21-if-you-define-or-delete-any-default-operation-define-or-delete-them-all.
+
+Options
+-------
+
+.. option:: AllowSoleDefaultDtor
+
+   When set to `1` (default is `0`), this check doesn't flag classes with a sole, explicitly
+   defaulted destructor. An example for such a class is:
+   
+   .. code-block:: c++
+   
+     struct A {
+       virtual ~A() = default;
+     };
+   
+.. option:: AllowMissingMoveFunctions
+
+   When set to `1` (default is `0`), this check doesn't flag classes which define no move
+   operations at all. It still flags classes which define only one of either
+   move constructor or move assignment operator. With this option enabled, the following class won't be flagged:
+   
+   .. code-block:: c++
+   
+     struct A {
+       A(const A&);
+       A& operator=(const A&);
+       ~A();
+     }
diff --git a/docs/clang-tidy/checks/google-build-explicit-make-pair.rst b/docs/clang-tidy/checks/google-build-explicit-make-pair.rst
new file mode 100644 (file)
index 0000000..e3e9eeb
--- /dev/null
@@ -0,0 +1,11 @@
+.. title:: clang-tidy - google-build-explicit-make-pair
+
+google-build-explicit-make-pair
+===============================
+
+Check that ``make_pair``'s template arguments are deduced.
+
+G++ 4.6 in C++11 mode fails badly if ``make_pair``'s template arguments are
+specified explicitly, and such use isn't intended in any case.
+
+Corresponding cpplint.py check name: `build/explicit_make_pair`.
diff --git a/docs/clang-tidy/checks/google-build-namespaces.rst b/docs/clang-tidy/checks/google-build-namespaces.rst
new file mode 100644 (file)
index 0000000..69d124d
--- /dev/null
@@ -0,0 +1,23 @@
+.. title:: clang-tidy - google-build-namespaces
+
+google-build-namespaces
+=======================
+
+`cert-dcl59-cpp` redirects here as an alias for this check.
+
+Finds anonymous namespaces in headers.
+
+https://google.github.io/styleguide/cppguide.html#Namespaces
+
+Corresponding cpplint.py check name: `build/namespaces`.
+
+Options
+-------
+
+.. option:: HeaderFileExtensions
+
+   A comma-separated list of filename extensions of header files (the filename
+   extensions should not include "." prefix). Default is "h,hh,hpp,hxx".
+   For header files without an extension, use an empty string (if there are no
+   other desired extensions) or leave an empty element in the list. e.g.,
+   "h,hh,hpp,hxx," (note the trailing comma).
diff --git a/docs/clang-tidy/checks/google-build-using-namespace.rst b/docs/clang-tidy/checks/google-build-using-namespace.rst
new file mode 100644 (file)
index 0000000..f01bfed
--- /dev/null
@@ -0,0 +1,19 @@
+.. title:: clang-tidy - google-build-using-namespace
+
+google-build-using-namespace
+============================
+
+Finds ``using namespace`` directives.
+
+The check implements the following rule of the
+`Google C++ Style Guide <https://google.github.io/styleguide/cppguide.html#Namespaces>`_:
+
+  You may not use a using-directive to make all names from a namespace
+  available.
+
+  .. code-block:: c++
+
+    // Forbidden -- This pollutes the namespace.
+    using namespace foo;
+
+Corresponding cpplint.py check name: `build/namespaces`.
diff --git a/docs/clang-tidy/checks/google-default-arguments.rst b/docs/clang-tidy/checks/google-default-arguments.rst
new file mode 100644 (file)
index 0000000..c02099c
--- /dev/null
@@ -0,0 +1,8 @@
+.. title:: clang-tidy - google-default-arguments
+
+google-default-arguments
+========================
+
+Checks that default arguments are not given for virtual methods.
+
+See https://google.github.io/styleguide/cppguide.html#Default_Arguments
diff --git a/docs/clang-tidy/checks/google-explicit-constructor.rst b/docs/clang-tidy/checks/google-explicit-constructor.rst
new file mode 100644 (file)
index 0000000..acafc1a
--- /dev/null
@@ -0,0 +1,56 @@
+.. title:: clang-tidy - google-explicit-constructor
+
+google-explicit-constructor
+===========================
+
+
+Checks that constructors callable with a single argument and conversion
+operators are marked explicit to avoid the risk of unintentional implicit
+conversions.
+
+Consider this example:
+
+.. code-block:: c++
+
+  struct S {
+    int x;
+    operator bool() const { return true; }
+  };
+
+  bool f() {
+    S a{1};
+    S b{2};
+    return a == b;
+  }
+
+The function will return ``true``, since the objects are implicitly converted to
+``bool`` before comparison, which is unlikely to be the intent.
+
+The check will suggest inserting ``explicit`` before the constructor or
+conversion operator declaration. However, copy and move constructors should not
+be explicit, as well as constructors taking a single ``initializer_list``
+argument.
+
+This code:
+
+.. code-block:: c++
+
+  struct S {
+    S(int a);
+    explicit S(const S&);
+    operator bool() const;
+    ...
+
+will become
+
+.. code-block:: c++
+
+  struct S {
+    explicit S(int a);
+    S(const S&);
+    explicit operator bool() const;
+    ...
+
+
+
+See https://google.github.io/styleguide/cppguide.html#Explicit_Constructors
diff --git a/docs/clang-tidy/checks/google-global-names-in-headers.rst b/docs/clang-tidy/checks/google-global-names-in-headers.rst
new file mode 100644 (file)
index 0000000..88ba906
--- /dev/null
@@ -0,0 +1,21 @@
+.. title:: clang-tidy - google-global-names-in-headers
+
+google-global-names-in-headers
+==============================
+
+Flag global namespace pollution in header files. Right now it only triggers on
+``using`` declarations and directives.
+
+The relevant style guide section is
+https://google.github.io/styleguide/cppguide.html#Namespaces.
+
+Options
+-------
+
+.. option:: HeaderFileExtensions
+
+   A comma-separated list of filename extensions of header files (the filename
+   extensions should not contain "." prefix). Default is "h".
+   For header files without an extension, use an empty string (if there are no
+   other desired extensions) or leave an empty element in the list. e.g.,
+   "h,hh,hpp,hxx," (note the trailing comma).
diff --git a/docs/clang-tidy/checks/google-readability-braces-around-statements.rst b/docs/clang-tidy/checks/google-readability-braces-around-statements.rst
new file mode 100644 (file)
index 0000000..e5c8eb6
--- /dev/null
@@ -0,0 +1,10 @@
+.. title:: clang-tidy - google-readability-braces-around-statements
+.. meta::
+   :http-equiv=refresh: 5;URL=readability-braces-around-statements.html
+
+google-readability-braces-around-statements
+===========================================
+
+The google-readability-braces-around-statements check is an alias, please see
+`readability-braces-around-statements <readability-braces-around-statements.html>`_
+for more information.
diff --git a/docs/clang-tidy/checks/google-readability-casting.rst b/docs/clang-tidy/checks/google-readability-casting.rst
new file mode 100644 (file)
index 0000000..4c9d1bc
--- /dev/null
@@ -0,0 +1,14 @@
+.. title:: clang-tidy - google-readability-casting
+
+google-readability-casting
+==========================
+
+Finds usages of C-style casts.
+
+https://google.github.io/styleguide/cppguide.html#Casting
+
+Corresponding cpplint.py check name: `readability/casting`.
+
+This check is similar to `-Wold-style-cast`, but it suggests automated fixes
+in some cases. The reported locations should not be different from the
+ones generated by `-Wold-style-cast`.
diff --git a/docs/clang-tidy/checks/google-readability-function-size.rst b/docs/clang-tidy/checks/google-readability-function-size.rst
new file mode 100644 (file)
index 0000000..b454628
--- /dev/null
@@ -0,0 +1,10 @@
+.. title:: clang-tidy - google-readability-function-size
+.. meta::
+   :http-equiv=refresh: 5;URL=readability-function-size.html
+
+google-readability-function-size
+================================
+
+The google-readability-function-size check is an alias, please see
+`readability-function-size <readability-function-size.html>`_ for more
+information.
diff --git a/docs/clang-tidy/checks/google-readability-namespace-comments.rst b/docs/clang-tidy/checks/google-readability-namespace-comments.rst
new file mode 100644 (file)
index 0000000..258a305
--- /dev/null
@@ -0,0 +1,9 @@
+.. title:: clang-tidy - google-readability-namespace-comments
+.. meta::
+   :http-equiv=refresh: 5;URL=llvm-namespace-comment.html
+
+google-readability-namespace-comments
+=====================================
+
+The google-readability-namespace-comments check is an alias, please see
+`llvm-namespace-comment <llvm-namespace-comment.html>`_ for more information.
diff --git a/docs/clang-tidy/checks/google-readability-redundant-smartptr-get.rst b/docs/clang-tidy/checks/google-readability-redundant-smartptr-get.rst
new file mode 100644 (file)
index 0000000..e77fd64
--- /dev/null
@@ -0,0 +1,10 @@
+.. title:: clang-tidy - google-readability-redundant-smartptr-get
+.. meta::
+   :http-equiv=refresh: 5;URL=readability-redundant-smartptr-get.html
+
+google-readability-redundant-smartptr-get
+=========================================
+
+The google-readability-redundant-smartptr-get check is an alias, please see
+`readability-redundant-smartptr-get <readability-redundant-smartptr-get.html>`_
+for more information.
diff --git a/docs/clang-tidy/checks/google-readability-todo.rst b/docs/clang-tidy/checks/google-readability-todo.rst
new file mode 100644 (file)
index 0000000..159d2b4
--- /dev/null
@@ -0,0 +1,11 @@
+.. title:: clang-tidy - google-readability-todo
+
+google-readability-todo
+=======================
+
+Finds TODO comments without a username or bug number.
+
+The relevant style guide section is
+https://google.github.io/styleguide/cppguide.html#TODO_Comments.
+
+Corresponding cpplint.py check: `readability/todo`
diff --git a/docs/clang-tidy/checks/google-runtime-int.rst b/docs/clang-tidy/checks/google-runtime-int.rst
new file mode 100644 (file)
index 0000000..5e878a4
--- /dev/null
@@ -0,0 +1,27 @@
+.. title:: clang-tidy - google-runtime-int
+
+google-runtime-int
+==================
+
+Finds uses of ``short``, ``long`` and ``long long`` and suggest replacing them
+with ``u?intXX(_t)?``.
+
+The corresponding style guide rule:
+https://google.github.io/styleguide/cppguide.html#Integer_Types.
+
+Correspondig cpplint.py check: `runtime/int`.
+
+Options
+-------
+
+.. option:: UnsignedTypePrefix
+
+   A string specifying the unsigned type prefix. Default is `uint`.
+
+.. option:: SignedTypePrefix
+
+   A string specifying the signed type prefix. Default is `int`.
+
+.. option:: TypeSuffix
+
+   A string specifying the type suffix. Default is an empty string.
diff --git a/docs/clang-tidy/checks/google-runtime-member-string-references.rst b/docs/clang-tidy/checks/google-runtime-member-string-references.rst
new file mode 100644 (file)
index 0000000..bc4f0ee
--- /dev/null
@@ -0,0 +1,25 @@
+.. title:: clang-tidy - google-runtime-member-string-references
+
+google-runtime-member-string-references
+=======================================
+
+Finds members of type ``const string&``.
+
+const string reference members are generally considered unsafe as they can be
+created from a temporary quite easily.
+
+.. code-block:: c++
+
+  struct S {
+    S(const string &Str) : Str(Str) {}
+    const string &Str;
+  };
+  S instance("string");
+
+In the constructor call a string temporary is created from ``const char *`` and
+destroyed immediately after the call. This leaves around a dangling reference.
+
+This check emit warnings for both ``std::string`` and ``::string`` const
+reference members.
+
+Corresponding cpplint.py check name: `runtime/member_string_reference`.
diff --git a/docs/clang-tidy/checks/google-runtime-operator.rst b/docs/clang-tidy/checks/google-runtime-operator.rst
new file mode 100644 (file)
index 0000000..67f2993
--- /dev/null
@@ -0,0 +1,10 @@
+.. title:: clang-tidy - google-runtime-operator
+
+google-runtime-operator
+=======================
+
+Finds overloads of unary ``operator &``.
+
+https://google.github.io/styleguide/cppguide.html#Operator_Overloading
+
+Corresponding cpplint.py check name: `runtime/operator`.
diff --git a/docs/clang-tidy/checks/google-runtime-references.rst b/docs/clang-tidy/checks/google-runtime-references.rst
new file mode 100644 (file)
index 0000000..a210ccc
--- /dev/null
@@ -0,0 +1,17 @@
+.. title:: clang-tidy - google-runtime-references
+
+google-runtime-references
+=========================
+
+Checks the usage of non-constant references in function parameters.
+
+The corresponding style guide rule:
+https://google.github.io/styleguide/cppguide.html#Reference_Arguments
+
+
+Options
+-------
+
+.. option:: WhiteListTypes
+
+   A semicolon-separated list of names of whitelist types. Default is empty.
diff --git a/docs/clang-tidy/checks/hicpp-explicit-conversions.rst b/docs/clang-tidy/checks/hicpp-explicit-conversions.rst
new file mode 100644 (file)
index 0000000..1779473
--- /dev/null
@@ -0,0 +1,15 @@
+.. title:: clang-tidy - hicpp-explicit-conversions
+
+hicpp-explicit-conversions
+==========================
+
+This check is an alias for `google-explicit-constructor <google-explicit-constructor.hml>`_.
+Used to enforce parts of `rule 5.4.1 <http://www.codingstandard.com/rule/5-4-1-only-use-casting-forms-static_cast-excl-void-dynamic_cast-or-explicit-constructor-call/>`_.
+This check will enforce that constructors and conversion operators are marked `explicit`.
+Other forms of casting checks are implemented in other places.
+The following checks can be used to check for more forms of casting:
+
+- `cppcoreguidelines-pro-type-static-cast-downcast <cppcoreguidelines-pro-type-static-cast-downcast.html>`_
+- `cppcoreguidelines-pro-type-reinterpret-cast <cppcoreguidelines-pro-type-reinterpret-cast.html>`_
+- `cppcoreguidelines-pro-type-const-cast <cppcoreguidelines-pro-type-const-cast.html>`_ 
+- `cppcoreguidelines-pro-type-cstyle-cast <cppcoreguidelines-pro-type-cstyle-cast.html>`_
diff --git a/docs/clang-tidy/checks/hicpp-function-size.rst b/docs/clang-tidy/checks/hicpp-function-size.rst
new file mode 100644 (file)
index 0000000..b82a722
--- /dev/null
@@ -0,0 +1,11 @@
+.. title:: clang-tidy - hicpp-function-size
+
+hicpp-function-size
+===================
+
+This check is an alias for `readability-function-size <readability-function-size.hml>`_.
+Useful to enforce multiple sections on function complexity.
+
+- `rule 8.2.2 <http://www.codingstandard.com/rule/8-2-2-do-not-declare-functions-with-an-excessive-number-of-parameters/>`_
+- `rule 8.3.1 <http://www.codingstandard.com/rule/8-3-1-do-not-write-functions-with-an-excessive-mccabe-cyclomatic-complexity/>`_
+- `rule 8.3.2 <http://www.codingstandard.com/rule/8-3-2-do-not-write-functions-with-a-high-static-program-path-count/>`_
diff --git a/docs/clang-tidy/checks/hicpp-invalid-access-moved.rst b/docs/clang-tidy/checks/hicpp-invalid-access-moved.rst
new file mode 100644 (file)
index 0000000..d016eae
--- /dev/null
@@ -0,0 +1,8 @@
+.. title:: clang-tidy - hicpp-invalid-access-moved
+
+hicpp-invalid-access-moved
+==========================
+
+This check is an alias for `misc-use-after-move <misc-use-after-move.hml>`_.
+
+Implements parts of the `rule 8.4.1 <http://www.codingstandard.com/rule/8-4-1-do-not-access-an-invalid-object-or-an-object-with-indeterminate-value/>`_ to check if moved-from objects are accessed.
diff --git a/docs/clang-tidy/checks/hicpp-member-init.rst b/docs/clang-tidy/checks/hicpp-member-init.rst
new file mode 100644 (file)
index 0000000..eb948ae
--- /dev/null
@@ -0,0 +1,9 @@
+.. title:: clang-tidy - hicpp-member-init
+
+hicpp-member-init
+=================
+
+This check is an alias for `cppcoreguidelines-pro-type-member-init <cppcoreguidelines-pro-type-member-init.hml>`_.
+Implements the check for 
+`rule 12.4.2 <http://www.codingstandard.com/rule/12-4-2-ensure-that-a-constructor-initializes-explicitly-all-base-classes-and-non-static-data-members/>`_ 
+to initialize class members in the right order.
diff --git a/docs/clang-tidy/checks/hicpp-named-parameter.rst b/docs/clang-tidy/checks/hicpp-named-parameter.rst
new file mode 100644 (file)
index 0000000..d848d07
--- /dev/null
@@ -0,0 +1,8 @@
+.. title:: clang-tidy - hicpp-named-parameter
+
+hicpp-named-parameter
+=====================
+
+This check is an alias for `readability-named-parameter <readability-named-parameter.hml>`_.
+
+Implements `rule 8.2.1 <http://www.codingstandard.com/rule/8-2-1-make-parameter-names-absent-or-identical-in-all-declarations/>`_.
diff --git a/docs/clang-tidy/checks/hicpp-new-delete-operators.rst b/docs/clang-tidy/checks/hicpp-new-delete-operators.rst
new file mode 100644 (file)
index 0000000..8994644
--- /dev/null
@@ -0,0 +1,8 @@
+.. title:: clang-tidy - hicpp-new-delete-operators
+
+hicpp-new-delete-operators
+==========================
+
+This check is an alias for `misc-new-delete-overloads <misc-new-delete-overloads.hml>`_.
+Implements `rule 12.3.1 <http://www.codingstandard.com/section/12-3-free-store/>`_ to ensure
+the `new` and `delete` operators have the correct signature.
diff --git a/docs/clang-tidy/checks/hicpp-no-assembler.rst b/docs/clang-tidy/checks/hicpp-no-assembler.rst
new file mode 100644 (file)
index 0000000..8295895
--- /dev/null
@@ -0,0 +1,10 @@
+.. title:: clang-tidy - hicpp-no-assembler
+
+hicpp-no-assembler
+===================
+
+Check for assembler statements. No fix is offered.
+
+Inline assembler is forbidden by the `High Intergrity C++ Coding Standard
+<http://www.codingstandard.com/section/7-5-the-asm-declaration/>`_ 
+as it restricts the portability of code.
diff --git a/docs/clang-tidy/checks/hicpp-noexcept-move.rst b/docs/clang-tidy/checks/hicpp-noexcept-move.rst
new file mode 100644 (file)
index 0000000..0f4505f
--- /dev/null
@@ -0,0 +1,7 @@
+.. title:: clang-tidy - hicpp-noexcept-move
+
+hicpp-noexcept-move
+===================
+
+This check is an alias for `misc-noexcept-moveconstructor <misc-noexcept-moveconstructor.hml>`_.
+Checks `rule 12.5.4 <http://www.codingstandard.com/rule/12-5-4-declare-noexcept-the-move-constructor-and-move-assignment-operator>`_ to mark move assignment and move construction `noexcept`.
diff --git a/docs/clang-tidy/checks/hicpp-special-member-functions.rst b/docs/clang-tidy/checks/hicpp-special-member-functions.rst
new file mode 100644 (file)
index 0000000..b50c934
--- /dev/null
@@ -0,0 +1,7 @@
+.. title:: clang-tidy - hicpp-special-member-functions
+
+hicpp-special-member-functions
+==============================
+
+This check is an alias for `cppcoreguidelines-special-member-functions <cppcoreguidelines-special-member-functions.hml>`_.
+Checks that special member functions have the correct signature, according to `rule 12.5.7 <http://www.codingstandard.com/rule/12-5-7-declare-assignment-operators-with-the-ref-qualifier/>`_.
diff --git a/docs/clang-tidy/checks/hicpp-undelegated-constructor.rst b/docs/clang-tidy/checks/hicpp-undelegated-constructor.rst
new file mode 100644 (file)
index 0000000..d05d762
--- /dev/null
@@ -0,0 +1,23 @@
+.. title:: clang-tidy - hicpp-undelegated-construtor
+
+hicpp-undelegated-constructor
+=============================
+
+This check is an alias for `misc-undelegated-constructor <misc-undelegated-constructor.hml>`_.
+Partially implements `rule 12.4.5 <http://www.codingstandard.com/rule/12-4-5-use-delegating-constructors-to-reduce-code-duplication/>`_ 
+to find misplaced constructor calls inside a constructor.
+
+.. code-block:: c++
+
+  struct Ctor {
+    Ctor();
+    Ctor(int);
+    Ctor(int, int);
+    Ctor(Ctor *i) {
+      // All Ctor() calls result in a temporary object
+      Ctor(); // did you intend to call a delegated constructor? 
+      Ctor(0); // did you intend to call a delegated constructor?
+      Ctor(1, 2); // did you intend to call a delegated constructor?
+      foo();
+    }
+  };
diff --git a/docs/clang-tidy/checks/hicpp-use-equals-default.rst b/docs/clang-tidy/checks/hicpp-use-equals-default.rst
new file mode 100644 (file)
index 0000000..380ecfc
--- /dev/null
@@ -0,0 +1,7 @@
+.. title:: clang-tidy - hicpp-use-equals-defaults
+
+hicpp-use-equals-default
+========================
+
+This check is an alias for `modernize-use-equals-default <modernize-use-equals-default.hml>`_.
+Implements `rule 12.5.1 <http://www.codingstandard.com/rule/12-5-1-define-explicitly-default-or-delete-implicit-special-member-functions-of-concrete-classes/>`_ to explicitly default special member functions.
diff --git a/docs/clang-tidy/checks/hicpp-use-equals-delete.rst b/docs/clang-tidy/checks/hicpp-use-equals-delete.rst
new file mode 100644 (file)
index 0000000..c05da23
--- /dev/null
@@ -0,0 +1,8 @@
+.. title:: clang-tidy - hicpp-use-equals-delete
+
+hicpp-use-equals-delete
+=======================
+
+This check is an alias for `modernize-use-equals-delete <modernize-use-equals-delete.hml>`_.
+Implements `rule 12.5.1 <http://www.codingstandard.com/rule/12-5-1-define-explicitly-default-or-delete-implicit-special-member-functions-of-concrete-classes/>`_ 
+to explicitly default or delete special member functions.
diff --git a/docs/clang-tidy/checks/hicpp-use-override.rst b/docs/clang-tidy/checks/hicpp-use-override.rst
new file mode 100644 (file)
index 0000000..fa1f158
--- /dev/null
@@ -0,0 +1,8 @@
+.. title:: clang-tidy - hicpp-use-override
+
+hicpp-use-override
+==================
+
+This check is an alias for `modernize-use-override <modernize-use-override.hml>`_.
+Implements `rule 10.2.1 <http://www.codingstandard.com/section/10-2-virtual-functions/>`_ to 
+declare a virtual function `override` when overriding.
diff --git a/docs/clang-tidy/checks/list.rst b/docs/clang-tidy/checks/list.rst
new file mode 100644 (file)
index 0000000..cb68092
--- /dev/null
@@ -0,0 +1,182 @@
+.. title:: clang-tidy - Clang-Tidy Checks
+
+Clang-Tidy Checks
+=================
+
+.. toctree::
+   android-cloexec-creat
+   android-cloexec-fopen
+   android-cloexec-open
+   android-cloexec-socket
+   boost-use-to-string
+   bugprone-suspicious-memset-usage
+   bugprone-undefined-memory-manipulation
+   cert-dcl03-c (redirects to misc-static-assert) <cert-dcl03-c>
+   cert-dcl21-cpp
+   cert-dcl50-cpp
+   cert-dcl54-cpp (redirects to misc-new-delete-overloads) <cert-dcl54-cpp>
+   cert-dcl58-cpp
+   cert-dcl59-cpp (redirects to google-build-namespaces) <cert-dcl59-cpp>
+   cert-env33-c
+   cert-err09-cpp (redirects to misc-throw-by-value-catch-by-reference) <cert-err09-cpp>
+   cert-err34-c
+   cert-err52-cpp
+   cert-err58-cpp
+   cert-err60-cpp
+   cert-err61-cpp (redirects to misc-throw-by-value-catch-by-reference) <cert-err61-cpp>
+   cert-fio38-c (redirects to misc-non-copyable-objects) <cert-fio38-c>
+   cert-flp30-c
+   cert-msc30-c (redirects to cert-msc50-cpp) <cert-msc30-c>
+   cert-msc50-cpp
+   cert-oop11-cpp (redirects to misc-move-constructor-init) <cert-oop11-cpp>
+   cppcoreguidelines-interfaces-global-init
+   cppcoreguidelines-no-malloc
+   cppcoreguidelines-pro-bounds-array-to-pointer-decay
+   cppcoreguidelines-pro-bounds-constant-array-index
+   cppcoreguidelines-pro-bounds-pointer-arithmetic
+   cppcoreguidelines-pro-type-const-cast
+   cppcoreguidelines-pro-type-cstyle-cast
+   cppcoreguidelines-pro-type-member-init
+   cppcoreguidelines-pro-type-reinterpret-cast
+   cppcoreguidelines-pro-type-static-cast-downcast
+   cppcoreguidelines-pro-type-union-access
+   cppcoreguidelines-pro-type-vararg
+   cppcoreguidelines-slicing
+   cppcoreguidelines-special-member-functions
+   google-build-explicit-make-pair
+   google-build-namespaces
+   google-build-using-namespace
+   google-default-arguments
+   google-explicit-constructor
+   google-global-names-in-headers
+   google-readability-braces-around-statements (redirects to readability-braces-around-statements) <google-readability-braces-around-statements>
+   google-readability-casting
+   google-readability-function-size (redirects to readability-function-size) <google-readability-function-size>
+   google-readability-namespace-comments (redirects to llvm-namespace-comment) <google-readability-namespace-comments>
+   google-readability-redundant-smartptr-get (redirects to readability-redundant-smartptr-get) <google-readability-redundant-smartptr-get>
+   google-readability-todo
+   google-runtime-int
+   google-runtime-member-string-references
+   google-runtime-operator
+   google-runtime-references
+   hicpp-explicit-conversions
+   hicpp-function-size
+   hicpp-invalid-access-moved
+   hicpp-member-init
+   hicpp-named-parameter
+   hicpp-new-delete-operators
+   hicpp-no-assembler
+   hicpp-noexcept-move
+   hicpp-special-member-functions
+   hicpp-undelegated-constructor
+   hicpp-use-equals-default
+   hicpp-use-equals-delete
+   hicpp-use-override
+   llvm-header-guard
+   llvm-include-order
+   llvm-namespace-comment
+   llvm-twine-local
+   misc-argument-comment
+   misc-assert-side-effect
+   misc-bool-pointer-implicit-conversion
+   misc-dangling-handle
+   misc-definitions-in-headers
+   misc-fold-init-type
+   misc-forward-declaration-namespace
+   misc-forwarding-reference-overload
+   misc-inaccurate-erase
+   misc-incorrect-roundings
+   misc-inefficient-algorithm
+   misc-lambda-function-name
+   misc-macro-parentheses
+   misc-macro-repeated-side-effects
+   misc-misplaced-const
+   misc-misplaced-widening-cast
+   misc-move-const-arg
+   misc-move-constructor-init
+   misc-move-forwarding-reference
+   misc-multiple-statement-macro
+   misc-new-delete-overloads
+   misc-noexcept-move-constructor
+   misc-non-copyable-objects
+   misc-redundant-expression
+   misc-sizeof-container
+   misc-sizeof-expression
+   misc-static-assert
+   misc-string-compare
+   misc-string-constructor
+   misc-string-integer-assignment
+   misc-string-literal-with-embedded-nul
+   misc-suspicious-enum-usage
+   misc-suspicious-missing-comma
+   misc-suspicious-semicolon
+   misc-suspicious-string-compare
+   misc-swapped-arguments
+   misc-throw-by-value-catch-by-reference
+   misc-unconventional-assign-operator
+   misc-undelegated-constructor
+   misc-uniqueptr-reset-release
+   misc-unused-alias-decls
+   misc-unused-parameters
+   misc-unused-raii
+   misc-unused-using-decls
+   misc-use-after-move
+   misc-virtual-near-miss
+   modernize-avoid-bind
+   modernize-deprecated-headers
+   modernize-loop-convert
+   modernize-make-shared
+   modernize-make-unique
+   modernize-pass-by-value
+   modernize-raw-string-literal
+   modernize-redundant-void-arg
+   modernize-replace-auto-ptr
+   modernize-replace-random-shuffle
+   modernize-return-braced-init-list
+   modernize-shrink-to-fit
+   modernize-unary-static-assert
+   modernize-use-auto
+   modernize-use-bool-literals
+   modernize-use-default-member-init
+   modernize-use-emplace
+   modernize-use-equals-default
+   modernize-use-equals-delete
+   modernize-use-noexcept
+   modernize-use-nullptr
+   modernize-use-override
+   modernize-use-transparent-functors
+   modernize-use-using
+   mpi-buffer-deref
+   mpi-type-mismatch
+   performance-faster-string-find
+   performance-for-range-copy
+   performance-implicit-cast-in-loop
+   performance-inefficient-string-concatenation
+   performance-inefficient-vector-operation
+   performance-type-promotion-in-math-fn
+   performance-unnecessary-copy-initialization
+   performance-unnecessary-value-param
+   readability-avoid-const-params-in-decls
+   readability-braces-around-statements
+   readability-container-size-empty
+   readability-delete-null-pointer
+   readability-deleted-default
+   readability-else-after-return
+   readability-function-size
+   readability-identifier-naming
+   readability-implicit-bool-cast
+   readability-inconsistent-declaration-parameter-name
+   readability-misleading-indentation
+   readability-misplaced-array-index
+   readability-named-parameter
+   readability-non-const-parameter
+   readability-redundant-control-flow
+   readability-redundant-declaration
+   readability-redundant-function-ptr-dereference
+   readability-redundant-member-init
+   readability-redundant-smartptr-get
+   readability-redundant-string-cstr
+   readability-redundant-string-init
+   readability-simplify-boolean-expr
+   readability-static-definition-in-anonymous-namespace
+   readability-uniqueptr-delete-release
diff --git a/docs/clang-tidy/checks/llvm-header-guard.rst b/docs/clang-tidy/checks/llvm-header-guard.rst
new file mode 100644 (file)
index 0000000..58233ec
--- /dev/null
@@ -0,0 +1,17 @@
+.. title:: clang-tidy - llvm-header-guard
+
+llvm-header-guard
+=================
+
+Finds and fixes header guards that do not adhere to LLVM style.
+
+Options
+-------
+
+.. option:: HeaderFileExtensions
+
+   A comma-separated list of filename extensions of header files (the filename
+   extensions should not include "." prefix). Default is "h,hh,hpp,hxx".
+   For header files without an extension, use an empty string (if there are no
+   other desired extensions) or leave an empty element in the list. e.g.,
+   "h,hh,hpp,hxx," (note the trailing comma).
diff --git a/docs/clang-tidy/checks/llvm-include-order.rst b/docs/clang-tidy/checks/llvm-include-order.rst
new file mode 100644 (file)
index 0000000..dba9837
--- /dev/null
@@ -0,0 +1,9 @@
+.. title:: clang-tidy - llvm-include-order
+
+llvm-include-order
+==================
+
+
+Checks the correct order of ``#includes``.
+
+See http://llvm.org/docs/CodingStandards.html#include-style
diff --git a/docs/clang-tidy/checks/llvm-namespace-comment.rst b/docs/clang-tidy/checks/llvm-namespace-comment.rst
new file mode 100644 (file)
index 0000000..f6bc598
--- /dev/null
@@ -0,0 +1,41 @@
+.. title:: clang-tidy - llvm-namespace-comment
+
+llvm-namespace-comment
+======================
+
+`google-readability-namespace-comments` redirects here as an alias for this
+check.
+
+Checks that long namespaces have a closing comment.
+
+http://llvm.org/docs/CodingStandards.html#namespace-indentation
+
+https://google.github.io/styleguide/cppguide.html#Namespaces
+
+.. code-block:: c++
+
+  namespace n1 {
+  void f();
+  }
+
+  // becomes
+
+  namespace n1 {
+  void f();
+  }  // namespace n1
+
+
+Options
+-------
+
+.. option:: ShortNamespaceLines
+
+   Requires the closing brace of the namespace definition to be followed by a
+   closing comment if the body of the namespace has more than
+   `ShortNamespaceLines` lines of code. The value is an unsigned integer that
+   defaults to `1U`.
+
+.. option:: SpacesBeforeComments
+
+   An unsigned integer specifying the number of spaces before the comment
+   closing a namespace definition. Default is `1U`.
diff --git a/docs/clang-tidy/checks/llvm-twine-local.rst b/docs/clang-tidy/checks/llvm-twine-local.rst
new file mode 100644 (file)
index 0000000..ec9ef1c
--- /dev/null
@@ -0,0 +1,16 @@
+.. title:: clang-tidy - llvm-twine-local
+
+llvm-twine-local
+================
+
+
+Looks for local ``Twine`` variables which are prone to use after frees and
+should be generally avoided.
+
+.. code-block:: c++
+
+  static Twine Moo = Twine("bark") + "bah";
+
+  // becomes
+
+  static std::string Moo = (Twine("bark") + "bah").str();
diff --git a/docs/clang-tidy/checks/misc-argument-comment.rst b/docs/clang-tidy/checks/misc-argument-comment.rst
new file mode 100644 (file)
index 0000000..b4101c2
--- /dev/null
@@ -0,0 +1,29 @@
+.. title:: clang-tidy - misc-argument-comment
+
+misc-argument-comment
+=====================
+
+Checks that argument comments match parameter names.
+
+The check understands argument comments in the form ``/*parameter_name=*/``
+that are placed right before the argument.
+
+.. code-block:: c++
+
+  void f(bool foo);
+
+  ...
+
+  f(/*bar=*/true);
+  // warning: argument name 'bar' in comment does not match parameter name 'foo'
+
+The check tries to detect typos and suggest automated fixes for them.
+
+Options
+-------
+
+.. option:: StrictMode
+
+   When zero (default value), the check will ignore leading and trailing
+   underscores and case when comparing names -- otherwise they are taken into
+   account.
diff --git a/docs/clang-tidy/checks/misc-assert-side-effect.rst b/docs/clang-tidy/checks/misc-assert-side-effect.rst
new file mode 100644 (file)
index 0000000..3f8b469
--- /dev/null
@@ -0,0 +1,23 @@
+.. title:: clang-tidy - misc-assert-side-effect
+
+misc-assert-side-effect
+=======================
+
+Finds ``assert()`` with side effect.
+
+The condition of ``assert()`` is evaluated only in debug builds so a
+condition with side effect can cause different behavior in debug / release
+builds.
+
+Options
+-------
+
+.. option:: AssertMacros
+
+   A comma-separated list of the names of assert macros to be checked.
+
+.. option:: CheckFunctionCalls
+
+   Whether to treat non-const member and non-member functions as they produce
+   side effects. Disabled by default because it can increase the number of false
+   positive warnings.
diff --git a/docs/clang-tidy/checks/misc-bool-pointer-implicit-conversion.rst b/docs/clang-tidy/checks/misc-bool-pointer-implicit-conversion.rst
new file mode 100644 (file)
index 0000000..db9a2f4
--- /dev/null
@@ -0,0 +1,16 @@
+.. title:: clang-tidy - misc-bool-pointer-implicit-conversion
+
+misc-bool-pointer-implicit-conversion
+=====================================
+
+Checks for conditions based on implicit conversion from a ``bool`` pointer to
+``bool``.
+
+Example:
+
+.. code-block:: c++
+
+  bool *p;
+  if (p) {
+    // Never used in a pointer-specific way.
+  }
diff --git a/docs/clang-tidy/checks/misc-dangling-handle.rst b/docs/clang-tidy/checks/misc-dangling-handle.rst
new file mode 100644 (file)
index 0000000..03d03ed
--- /dev/null
@@ -0,0 +1,38 @@
+.. title:: clang-tidy - misc-dangling-handle
+
+misc-dangling-handle
+====================
+
+Detect dangling references in value handles like
+``std::experimental::string_view``.
+These dangling references can be a result of constructing handles from temporary
+values, where the temporary is destroyed soon after the handle is created.
+
+Examples:
+
+.. code-block:: c++
+
+  string_view View = string();  // View will dangle.
+  string A;
+  View = A + "A";  // still dangle.
+
+  vector<string_view> V;
+  V.push_back(string());  // V[0] is dangling.
+  V.resize(3, string());  // V[1] and V[2] will also dangle.
+
+  string_view f() {
+    // All these return values will dangle.
+    return string();
+    string S;
+    return S;
+    char Array[10]{};
+    return Array;
+  }
+
+Options
+-------
+
+.. option:: HandleClasses
+
+   A semicolon-separated list of class names that should be treated as handles.
+   By default only ``std::experimental::basic_string_view`` is considered.
diff --git a/docs/clang-tidy/checks/misc-definitions-in-headers.rst b/docs/clang-tidy/checks/misc-definitions-in-headers.rst
new file mode 100644 (file)
index 0000000..96a3b1b
--- /dev/null
@@ -0,0 +1,101 @@
+.. title:: clang-tidy - misc-definitions-in-headers
+
+misc-definitions-in-headers
+===========================
+
+Finds non-extern non-inline function and variable definitions in header files,
+which can lead to potential ODR violations in case these headers are included
+from multiple translation units.
+
+.. code-block:: c++
+
+   // Foo.h
+   int a = 1; // Warning: variable definition.
+   extern int d; // OK: extern variable.
+
+   namespace N {
+     int e = 2; // Warning: variable definition.
+   }
+
+   // Warning: variable definition.
+   const char* str = "foo";
+
+   // OK: internal linkage variable definitions are ignored for now.
+   // Although these might also cause ODR violations, we can be less certain and
+   // should try to keep the false-positive rate down.
+   static int b = 1;
+   const int c = 1;
+   const char* const str2 = "foo";
+
+   // Warning: function definition.
+   int g() {
+     return 1;
+   }
+
+   // OK: inline function definition is allowed to be defined multiple times.
+   inline int e() {
+     return 1;
+   }
+
+   class A {
+   public:
+     int f1() { return 1; } // OK: implicitly inline member function definition is allowed.
+     int f2();
+
+     static int d;
+   };
+
+   // Warning: not an inline member function definition.
+   int A::f2() { return 1; }
+
+   // OK: class static data member declaration is allowed.
+   int A::d = 1;
+
+   // OK: function template is allowed.
+   template<typename T>
+   T f3() {
+     T a = 1;
+     return a;
+   }
+
+   // Warning: full specialization of a function template is not allowed.
+   template <>
+   int f3() {
+     int a = 1;
+     return a;
+   }
+
+   template <typename T>
+   struct B {
+     void f1();
+   };
+
+   // OK: member function definition of a class template is allowed.
+   template <typename T>
+   void B<T>::f1() {}
+
+   class CE {
+     constexpr static int i = 5; // OK: inline variable definition.
+   };
+
+   inline int i = 5; // OK: inline variable definition.
+
+   constexpr int k = 1; // OK: constexpr variable has internal linkage.
+
+   constexpr int f10() { return 0; } // OK: constexpr function definition.
+
+Options
+-------
+
+.. option:: HeaderFileExtensions
+
+   A comma-separated list of filename extensions of header files (the filename
+   extensions should not include "." prefix). Default is "h,hh,hpp,hxx".
+   For header files without an extension, use an empty string (if there are no
+   other desired extensions) or leave an empty element in the list. e.g.,
+   "h,hh,hpp,hxx," (note the trailing comma).
+
+.. option:: UseHeaderFileExtension
+
+   When non-zero, the check will use the file extension to distinguish header
+   files. Default is `1`.
diff --git a/docs/clang-tidy/checks/misc-fold-init-type.rst b/docs/clang-tidy/checks/misc-fold-init-type.rst
new file mode 100644 (file)
index 0000000..ff1c6f0
--- /dev/null
@@ -0,0 +1,27 @@
+.. title:: clang-tidy - misc-fold-init-type
+
+misc-fold-init-type
+===================
+
+The check flags type mismatches in
+`folds <https://en.wikipedia.org/wiki/Fold_(higher-order_function)>`_
+like ``std::accumulate`` that might result in loss of precision.
+``std::accumulate`` folds an input range into an initial value using the type of
+the latter, with ``operator+`` by default. This can cause loss of precision
+through:
+
+- Truncation: The following code uses a floating point range and an int
+  initial value, so trucation wil happen at every application of ``operator+``
+  and the result will be `0`, which might not be what the user expected.
+
+.. code-block:: c++
+
+  auto a = {0.5f, 0.5f, 0.5f, 0.5f};
+  return std::accumulate(std::begin(a), std::end(a), 0);
+
+- Overflow: The following code also returns `0`.
+
+.. code-block:: c++
+
+  auto a = {65536LL * 65536 * 65536};
+  return std::accumulate(std::begin(a), std::end(a), 0);
diff --git a/docs/clang-tidy/checks/misc-forward-declaration-namespace.rst b/docs/clang-tidy/checks/misc-forward-declaration-namespace.rst
new file mode 100644 (file)
index 0000000..2dd1c46
--- /dev/null
@@ -0,0 +1,20 @@
+.. title:: clang-tidy - misc-forward-declaration-namespace
+
+misc-forward-declaration-namespace
+==================================
+
+Checks if an unused forward declaration is in a wrong namespace.
+
+The check inspects all unused forward declarations and checks if there is any
+declaration/definition with the same name existing, which could indicate that
+the forward declaration is in a potentially wrong namespace.
+
+.. code-block:: c++
+
+  namespace na { struct A; }
+  namespace nb { struct A {}; }
+  nb::A a;
+  // warning : no definition found for 'A', but a definition with the same name
+  // 'A' found in another namespace 'nb::'
+
+This check can only generate warnings, but it can't suggest a fix at this point.
diff --git a/docs/clang-tidy/checks/misc-forwarding-reference-overload.rst b/docs/clang-tidy/checks/misc-forwarding-reference-overload.rst
new file mode 100644 (file)
index 0000000..1682a5f
--- /dev/null
@@ -0,0 +1,49 @@
+.. title:: clang-tidy - misc-forwarding-reference-overload
+
+misc-forwarding-reference-overload
+==================================
+
+The check looks for perfect forwarding constructors that can hide copy or move
+constructors. If a non const lvalue reference is passed to the constructor, the
+forwarding reference parameter will be a better match than the const reference
+parameter of the copy constructor, so the perfect forwarding constructor will be
+called, which can be confusing.
+For detailed description of this issue see: Scott Meyers, Effective Modern C++,
+Item 26.
+
+Consider the following example:
+
+  .. code-block:: c++
+
+    class Person {
+    public:
+      // C1: perfect forwarding ctor
+      template<typename T>
+      explicit Person(T&& n) {}
+
+      // C2: perfect forwarding ctor with parameter default value
+      template<typename T>
+      explicit Person(T&& n, int x = 1) {}
+
+      // C3: perfect forwarding ctor guarded with enable_if
+      template<typename T, typename X = enable_if_t<is_special<T>,void>>
+      explicit Person(T&& n) {}
+
+      // (possibly compiler generated) copy ctor
+      Person(const Person& rhs);
+    };
+
+The check warns for constructors C1 and C2, because those can hide copy and move
+constructors. We suppress warnings if the copy and the move constructors are both
+disabled (deleted or private), because there is nothing the perfect forwarding
+constructor could hide in this case. We also suppress warnings for constructors
+like C3 that are guarded with an enable_if, assuming the programmer was aware of
+the possible hiding.
+
+Background
+----------
+
+For deciding whether a constructor is guarded with enable_if, we consider the
+default values of the type parameters and the types of the constructor
+parameters. If any part of these types is std::enable_if or std::enable_if_t, we
+assume the constructor is guarded.
diff --git a/docs/clang-tidy/checks/misc-inaccurate-erase.rst b/docs/clang-tidy/checks/misc-inaccurate-erase.rst
new file mode 100644 (file)
index 0000000..f55bfa7
--- /dev/null
@@ -0,0 +1,13 @@
+.. title:: clang-tidy - misc-inaccurate-erase
+
+misc-inaccurate-erase
+=====================
+
+
+Checks for inaccurate use of the ``erase()`` method.
+
+Algorithms like ``remove()`` do not actually remove any element from the
+container but return an iterator to the first redundant element at the end
+of the container. These redundant elements must be removed using the
+``erase()`` method. This check warns when not all of the elements will be
+removed due to using an inappropriate overload.
diff --git a/docs/clang-tidy/checks/misc-incorrect-roundings.rst b/docs/clang-tidy/checks/misc-incorrect-roundings.rst
new file mode 100644 (file)
index 0000000..be04f6c
--- /dev/null
@@ -0,0 +1,16 @@
+.. title:: clang-tidy - misc-incorrect-roundings
+
+misc-incorrect-roundings
+========================
+
+Checks the usage of patterns known to produce incorrect rounding.
+Programmers often use::
+
+   (int)(double_expression + 0.5)
+
+to round the double expression to an integer. The problem with this:
+
+1. It is unnecessarily slow.
+2. It is incorrect. The number 0.499999975 (smallest representable float
+   number below 0.5) rounds to 1.0. Even worse behavior for negative
+   numbers where both -0.5f and -1.4f both round to 0.0.
diff --git a/docs/clang-tidy/checks/misc-inefficient-algorithm.rst b/docs/clang-tidy/checks/misc-inefficient-algorithm.rst
new file mode 100644 (file)
index 0000000..0032487
--- /dev/null
@@ -0,0 +1,29 @@
+.. title:: clang-tidy - misc-inefficient-algorithm
+
+misc-inefficient-algorithm
+==========================
+
+
+Warns on inefficient use of STL algorithms on associative containers.
+
+Associative containers implements some of the algorithms as methods which
+should be preferred to the algorithms in the algorithm header. The methods
+can take advanatage of the order of the elements.
+
+.. code-block:: c++
+
+  std::set<int> s;
+  auto it = std::find(s.begin(), s.end(), 43);
+
+  // becomes
+
+  auto it = s.find(43);
+
+.. code-block:: c++
+
+  std::set<int> s;
+  auto c = std::count(s.begin(), s.end(), 43);
+
+  // becomes
+
+  auto c = s.count(43);
diff --git a/docs/clang-tidy/checks/misc-lambda-function-name.rst b/docs/clang-tidy/checks/misc-lambda-function-name.rst
new file mode 100644 (file)
index 0000000..1ab584c
--- /dev/null
@@ -0,0 +1,27 @@
+.. title:: clang-tidy - misc-lambda-function-name
+
+misc-lambda-function-name
+=========================
+
+Checks for attempts to get the name of a function from within a lambda
+expression. The name of a lambda is always something like ``operator()``, which
+is almost never what was intended.
+
+Example:
+
+.. code-block:: c++
+                                                               
+  void FancyFunction() {
+    [] { printf("Called from %s\n", __func__); }();
+    [] { printf("Now called from %s\n", __FUNCTION__); }();
+  }
+
+Output::
+
+  Called from operator()
+  Now called from operator()
+
+Likely intended output::
+
+  Called from FancyFunction
+  Now called from FancyFunction
diff --git a/docs/clang-tidy/checks/misc-macro-parentheses.rst b/docs/clang-tidy/checks/misc-macro-parentheses.rst
new file mode 100644 (file)
index 0000000..6120170
--- /dev/null
@@ -0,0 +1,19 @@
+.. title:: clang-tidy - misc-macro-parentheses
+
+misc-macro-parentheses
+======================
+
+
+Finds macros that can have unexpected behaviour due to missing parentheses.
+
+Macros are expanded by the preprocessor as-is. As a result, there can be
+unexpected behaviour; operators may be evaluated in unexpected order and
+unary operators may become binary operators, etc.
+
+When the replacement list has an expression, it is recommended to surround
+it with parentheses. This ensures that the macro result is evaluated
+completely before it is used.
+
+It is also recommended to surround macro arguments in the replacement list
+with parentheses. This ensures that the argument value is calculated
+properly.
diff --git a/docs/clang-tidy/checks/misc-macro-repeated-side-effects.rst b/docs/clang-tidy/checks/misc-macro-repeated-side-effects.rst
new file mode 100644 (file)
index 0000000..7cd3781
--- /dev/null
@@ -0,0 +1,7 @@
+.. title:: clang-tidy - misc-macro-repeated-side-effects
+
+misc-macro-repeated-side-effects
+================================
+
+
+Checks for repeated argument with side effects in macros.
diff --git a/docs/clang-tidy/checks/misc-misplaced-const.rst b/docs/clang-tidy/checks/misc-misplaced-const.rst
new file mode 100644 (file)
index 0000000..ee1549f
--- /dev/null
@@ -0,0 +1,22 @@
+.. title:: clang-tidy - misc-misplaced-const
+
+misc-misplaced-const
+====================
+
+This check diagnoses when a ``const`` qualifier is applied to a ``typedef`` to a
+pointer type rather than to the pointee, because such constructs are often
+misleading to developers because the ``const`` applies to the pointer rather
+than the pointee.
+
+For instance, in the following code, the resulting type is ``int *`` ``const``
+rather than ``const int *``:
+
+.. code-block:: c++
+
+  typedef int *int_ptr;
+  void f(const int_ptr ptr);
+
+The check does not diagnose when the underlying ``typedef`` type is a pointer to
+a ``const`` type or a function pointer type. This is because the ``const``
+qualifier is less likely to be mistaken because it would be redundant (or
+disallowed) on the underlying pointee type.
diff --git a/docs/clang-tidy/checks/misc-misplaced-widening-cast.rst b/docs/clang-tidy/checks/misc-misplaced-widening-cast.rst
new file mode 100644 (file)
index 0000000..a2fc2bf
--- /dev/null
@@ -0,0 +1,65 @@
+.. title:: clang-tidy - misc-misplaced-widening-cast
+
+misc-misplaced-widening-cast
+============================
+
+This check will warn when there is a cast of a calculation result to a bigger
+type. If the intention of the cast is to avoid loss of precision then the cast
+is misplaced, and there can be loss of precision. Otherwise the cast is
+ineffective.
+
+Example code:
+
+.. code-block:: c++
+
+    long f(int x) {
+        return (long)(x * 1000);
+    }
+
+The result ``x * 1000`` is first calculated using ``int`` precision. If the
+result exceeds ``int`` precision there is loss of precision. Then the result is
+casted to ``long``.
+
+If there is no loss of precision then the cast can be removed or you can
+explicitly cast to ``int`` instead.
+
+If you want to avoid loss of precision then put the cast in a proper location,
+for instance:
+
+.. code-block:: c++
+
+    long f(int x) {
+        return (long)x * 1000;
+    }
+
+Implicit casts
+--------------
+
+Forgetting to place the cast at all is at least as dangerous and at least as
+common as misplacing it. If :option:`CheckImplicitCasts` is enabled the check
+also detects these cases, for instance:
+
+.. code-block:: c++
+
+    long f(int x) {
+        return x * 1000;
+    }
+
+Floating point
+--------------
+
+Currently warnings are only written for integer conversion. No warning is
+written for this code:
+
+.. code-block:: c++
+
+    double f(float x) {
+        return (double)(x * 10.0f);
+    }
+
+Options
+-------
+
+.. option:: CheckImplicitCasts
+
+   If non-zero, enables detection of implicit casts. Default is non-zero.
diff --git a/docs/clang-tidy/checks/misc-move-const-arg.rst b/docs/clang-tidy/checks/misc-move-const-arg.rst
new file mode 100644 (file)
index 0000000..00f4753
--- /dev/null
@@ -0,0 +1,29 @@
+.. title:: clang-tidy - misc-move-const-arg
+
+misc-move-const-arg
+===================
+
+The check warns
+
+- if ``std::move()`` is called with a constant argument,
+
+- if ``std::move()`` is called with an argument of a trivially-copyable type,
+
+- if the result of ``std::move()`` is passed as a const reference argument.
+
+In all three cases, the check will suggest a fix that removes the
+``std::move()``.
+
+Here are examples of each of the three cases:
+
+.. code-block:: c++
+
+  const string s;
+  return std::move(s);  // Warning: std::move of the const variable has no effect
+
+  int x;
+  return std::move(x);  // Warning: std::move of the variable of a trivially-copyable type has no effect
+
+  void f(const string &s);
+  string s;
+  f(std::move(s));  // Warning: passing result of std::move as a const reference argument; no move will actually happen
diff --git a/docs/clang-tidy/checks/misc-move-constructor-init.rst b/docs/clang-tidy/checks/misc-move-constructor-init.rst
new file mode 100644 (file)
index 0000000..f56c592
--- /dev/null
@@ -0,0 +1,18 @@
+.. title:: clang-tidy - misc-move-constructor-init
+
+misc-move-constructor-init
+==========================
+
+"cert-oop11-cpp" redirects here as an alias for this check.
+
+The check flags user-defined move constructors that have a ctor-initializer
+initializing a member or base class through a copy constructor instead of a
+move constructor.
+
+Options
+-------
+
+.. option:: IncludeStyle
+
+   A string specifying which include-style is used, `llvm` or `google`. Default
+   is `llvm`.
diff --git a/docs/clang-tidy/checks/misc-move-forwarding-reference.rst b/docs/clang-tidy/checks/misc-move-forwarding-reference.rst
new file mode 100644 (file)
index 0000000..327f720
--- /dev/null
@@ -0,0 +1,60 @@
+.. title:: clang-tidy - misc-move-forwarding-reference
+
+misc-move-forwarding-reference
+==============================
+
+Warns if ``std::move`` is called on a forwarding reference, for example:
+
+  .. code-block:: c++
+
+    template <typename T>
+    void foo(T&& t) {
+      bar(std::move(t));
+    }
+
+`Forwarding references
+<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4164.pdf>`_ should
+typically be passed to ``std::forward`` instead of ``std::move``, and this is
+the fix that will be suggested.
+
+(A forwarding reference is an rvalue reference of a type that is a deduced
+function template argument.)
+
+In this example, the suggested fix would be
+
+  .. code-block:: c++
+
+    bar(std::forward<T>(t));
+
+Background
+----------
+
+Code like the example above is sometimes written with the expectation that
+``T&&`` will always end up being an rvalue reference, no matter what type is
+deduced for ``T``, and that it is therefore not possible to pass an lvalue to
+``foo()``. However, this is not true. Consider this example:
+
+  .. code-block:: c++
+
+    std::string s = "Hello, world";
+    foo(s);
+
+This code compiles and, after the call to ``foo()``, ``s`` is left in an
+indeterminate state because it has been moved from. This may be surprising to
+the caller of ``foo()`` because no ``std::move`` was used when calling
+``foo()``.
+
+The reason for this behavior lies in the special rule for template argument
+deduction on function templates like ``foo()`` -- i.e. on function templates
+that take an rvalue reference argument of a type that is a deduced function
+template argument. (See section [temp.deduct.call]/3 in the C++11 standard.)
+
+If ``foo()`` is called on an lvalue (as in the example above), then ``T`` is
+deduced to be an lvalue reference. In the example, ``T`` is deduced to be
+``std::string &``. The type of the argument ``t`` therefore becomes
+``std::string& &&``; by the reference collapsing rules, this collapses to
+``std::string&``.
+
+This means that the ``foo(s)`` call passes ``s`` as an lvalue reference, and
+``foo()`` ends up moving ``s`` and thereby placing it into an indeterminate
+state.
diff --git a/docs/clang-tidy/checks/misc-multiple-statement-macro.rst b/docs/clang-tidy/checks/misc-multiple-statement-macro.rst
new file mode 100644 (file)
index 0000000..a0a6594
--- /dev/null
@@ -0,0 +1,16 @@
+.. title:: clang-tidy - misc-multiple-statement-macro
+
+misc-multiple-statement-macro
+=============================
+
+Detect multiple statement macros that are used in unbraced conditionals. Only
+the first statement of the macro will be inside the conditional and the other
+ones will be executed unconditionally.
+
+Example:
+
+.. code-block:: c++
+
+  #define INCREMENT_TWO(x, y) (x)++; (y)++
+  if (do_increment)
+    INCREMENT_TWO(a, b);  // (b)++ will be executed unconditionally.
diff --git a/docs/clang-tidy/checks/misc-new-delete-overloads.rst b/docs/clang-tidy/checks/misc-new-delete-overloads.rst
new file mode 100644 (file)
index 0000000..727436d
--- /dev/null
@@ -0,0 +1,19 @@
+.. title:: clang-tidy - misc-new-delete-overloads
+
+misc-new-delete-overloads
+=========================
+
+`cert-dcl54-cpp` redirects here as an alias for this check.
+
+The check flags overloaded operator ``new()`` and operator ``delete()``
+functions that do not have a corresponding free store function defined within
+the same scope.
+For instance, the check will flag a class implementation of a non-placement
+operator ``new()`` when the class does not also define a non-placement operator
+``delete()`` function as well.
+
+The check does not flag implicitly-defined operators, deleted or private
+operators, or placement operators.
+
+This check corresponds to CERT C++ Coding Standard rule `DCL54-CPP. Overload allocation and deallocation functions as a pair in the same scope
+<https://www.securecoding.cert.org/confluence/display/cplusplus/DCL54-CPP.+Overload+allocation+and+deallocation+functions+as+a+pair+in+the+same+scope>`_.
diff --git a/docs/clang-tidy/checks/misc-noexcept-move-constructor.rst b/docs/clang-tidy/checks/misc-noexcept-move-constructor.rst
new file mode 100644 (file)
index 0000000..9d3d4f7
--- /dev/null
@@ -0,0 +1,13 @@
+.. title:: clang-tidy - misc-noexcept-move-constructor
+
+misc-noexcept-move-constructor
+==============================
+
+
+The check flags user-defined move constructors and assignment operators not
+marked with ``noexcept`` or marked with ``noexcept(expr)`` where ``expr``
+evaluates to ``false`` (but is not a ``false`` literal itself).
+
+Move constructors of all the types used with STL containers, for example,
+need to be declared ``noexcept``. Otherwise STL will choose copy constructors
+instead. The same is valid for move assignment operations.
diff --git a/docs/clang-tidy/checks/misc-non-copyable-objects.rst b/docs/clang-tidy/checks/misc-non-copyable-objects.rst
new file mode 100644 (file)
index 0000000..d1f7bba
--- /dev/null
@@ -0,0 +1,13 @@
+.. title:: clang-tidy - misc-non-copyable-objects
+
+misc-non-copyable-objects
+=========================
+
+`cert-fio38-c` redirects here as an alias for this check.
+
+The check flags dereferences and non-pointer declarations of objects that are
+not meant to be passed by value, such as C FILE objects or POSIX
+``pthread_mutex_t`` objects.
+
+This check corresponds to CERT C++ Coding Standard rule `FIO38-C. Do not copy a FILE object
+<https://www.securecoding.cert.org/confluence/display/c/FIO38-C.+Do+not+copy+a+FILE+object>`_.
diff --git a/docs/clang-tidy/checks/misc-redundant-expression.rst b/docs/clang-tidy/checks/misc-redundant-expression.rst
new file mode 100644 (file)
index 0000000..ddef9af
--- /dev/null
@@ -0,0 +1,25 @@
+.. title:: clang-tidy - misc-redundant-expression
+
+misc-redundant-expression
+=========================
+
+Detect redundant expressions which are typically errors due to copy-paste.
+
+Depending on the operator expressions may be
+
+- redundant,
+
+- always be ``true``,
+
+- always be ``false``,
+
+- always be a constant (zero or one).
+
+Example:
+
+.. code-block:: c++
+
+  ((x+1) | (x+1))             // (x+1) is redundant
+  (p->x == p->x)              // always true
+  (p->x < p->x)               // always false
+  (speed - speed + 1 == 12)   // speed - speed is always zero
diff --git a/docs/clang-tidy/checks/misc-sizeof-container.rst b/docs/clang-tidy/checks/misc-sizeof-container.rst
new file mode 100644 (file)
index 0000000..9ee4440
--- /dev/null
@@ -0,0 +1,26 @@
+.. title:: clang-tidy - misc-sizeof-container
+
+misc-sizeof-container
+=====================
+
+The check finds usages of ``sizeof`` on expressions of STL container types. Most
+likely the user wanted to use ``.size()`` instead.
+
+All class/struct types declared in namespace ``std::`` having a const ``size()``
+method are considered containers, with the exception of ``std::bitset`` and
+``std::array``.
+
+Examples:
+
+.. code-block:: c++
+
+  std::string s;
+  int a = 47 + sizeof(s); // warning: sizeof() doesn't return the size of the container. Did you mean .size()?
+
+  int b = sizeof(std::string); // no warning, probably intended.
+
+  std::string array_of_strings[10];
+  int c = sizeof(array_of_strings) / sizeof(array_of_strings[0]); // no warning, definitely intended.
+
+  std::array<int, 3> std_array;
+  int d = sizeof(std_array); // no warning, probably intended.
diff --git a/docs/clang-tidy/checks/misc-sizeof-expression.rst b/docs/clang-tidy/checks/misc-sizeof-expression.rst
new file mode 100644 (file)
index 0000000..d3e3790
--- /dev/null
@@ -0,0 +1,154 @@
+.. title:: clang-tidy - misc-sizeof-expression
+
+misc-sizeof-expression
+======================
+
+The check finds usages of ``sizeof`` expressions which are most likely errors.
+
+The ``sizeof`` operator yields the size (in bytes) of its operand, which may be
+an expression or the parenthesized name of a type. Misuse of this operator may
+be leading to errors and possible software vulnerabilities.
+
+Suspicious usage of 'sizeof(K)'
+-------------------------------
+
+A common mistake is to query the ``sizeof`` of an integer literal. This is
+equivalent to query the size of its type (probably ``int``). The intent of the
+programmer was probably to simply get the integer and not its size.
+
+.. code-block:: c++
+
+  #define BUFLEN 42
+  char buf[BUFLEN];
+  memset(buf, 0, sizeof(BUFLEN));  // sizeof(42) ==> sizeof(int)
+
+Suspicious usage of 'sizeof(this)'
+----------------------------------
+
+The ``this`` keyword is evaluated to a pointer to an object of a given type.
+The expression ``sizeof(this)`` is returning the size of a pointer. The
+programmer most likely wanted the size of the object and not the size of the
+pointer.
+
+.. code-block:: c++
+
+  class Point {
+    [...]
+    size_t size() { return sizeof(this); }  // should probably be sizeof(*this)
+    [...]
+  };
+
+Suspicious usage of 'sizeof(char*)'
+-----------------------------------
+
+There is a subtle difference between declaring a string literal with
+``char* A = ""`` and ``char A[] = ""``. The first case has the type ``char*``
+instead of the aggregate type ``char[]``. Using ``sizeof`` on an object declared
+with ``char*`` type is returning the size of a pointer instead of the number of
+characters (bytes) in the string literal.
+
+.. code-block:: c++
+
+  const char* kMessage = "Hello World!";      // const char kMessage[] = "...";
+  void getMessage(char* buf) {
+    memcpy(buf, kMessage, sizeof(kMessage));  // sizeof(char*)
+  }
+
+Suspicious usage of 'sizeof(A*)'
+--------------------------------
+
+A common mistake is to compute the size of a pointer instead of its pointee.
+These cases may occur because of explicit cast or implicit conversion.
+
+.. code-block:: c++
+
+  int A[10];
+  memset(A, 0, sizeof(A + 0));
+
+  struct Point point;
+  memset(point, 0, sizeof(&point));
+
+Suspicious usage of 'sizeof(...)/sizeof(...)'
+---------------------------------------------
+
+Dividing ``sizeof`` expressions is typically used to retrieve the number of
+elements of an aggregate. This check warns on incompatible or suspicious cases.
+
+In the following example, the entity has 10-bytes and is incompatible with the
+type ``int`` which has 4 bytes.
+
+.. code-block:: c++
+
+  char buf[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };  // sizeof(buf) => 10
+  void getMessage(char* dst) {
+    memcpy(dst, buf, sizeof(buf) / sizeof(int));  // sizeof(int) => 4  [incompatible sizes]
+  }
+
+In the following example, the expression ``sizeof(Values)`` is returning the
+size of ``char*``. One can easily be fooled by its declaration, but in parameter
+declaration the size '10' is ignored and the function is receiving a ``char*``.
+
+.. code-block:: c++
+
+  char OrderedValues[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+  return CompareArray(char Values[10]) {
+    return memcmp(OrderedValues, Values, sizeof(Values)) == 0;  // sizeof(Values) ==> sizeof(char*) [implicit cast to char*]
+  }
+
+Suspicious 'sizeof' by 'sizeof' expression
+------------------------------------------
+
+Multiplying ``sizeof`` expressions typically makes no sense and is probably a
+logic error. In the following example, the programmer used ``*`` instead of
+``/``.
+
+.. code-block:: c++
+
+  const char kMessage[] = "Hello World!";
+  void getMessage(char* buf) {
+    memcpy(buf, kMessage, sizeof(kMessage) * sizeof(char));  //  sizeof(kMessage) / sizeof(char)
+  }
+
+This check may trigger on code using the arraysize macro. The following code is
+working correctly but should be simplified by using only the ``sizeof``
+operator.
+
+.. code-block:: c++
+
+  extern Object objects[100];
+  void InitializeObjects() {
+    memset(objects, 0, arraysize(objects) * sizeof(Object));  // sizeof(objects)
+  }
+
+Suspicious usage of 'sizeof(sizeof(...))'
+-----------------------------------------
+
+Getting the ``sizeof`` of a ``sizeof`` makes no sense and is typically an error
+hidden through macros.
+
+.. code-block:: c++
+
+  #define INT_SZ sizeof(int)
+  int buf[] = { 42 };
+  void getInt(int* dst) {
+    memcpy(dst, buf, sizeof(INT_SZ));  // sizeof(sizeof(int)) is suspicious.
+  }
+
+Options
+-------
+
+.. option:: WarnOnSizeOfConstant
+
+   When non-zero, the check will warn on an expression like
+   ``sizeof(CONSTANT)``. Default is `1`.
+
+.. option:: WarnOnSizeOfThis
+
+   When non-zero, the check will warn on an expression like ``sizeof(this)``.
+   Default is `1`.
+
+.. option:: WarnOnSizeOfCompareToConstant
+
+   When non-zero, the check will warn on an expression like
+   ``sizeof(epxr) <= k`` for a suspicious constant `k` while `k` is `0` or
+   greater than `0x8000`. Default is `1`.
diff --git a/docs/clang-tidy/checks/misc-static-assert.rst b/docs/clang-tidy/checks/misc-static-assert.rst
new file mode 100644 (file)
index 0000000..cf0cc2d
--- /dev/null
@@ -0,0 +1,12 @@
+.. title:: clang-tidy - misc-static-assert
+
+misc-static-assert
+==================
+
+`cert-dcl03-c` redirects here as an alias for this check.
+
+Replaces ``assert()`` with ``static_assert()`` if the condition is evaluatable
+at compile time.
+
+The condition of ``static_assert()`` is evaluated at compile time which is
+safer and more efficient.
diff --git a/docs/clang-tidy/checks/misc-string-compare.rst b/docs/clang-tidy/checks/misc-string-compare.rst
new file mode 100644 (file)
index 0000000..78a5d4f
--- /dev/null
@@ -0,0 +1,54 @@
+.. title:: clang-tidy - misc-string-compare
+
+misc-string-compare
+===================
+
+Finds string comparisons using the compare method.
+
+A common mistake is to use the string's ``compare`` method instead of using the 
+equality or inequality operators. The compare method is intended for sorting
+functions and thus returns a negative number, a positive number or 
+zero depending on the lexicographical relationship between the strings compared. 
+If an equality or inequality check can suffice, that is recommended. This is 
+recommended to avoid the risk of incorrect interpretation of the return value
+and to simplify the code. The string equality and inequality operators can
+also be faster than the ``compare`` method due to early termination.
+
+Examples:
+
+.. code-block:: c++
+
+  std::string str1{"a"};
+  std::string str2{"b"};
+
+  // use str1 != str2 instead.
+  if (str1.compare(str2)) {
+  }
+
+  // use str1 == str2 instead.
+  if (!str1.compare(str2)) {
+  }
+
+  // use str1 == str2 instead.
+  if (str1.compare(str2) == 0) {
+  }
+
+  // use str1 != str2 instead.
+  if (str1.compare(str2) != 0) {
+  }
+
+  // use str1 == str2 instead.
+  if (0 == str1.compare(str2)) {
+  }
+
+  // use str1 != str2 instead.
+  if (0 != str1.compare(str2)) {
+  }
+
+  // Use str1 == "foo" instead.
+  if (str1.compare("foo") == 0) {
+  }
+
+The above code examples shows the list of if-statements that this check will
+give a warning for. All of them uses ``compare`` to check if equality or 
+inequality of two strings instead of using the correct operators.
diff --git a/docs/clang-tidy/checks/misc-string-constructor.rst b/docs/clang-tidy/checks/misc-string-constructor.rst
new file mode 100644 (file)
index 0000000..a5d2c88
--- /dev/null
@@ -0,0 +1,44 @@
+.. title:: clang-tidy - misc-string-constructor
+
+misc-string-constructor
+=======================
+
+Finds string constructors that are suspicious and probably errors.
+
+A common mistake is to swap parameters to the 'fill' string-constructor.
+
+Examples:
+
+.. code-block:: c++
+
+  std::string('x', 50) str; // should be std::string(50, 'x')
+
+Calling the string-literal constructor with a length bigger than the literal is
+suspicious and adds extra random characters to the string.
+
+Examples:
+
+.. code-block:: c++
+
+  std::string("test", 200);   // Will include random characters after "test".
+
+Creating an empty string from constructors with parameters is considered
+suspicious. The programmer should use the empty constructor instead.
+
+Examples:
+
+.. code-block:: c++
+
+  std::string("test", 0);   // Creation of an empty string.
+
+Options
+-------
+
+.. option::  WarnOnLargeLength
+
+   When non-zero, the check will warn on a string with a length greater than
+   `LargeLengthThreshold`. Default is `1`.
+
+.. option::  LargeLengthThreshold
+
+   An integer specifying the large length threshold. Default is `0x800000`.
diff --git a/docs/clang-tidy/checks/misc-string-integer-assignment.rst b/docs/clang-tidy/checks/misc-string-integer-assignment.rst
new file mode 100644 (file)
index 0000000..d882e8d
--- /dev/null
@@ -0,0 +1,37 @@
+.. title:: clang-tidy - misc-string-integer-assignment
+
+misc-string-integer-assignment
+==============================
+
+The check finds assignments of an integer to ``std::basic_string<CharT>``
+(``std::string``, ``std::wstring``, etc.). The source of the problem is the
+following assignment operator of ``std::basic_string<CharT>``:
+
+.. code-block:: c++
+
+  basic_string& operator=( CharT ch );
+
+Numeric types can be implicitly casted to character types.
+
+.. code-block:: c++
+
+  std::string s;
+  int x = 5965;
+  s = 6;
+  s = x;
+
+Use the appropriate conversion functions or character literals.
+
+.. code-block:: c++
+
+  std::string s;
+  int x = 5965;
+  s = '6';
+  s = std::to_string(x);
+
+In order to suppress false positives, use an explicit cast.
+
+.. code-block:: c++
+
+  std::string s;
+  s = static_cast<char>(6);
diff --git a/docs/clang-tidy/checks/misc-string-literal-with-embedded-nul.rst b/docs/clang-tidy/checks/misc-string-literal-with-embedded-nul.rst
new file mode 100644 (file)
index 0000000..29af0a4
--- /dev/null
@@ -0,0 +1,36 @@
+.. title:: clang-tidy - misc-string-literal-with-embedded-nul
+
+misc-string-literal-with-embedded-nul
+=====================================
+
+Finds occurrences of string literal with embedded NUL character and validates
+their usage.
+
+Invalid escaping
+----------------
+
+Special characters can be escaped within a string literal by using their
+hexadecimal encoding like ``\x42``. A common mistake is to escape them
+like this ``\0x42`` where the ``\0`` stands for the NUL character.
+
+.. code-block:: c++
+
+  const char* Example[] = "Invalid character: \0x12 should be \x12";
+  const char* Bytes[] = "\x03\0x02\0x01\0x00\0xFF\0xFF\0xFF";
+
+Truncated literal
+-----------------
+
+String-like classes can manipulate strings with embedded NUL as they are keeping
+track of the bytes and the length. This is not the case for a ``char*``
+(NUL-terminated) string.
+
+A common mistake is to pass a string-literal with embedded NUL to a string
+constructor expecting a NUL-terminated string. The bytes after the first NUL
+character are truncated.
+
+.. code-block:: c++
+
+  std::string str("abc\0def");  // "def" is truncated
+  str += "\0";                  // This statement is doing nothing
+  if (str == "\0abc") return;   // This expression is always true
diff --git a/docs/clang-tidy/checks/misc-suspicious-enum-usage.rst b/docs/clang-tidy/checks/misc-suspicious-enum-usage.rst
new file mode 100644 (file)
index 0000000..0a5a484
--- /dev/null
@@ -0,0 +1,78 @@
+.. title:: clang-tidy - misc-suspicious-enum-usage
+
+misc-suspicious-enum-usage
+==========================
+
+The checker detects various cases when an enum is probably misused (as a bitmask
+).
+  
+1. When "ADD" or "bitwise OR" is used between two enum which come from different
+   types and these types value ranges are not disjoint.
+
+The following cases will be investigated only using :option:`StrictMode`. We 
+regard the enum as a (suspicious)
+bitmask if the three conditions below are true at the same time:
+
+* at most half of the elements of the enum are non pow-of-2 numbers (because of
+  short enumerations)
+* there is another non pow-of-2 number than the enum constant representing all
+  choices (the result "bitwise OR" operation of all enum elements)
+* enum type variable/enumconstant is used as an argument of a `+` or "bitwise OR
+  " operator
+
+So whenever the non pow-of-2 element is used as a bitmask element we diagnose a
+misuse and give a warning.
+
+2. Investigating the right hand side of `+=` and `|=` operator.
+3. Check only the enum value side of a `|` and `+` operator if one of them is not
+   enum val.
+4. Check both side of `|` or `+` operator where the enum values are from the
+   same enum type.
+
+Examples:
+
+.. code-block:: c++
+
+  enum { A, B, C };
+  enum { D, E, F = 5 };
+  enum { G = 10, H = 11, I = 12 };
+  
+  unsigned flag;
+  flag =
+      A |
+      H; // OK, disjoint value intervalls in the enum types ->probably good use.
+  flag = B | F; // Warning, have common values so they are probably misused.
+  
+  // Case 2:
+  enum Bitmask {
+    A = 0,
+    B = 1,
+    C = 2,
+    D = 4,
+    E = 8,
+    F = 16,
+    G = 31 // OK, real bitmask.
+  };
+  
+  enum Almostbitmask {
+    AA = 0,
+    BB = 1,
+    CC = 2,
+    DD = 4,
+    EE = 8,
+    FF = 16,
+    GG // Problem, forgot to initialize.
+  };
+  
+  unsigned flag = 0;
+  flag |= E; // OK.
+  flag |=
+      EE; // Warning at the decl, and note that it was used here as a bitmask.
+
+Options
+-------
+.. option:: StrictMode
+
+   Default value: 0.
+   When non-null the suspicious bitmask usage will be investigated additionally
+   to the different enum usage check.
diff --git a/docs/clang-tidy/checks/misc-suspicious-missing-comma.rst b/docs/clang-tidy/checks/misc-suspicious-missing-comma.rst
new file mode 100644 (file)
index 0000000..291f27e
--- /dev/null
@@ -0,0 +1,59 @@
+.. title:: clang-tidy - misc-suspicious-missing-comma
+
+misc-suspicious-missing-comma
+=============================
+
+String literals placed side-by-side are concatenated at translation phase 6
+(after the preprocessor). This feature is used to represent long string
+literal on multiple lines.
+
+For instance, the following declarations are equivalent:
+
+.. code-block:: c++
+
+  const char* A[] = "This is a test";
+  const char* B[] = "This" " is a "    "test";
+
+A common mistake done by programmers is to forget a comma between two string
+literals in an array initializer list.
+
+.. code-block:: c++
+
+  const char* Test[] = {
+    "line 1",
+    "line 2"     // Missing comma!
+    "line 3",
+    "line 4",
+    "line 5"
+  };
+
+The array contains the string "line 2line3" at offset 1 (i.e. Test[1]). Clang
+won't generate warnings at compile time.
+
+This check may warn incorrectly on cases like:
+
+.. code-block:: c++
+
+  const char* SupportedFormat[] = {
+    "Error %s",
+    "Code " PRIu64,   // May warn here.
+    "Warning %s",
+  };
+
+Options
+-------
+
+.. option::  SizeThreshold
+
+   An unsigned integer specifying the minimum size of a string literal to be
+   considered by the check. Default is `5U`.
+
+.. option::  RatioThreshold
+
+   A string specifying the maximum threshold ratio [0, 1.0] of suspicious string
+   literals to be considered. Default is `".2"`.
+
+.. option::  MaxConcatenatedTokens
+
+   An unsigned integer specifying the maximum number of concatenated tokens.
+   Default is `5U`.
diff --git a/docs/clang-tidy/checks/misc-suspicious-semicolon.rst b/docs/clang-tidy/checks/misc-suspicious-semicolon.rst
new file mode 100644 (file)
index 0000000..d38e6a6
--- /dev/null
@@ -0,0 +1,72 @@
+.. title:: clang-tidy - misc-suspicious-semicolon
+
+misc-suspicious-semicolon
+=========================
+
+Finds most instances of stray semicolons that unexpectedly alter the meaning of
+the code. More specifically, it looks for ``if``, ``while``, ``for`` and
+``for-range`` statements whose body is a single semicolon, and then analyzes the
+context of the code (e.g. indentation) in an attempt to determine whether that
+is intentional.
+
+  .. code-block:: c++
+
+    if (x < y);
+    {
+      x++;
+    }
+
+Here the body of the ``if`` statement consists of only the semicolon at the end
+of the first line, and `x` will be incremented regardless of the condition.
+
+
+  .. code-block:: c++
+
+    while ((line = readLine(file)) != NULL);
+      processLine(line);
+
+As a result of this code, `processLine()` will only be called once, when the
+``while`` loop with the empty body exits with `line == NULL`. The indentation of
+the code indicates the intention of the programmer.
+
+
+  .. code-block:: c++
+
+    if (x >= y);
+    x -= y;
+
+While the indentation does not imply any nesting, there is simply no valid
+reason to have an `if` statement with an empty body (but it can make sense for
+a loop). So this check issues a warning for the code above.
+
+To solve the issue remove the stray semicolon or in case the empty body is
+intentional, reflect this using code indentation or put the semicolon in a new
+line. For example:
+
+  .. code-block:: c++
+
+    while (readWhitespace());
+      Token t = readNextToken();
+
+Here the second line is indented in a way that suggests that it is meant to be
+the body of the `while` loop - whose body is in fact empty, because of the
+semicolon at the end of the first line.
+
+Either remove the indentation from the second line:
+
+  .. code-block:: c++
+
+    while (readWhitespace());
+    Token t = readNextToken();
+
+... or move the semicolon from the end of the first line to a new line:
+
+  .. code-block:: c++
+
+    while (readWhitespace())
+      ;
+
+      Token t = readNextToken();
+
+In this case the check will assume that you know what you are doing, and will
+not raise a warning.
diff --git a/docs/clang-tidy/checks/misc-suspicious-string-compare.rst b/docs/clang-tidy/checks/misc-suspicious-string-compare.rst
new file mode 100644 (file)
index 0000000..9918dee
--- /dev/null
@@ -0,0 +1,64 @@
+.. title:: clang-tidy - misc-suspicious-string-compare
+
+misc-suspicious-string-compare
+==============================
+
+Find suspicious usage of runtime string comparison functions.
+This check is valid in C and C++.
+
+Checks for calls with implicit comparator and proposed to explicitly add it.
+
+.. code-block:: c++
+
+    if (strcmp(...))       // Implicitly compare to zero
+    if (!strcmp(...))      // Won't warn
+    if (strcmp(...) != 0)  // Won't warn
+
+Checks that compare function results (i,e, ``strcmp``) are compared to valid
+constant. The resulting value is
+
+.. code::
+
+    <  0    when lower than,
+    >  0    when greater than,
+    == 0    when equals.
+
+A common mistake is to compare the result to `1` or `-1`.
+
+.. code-block:: c++
+
+    if (strcmp(...) == -1)  // Incorrect usage of the returned value.
+
+Additionally, the check warns if the results value is implicitly cast to a
+*suspicious* non-integer type. It's happening when the returned value is used in
+a wrong context.
+
+.. code-block:: c++
+
+    if (strcmp(...) < 0.)  // Incorrect usage of the returned value.
+
+Options
+-------
+
+.. option:: WarnOnImplicitComparison
+
+   When non-zero, the check will warn on implicit comparison. `1` by default.
+
+.. option:: WarnOnLogicalNotComparison
+
+   When non-zero, the check will warn on logical not comparison. `0` by default.
+
+.. option:: StringCompareLikeFunctions
+
+   A string specifying the comma-separated names of the extra string comparison
+   functions. Default is an empty string.
+   The check will detect the following string comparison functions:
+   `__builtin_memcmp`, `__builtin_strcasecmp`, `__builtin_strcmp`,
+   `__builtin_strncasecmp`, `__builtin_strncmp`, `_mbscmp`, `_mbscmp_l`,
+   `_mbsicmp`, `_mbsicmp_l`, `_mbsnbcmp`, `_mbsnbcmp_l`, `_mbsnbicmp`,
+   `_mbsnbicmp_l`, `_mbsncmp`, `_mbsncmp_l`, `_mbsnicmp`, `_mbsnicmp_l`,
+   `_memicmp`, `_memicmp_l`, `_stricmp`, `_stricmp_l`, `_strnicmp`,
+   `_strnicmp_l`, `_wcsicmp`, `_wcsicmp_l`, `_wcsnicmp`, `_wcsnicmp_l`,
+   `lstrcmp`, `lstrcmpi`, `memcmp`, `memicmp`, `strcasecmp`, `strcmp`,
+   `strcmpi`, `stricmp`, `strncasecmp`, `strncmp`, `strnicmp`, `wcscasecmp`,
+   `wcscmp`, `wcsicmp`, `wcsncmp`, `wcsnicmp`, `wmemcmp`.
diff --git a/docs/clang-tidy/checks/misc-swapped-arguments.rst b/docs/clang-tidy/checks/misc-swapped-arguments.rst
new file mode 100644 (file)
index 0000000..67a604a
--- /dev/null
@@ -0,0 +1,7 @@
+.. title:: clang-tidy - misc-swapped-arguments
+
+misc-swapped-arguments
+======================
+
+
+Finds potentially swapped arguments by looking at implicit conversions.
diff --git a/docs/clang-tidy/checks/misc-throw-by-value-catch-by-reference.rst b/docs/clang-tidy/checks/misc-throw-by-value-catch-by-reference.rst
new file mode 100644 (file)
index 0000000..1a6d6b3
--- /dev/null
@@ -0,0 +1,34 @@
+.. title:: clang-tidy - misc-throw-by-value-catch-by-reference
+
+misc-throw-by-value-catch-by-reference
+======================================
+
+"cert-err09-cpp" redirects here as an alias for this check.
+"cert-err61-cpp" redirects here as an alias for this check.
+
+Finds violations of the rule "Throw by value, catch by reference" presented for
+example in "C++ Coding Standards" by H. Sutter and A. Alexandrescu.
+
+Exceptions:
+  * Throwing string literals will not be flagged despite being a pointer. They
+    are not susceptible to slicing and the usage of string literals is idomatic.
+  * Catching character pointers (``char``, ``wchar_t``, unicode character types)
+    will not be flagged to allow catching sting literals.
+  * Moved named values will not be flagged as not throwing an anonymous
+    temporary. In this case we can be sure that the user knows that the object
+    can't be accessed outside catch blocks handling the error.
+  * Throwing function parameters will not be flagged as not throwing an
+    anonymous temporary. This allows helper functions for throwing.
+  * Re-throwing caught exception variables will not be flragged as not throwing
+    an anonymous temporary. Although this can usually be done by just writing
+    ``throw;`` it happens often enough in real code.
+
+Options
+-------
+
+.. option:: CheckThrowTemporaries
+
+   Triggers detection of violations of the rule `Throw anonymous temporaries
+   <https://www.securecoding.cert.org/confluence/display/cplusplus/ERR09-CPP.+Throw+anonymous+temporaries>`_.
+   Default is `1`.
+
diff --git a/docs/clang-tidy/checks/misc-unconventional-assign-operator.rst b/docs/clang-tidy/checks/misc-unconventional-assign-operator.rst
new file mode 100644 (file)
index 0000000..8b85332
--- /dev/null
@@ -0,0 +1,13 @@
+.. title:: clang-tidy - misc-unconventional-assign-operator
+
+misc-unconventional-assign-operator
+===================================
+
+
+Finds declarations of assign operators with the wrong return and/or argument
+types and definitions with good return type but wrong ``return`` statements.
+
+  * The return type must be ``Class&``.
+  * Works with move-assign and assign by value.
+  * Private and deleted operators are ignored.
+  * The operator must always return ``*this``.
diff --git a/docs/clang-tidy/checks/misc-undelegated-constructor.rst b/docs/clang-tidy/checks/misc-undelegated-constructor.rst
new file mode 100644 (file)
index 0000000..3b7fffc
--- /dev/null
@@ -0,0 +1,11 @@
+.. title:: clang-tidy - misc-undelegated-constructor
+
+misc-undelegated-constructor
+============================
+
+
+Finds creation of temporary objects in constructors that look like a
+function call to another constructor of the same class.
+
+The user most likely meant to use a delegating constructor or base class
+initializer.
diff --git a/docs/clang-tidy/checks/misc-uniqueptr-reset-release.rst b/docs/clang-tidy/checks/misc-uniqueptr-reset-release.rst
new file mode 100644 (file)
index 0000000..858fbe7
--- /dev/null
@@ -0,0 +1,16 @@
+.. title:: clang-tidy - misc-uniqueptr-reset-release
+
+misc-uniqueptr-reset-release
+============================
+
+Find and replace ``unique_ptr::reset(release())`` with ``std::move()``.
+
+Example:
+
+.. code-block:: c++
+
+  std::unique_ptr<Foo> x, y;
+  x.reset(y.release()); -> x = std::move(y);
+
+If ``y`` is already rvalue, ``std::move()`` is not added. ``x`` and ``y`` can
+also be ``std::unique_ptr<Foo>*``.
diff --git a/docs/clang-tidy/checks/misc-unused-alias-decls.rst b/docs/clang-tidy/checks/misc-unused-alias-decls.rst
new file mode 100644 (file)
index 0000000..d0e8c71
--- /dev/null
@@ -0,0 +1,7 @@
+.. title:: clang-tidy - misc-unused-alias-decls
+
+misc-unused-alias-decls
+=======================
+
+
+Finds unused namespace alias declarations.
diff --git a/docs/clang-tidy/checks/misc-unused-parameters.rst b/docs/clang-tidy/checks/misc-unused-parameters.rst
new file mode 100644 (file)
index 0000000..de868b9
--- /dev/null
@@ -0,0 +1,26 @@
+.. title:: clang-tidy - misc-unused-parameters
+
+misc-unused-parameters
+======================
+
+Finds unused parameters and fixes them, so that `-Wunused-parameter` can be
+turned on.
+
+.. code-block:: c++
+
+  void a(int i) {}
+
+  // becomes
+
+  void a(int  /*i*/) {}
+
+
+.. code-block:: c++
+
+  static void staticFunctionA(int i);
+  static void staticFunctionA(int i) {}
+
+  // becomes
+
+  static void staticFunctionA()
+  static void staticFunctionA() {}
diff --git a/docs/clang-tidy/checks/misc-unused-raii.rst b/docs/clang-tidy/checks/misc-unused-raii.rst
new file mode 100644 (file)
index 0000000..d081702
--- /dev/null
@@ -0,0 +1,30 @@
+.. title:: clang-tidy - misc-unused-raii
+
+misc-unused-raii
+================
+
+Finds temporaries that look like RAII objects.
+
+The canonical example for this is a scoped lock.
+
+.. code-block:: c++
+
+  {
+    scoped_lock(&global_mutex);
+    critical_section();
+  }
+
+The destructor of the scoped_lock is called before the ``critical_section`` is
+entered, leaving it unprotected.
+
+We apply a number of heuristics to reduce the false positive count of this
+check:
+
+- Ignore code expanded from macros. Testing frameworks make heavy use of this.
+
+- Ignore types with trivial destructors. They are very unlikely to be RAII
+  objects and there's no difference when they are deleted.
+
+- Ignore objects at the end of a compound statement (doesn't change behavior).
+
+- Ignore objects returned from a call.
diff --git a/docs/clang-tidy/checks/misc-unused-using-decls.rst b/docs/clang-tidy/checks/misc-unused-using-decls.rst
new file mode 100644 (file)
index 0000000..9de1cb2
--- /dev/null
@@ -0,0 +1,13 @@
+.. title:: clang-tidy - misc-unused-using-decls
+
+misc-unused-using-decls
+=======================
+
+Finds unused ``using`` declarations.
+
+Example:
+
+.. code-block:: c++
+
+  namespace n { class C; }
+  using n::C;  // Never actually used.
diff --git a/docs/clang-tidy/checks/misc-use-after-move.rst b/docs/clang-tidy/checks/misc-use-after-move.rst
new file mode 100644 (file)
index 0000000..e658608
--- /dev/null
@@ -0,0 +1,203 @@
+.. title:: clang-tidy - misc-use-after-move
+
+misc-use-after-move
+===================
+
+Warns if an object is used after it has been moved, for example:
+
+  .. code-block:: c++
+
+    std::string str = "Hello, world!\n";
+    std::vector<std::string> messages;
+    messages.emplace_back(std::move(str));
+    std::cout << str;
+
+The last line will trigger a warning that ``str`` is used after it has been
+moved.
+
+The check does not trigger a warning if the object is reinitialized after the
+move and before the use. For example, no warning will be output for this code:
+
+  .. code-block:: c++
+
+    messages.emplace_back(std::move(str));
+    str = "Greetings, stranger!\n";
+    std::cout << str;
+
+The check takes control flow into account. A warning is only emitted if the use
+can be reached from the move. This means that the following code does not
+produce a warning:
+
+  .. code-block:: c++
+
+    if (condition) {
+      messages.emplace_back(std::move(str));
+    } else {
+      std::cout << str;
+    }
+
+On the other hand, the following code does produce a warning:
+
+  .. code-block:: c++
+
+    for (int i = 0; i < 10; ++i) {
+      std::cout << str;
+      messages.emplace_back(std::move(str));
+    }
+
+(The use-after-move happens on the second iteration of the loop.)
+
+In some cases, the check may not be able to detect that two branches are
+mutually exclusive. For example (assuming that ``i`` is an int):
+
+  .. code-block:: c++
+
+    if (i == 1) {
+      messages.emplace_back(std::move(str));
+    }
+    if (i == 2) {
+      std::cout << str;
+    }
+
+In this case, the check will erroneously produce a warning, even though it is
+not possible for both the move and the use to be executed.
+
+An erroneous warning can be silenced by reinitializing the object after the
+move:
+
+  .. code-block:: c++
+
+    if (i == 1) {
+      messages.emplace_back(std::move(str));
+      str = "";
+    }
+    if (i == 2) {
+      std::cout << str;
+    }
+
+Subsections below explain more precisely what exactly the check considers to be
+a move, use, and reinitialization.
+
+Unsequenced moves, uses, and reinitializations
+----------------------------------------------
+
+In many cases, C++ does not make any guarantees about the order in which
+sub-expressions of a statement are evaluated. This means that in code like the
+following, it is not guaranteed whether the use will happen before or after the
+move:
+
+  .. code-block:: c++
+
+    void f(int i, std::vector<int> v);
+    std::vector<int> v = { 1, 2, 3 };
+    f(v[1], std::move(v));
+
+In this kind of situation, the check will note that the use and move are
+unsequenced.
+
+The check will also take sequencing rules into account when reinitializations
+occur in the same statement as moves or uses. A reinitialization is only
+considered to reinitialize a variable if it is guaranteed to be evaluated after
+the move and before the use.
+
+Move
+----
+
+The check currently only considers calls of ``std::move`` on local variables or
+function parameters. It does not check moves of member variables or global
+variables.
+
+Any call of ``std::move`` on a variable is considered to cause a move of that
+variable, even if the result of ``std::move`` is not passed to an rvalue
+reference parameter.
+
+This means that the check will flag a use-after-move even on a type that does
+not define a move constructor or move assignment operator. This is intentional.
+Developers may use ``std::move`` on such a type in the expectation that the type
+will add move semantics in the future. If such a ``std::move`` has the potential
+to cause a use-after-move, we want to warn about it even if the type does not
+implement move semantics yet.
+
+Furthermore, if the result of ``std::move`` *is* passed to an rvalue reference
+parameter, this will always be considered to cause a move, even if the function
+that consumes this parameter does not move from it, or if it does so only
+conditionally. For example, in the following situation, the check will assume
+that a move always takes place:
+
+  .. code-block:: c++
+
+    std::vector<std::string> messages;
+    void f(std::string &&str) {
+      // Only remember the message if it isn't empty.
+      if (!str.empty()) {
+        messages.emplace_back(std::move(str));
+      }
+    }
+    std::string str = "";
+    f(std::move(str));
+
+The check will assume that the last line causes a move, even though, in this
+particular case, it does not. Again, this is intentional.
+
+When analyzing the order in which moves, uses and reinitializations happen (see
+section `Unsequenced moves, uses, and reinitializations`_), the move is assumed
+to occur in whichever function the result of the ``std::move`` is passed to.
+
+Use
+---
+
+Any occurrence of the moved variable that is not a reinitialization (see below)
+is considered to be a use.
+
+An exception to this are objects of type ``std::unique_ptr``,
+``std::shared_ptr`` and ``std::weak_ptr``, which have defined move behavior
+(objects of these classes are guaranteed to be empty after they have been moved
+from). Therefore, an object of these classes will only be considered to be used
+if it is dereferenced, i.e. if ``operator*``, ``operator->`` or ``operator[]``
+(in the case of ``std::unique_ptr<T []>``) is called on it.
+
+If multiple uses occur after a move, only the first of these is flagged.
+
+Reinitialization
+----------------
+
+The check considers a variable to be reinitialized in the following cases:
+
+  - The variable occurs on the left-hand side of an assignment.
+
+  - The variable is passed to a function as a non-const pointer or non-const
+    lvalue reference. (It is assumed that the variable may be an out-parameter
+    for the function.)
+
+  - ``clear()`` or ``assign()`` is called on the variable and the variable is of
+    one of the standard container types ``basic_string``, ``vector``, ``deque``,
+    ``forward_list``, ``list``, ``set``, ``map``, ``multiset``, ``multimap``,
+    ``unordered_set``, ``unordered_map``, ``unordered_multiset``,
+    ``unordered_multimap``.
+
+  - ``reset()`` is called on the variable and the variable is of type
+    ``std::unique_ptr``, ``std::shared_ptr`` or ``std::weak_ptr``.
+
+If the variable in question is a struct and an individual member variable of
+that struct is written to, the check does not consider this to be a
+reinitialization -- even if, eventually, all member variables of the struct are
+written to. For example:
+
+  .. code-block:: c++
+
+    struct S {
+      std::string str;
+      int i;
+    };
+    S s = { "Hello, world!\n", 42 };
+    S s_other = std::move(s);
+    s.str = "Lorem ipsum";
+    s.i = 99;
+
+The check will not consider ``s`` to be reinitialized after the last line;
+instead, the line that assigns to ``s.str`` will be flagged as a use-after-move.
+This is intentional as this pattern of reinitializing a struct is error-prone.
+For example, if an additional member variable is added to ``S``, it is easy to
+forget to add the reinitialization for this additional member. Instead, it is
+safer to assign to the entire struct in one go, and this will also avoid the
+use-after-move warning.
diff --git a/docs/clang-tidy/checks/misc-virtual-near-miss.rst b/docs/clang-tidy/checks/misc-virtual-near-miss.rst
new file mode 100644 (file)
index 0000000..6871989
--- /dev/null
@@ -0,0 +1,20 @@
+.. title:: clang-tidy - misc-virtual-near-miss
+
+misc-virtual-near-miss
+======================
+
+Warn if a function is a near miss (ie. the name is very similar and the function
+signiture is the same) to a virtual function from a base class.
+
+Example:
+
+.. code-block:: c++
+
+  struct Base {
+    virtual void func();
+  };
+
+  struct Derived : Base {
+    virtual funk();
+    // warning: 'Derived::funk' has a similar name and the same signature as virtual method 'Base::func'; did you mean to override it?
+  };
diff --git a/docs/clang-tidy/checks/modernize-avoid-bind.rst b/docs/clang-tidy/checks/modernize-avoid-bind.rst
new file mode 100644 (file)
index 0000000..7ea9bec
--- /dev/null
@@ -0,0 +1,37 @@
+.. title:: clang-tidy - modernize-avoid-bind
+
+modernize-avoid-bind
+====================
+
+The check finds uses of ``std::bind`` and replaces simple uses with lambdas.
+Lambdas will use value-capture where required.
+
+Right now it only handles free functions, not member functions.
+
+Given:
+
+.. code-block:: c++
+
+  int add(int x, int y) { return x + y; }
+
+Then:
+
+.. code-block:: c++
+
+  void f() {
+    int x = 2;
+    auto clj = std::bind(add, x, _1);
+  }
+
+is replaced by:
+
+.. code-block:: c++
+
+  void f() {
+    int x = 2;
+    auto clj = [=](auto && arg1) { return add(x, arg1); };
+  }
+
+``std::bind`` can be hard to read and can result in larger object files and
+binaries due to type information that will not be produced by equivalent
+lambdas.
diff --git a/docs/clang-tidy/checks/modernize-deprecated-headers.rst b/docs/clang-tidy/checks/modernize-deprecated-headers.rst
new file mode 100644 (file)
index 0000000..4c4750a
--- /dev/null
@@ -0,0 +1,49 @@
+.. title:: clang-tidy - modernize-deprecated-headers
+
+modernize-deprecated-headers
+============================
+
+Some headers from C library were deprecated in C++ and are no longer welcome in
+C++ codebases. Some have no effect in C++. For more details refer to the C++ 14
+Standard [depr.c.headers] section.
+
+This check replaces C standard library headers with their C++ alternatives and
+removes redundant ones.
+
+Improtant note: the Standard doesn't guarantee that the C++ headers declare all
+the same functions in the global namespace. The check in its current form can
+break the code that uses library symbols from the global namespace.
+
+* `<assert.h>`
+* `<complex.h>`
+* `<ctype.h>`
+* `<errno.h>`
+* `<fenv.h>`     // deprecated since C++11
+* `<float.h>`
+* `<inttypes.h>`
+* `<limits.h>`
+* `<locale.h>`
+* `<math.h>`
+* `<setjmp.h>`
+* `<signal.h>`
+* `<stdarg.h>`
+* `<stddef.h>`
+* `<stdint.h>`
+* `<stdio.h>`
+* `<stdlib.h>`
+* `<string.h>`
+* `<tgmath.h>`   // deprecated since C++11
+* `<time.h>`
+* `<uchar.h>`    // deprecated since C++11
+* `<wchar.h>`
+* `<wctype.h>`
+
+If the specified standard is older than C++11 the check will only replace
+headers deprecated before C++11, otherwise -- every header that appeared in
+the previous list.
+
+These headers don't have effect in C++:
+
+* `<iso646.h>`
+* `<stdalign.h>`
+* `<stdbool.h>`
diff --git a/docs/clang-tidy/checks/modernize-loop-convert.rst b/docs/clang-tidy/checks/modernize-loop-convert.rst
new file mode 100644 (file)
index 0000000..bad574f
--- /dev/null
@@ -0,0 +1,255 @@
+.. title:: clang-tidy - modernize-loop-convert
+
+modernize-loop-convert
+======================
+
+This check converts ``for(...; ...; ...)`` loops to use the new range-based
+loops in C++11.
+
+Three kinds of loops can be converted:
+
+-  Loops over statically allocated arrays.
+-  Loops over containers, using iterators.
+-  Loops over array-like containers, using ``operator[]`` and ``at()``.
+
+MinConfidence option
+--------------------
+
+risky
+^^^^^
+
+In loops where the container expression is more complex than just a
+reference to a declared expression (a variable, function, enum, etc.),
+and some part of it appears elsewhere in the loop, we lower our confidence
+in the transformation due to the increased risk of changing semantics.
+Transformations for these loops are marked as `risky`, and thus will only
+be converted if the minimum required confidence level is set to `risky`.
+
+.. code-block:: c++
+
+  int arr[10][20];
+  int l = 5;
+
+  for (int j = 0; j < 20; ++j)
+    int k = arr[l][j] + l; // using l outside arr[l] is considered risky
+
+  for (int i = 0; i < obj.getVector().size(); ++i)
+    obj.foo(10); // using 'obj' is considered risky
+
+See
+:ref:`Range-based loops evaluate end() only once<IncorrectRiskyTransformation>`
+for an example of an incorrect transformation when the minimum required confidence
+level is set to `risky`.
+
+reasonable (Default)
+^^^^^^^^^^^^^^^^^^^^
+
+If a loop calls ``.end()`` or ``.size()`` after each iteration, the
+transformation for that loop is marked as `reasonable`, and thus will
+be converted if the required confidence level is set to `reasonable`
+(default) or lower.
+
+.. code-block:: c++
+
+  // using size() is considered reasonable
+  for (int i = 0; i < container.size(); ++i)
+    cout << container[i];
+
+safe
+^^^^
+
+Any other loops that do not match the above criteria to be marked as
+`risky` or `reasonable` are marked `safe`, and thus will be converted
+if the required confidence level is set to `safe` or lower.
+
+.. code-block:: c++
+
+  int arr[] = {1,2,3};
+
+  for (int i = 0; i < 3; ++i)
+    cout << arr[i];
+
+Example
+-------
+
+Original:
+
+.. code-block:: c++
+
+  const int N = 5;
+  int arr[] = {1,2,3,4,5};
+  vector<int> v;
+  v.push_back(1);
+  v.push_back(2);
+  v.push_back(3);
+
+  // safe conversion
+  for (int i = 0; i < N; ++i)
+    cout << arr[i];
+
+  // reasonable conversion
+  for (vector<int>::iterator it = v.begin(); it != v.end(); ++it)
+    cout << *it;
+
+  // reasonable conversion
+  for (int i = 0; i < v.size(); ++i)
+    cout << v[i];
+
+After applying the check with minimum confidence level set to `reasonable` (default):
+
+.. code-block:: c++
+
+  const int N = 5;
+  int arr[] = {1,2,3,4,5};
+  vector<int> v;
+  v.push_back(1);
+  v.push_back(2);
+  v.push_back(3);
+
+  // safe conversion
+  for (auto & elem : arr)
+    cout << elem;
+
+  // reasonable conversion
+  for (auto & elem : v)
+    cout << elem;
+
+  // reasonable conversion
+  for (auto & elem : v)
+    cout << elem;
+
+Limitations
+-----------
+
+There are certain situations where the tool may erroneously perform
+transformations that remove information and change semantics. Users of the tool
+should be aware of the behaviour and limitations of the check outlined by
+the cases below.
+
+Comments inside loop headers
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Comments inside the original loop header are ignored and deleted when
+transformed.
+
+.. code-block:: c++
+
+  for (int i = 0; i < N; /* This will be deleted */ ++i) { }
+
+Range-based loops evaluate end() only once
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The C++11 range-based for loop calls ``.end()`` only once during the
+initialization of the loop. If in the original loop ``.end()`` is called after
+each iteration the semantics of the transformed loop may differ.
+
+.. code-block:: c++
+
+  // The following is semantically equivalent to the C++11 range-based for loop,
+  // therefore the semantics of the header will not change.
+  for (iterator it = container.begin(), e = container.end(); it != e; ++it) { }
+
+  // Instead of calling .end() after each iteration, this loop will be
+  // transformed to call .end() only once during the initialization of the loop,
+  // which may affect semantics.
+  for (iterator it = container.begin(); it != container.end(); ++it) { }
+
+.. _IncorrectRiskyTransformation:
+
+As explained above, calling member functions of the container in the body
+of the loop is considered `risky`. If the called member function modifies the
+container the semantics of the converted loop will differ due to ``.end()``
+being called only once.
+
+.. code-block:: c++
+
+  bool flag = false;
+  for (vector<T>::iterator it = vec.begin(); it != vec.end(); ++it) {
+    // Add a copy of the first element to the end of the vector.
+    if (!flag) {
+      // This line makes this transformation 'risky'.
+      vec.push_back(*it);
+      flag = true;
+    }
+    cout << *it;
+  }
+
+The original code above prints out the contents of the container including the
+newly added element while the converted loop, shown below, will only print the
+original contents and not the newly added element.
+
+.. code-block:: c++
+
+  bool flag = false;
+  for (auto & elem : vec) {
+    // Add a copy of the first element to the end of the vector.
+    if (!flag) {
+      // This line makes this transformation 'risky'
+      vec.push_back(elem);
+      flag = true;
+    }
+    cout << elem;
+  }
+
+Semantics will also be affected if ``.end()`` has side effects. For example, in
+the case where calls to ``.end()`` are logged the semantics will change in the
+transformed loop if ``.end()`` was originally called after each iteration.
+
+.. code-block:: c++
+
+  iterator end() {
+    num_of_end_calls++;
+    return container.end();
+  }
+
+Overloaded operator->() with side effects
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Similarly, if ``operator->()`` was overloaded to have side effects, such as
+logging, the semantics will change. If the iterator's ``operator->()`` was used
+in the original loop it will be replaced with ``<container element>.<member>``
+instead due to the implicit dereference as part of the range-based for loop.
+Therefore any side effect of the overloaded ``operator->()`` will no longer be
+performed.
+
+.. code-block:: c++
+
+  for (iterator it = c.begin(); it != c.end(); ++it) {
+    it->func(); // Using operator->()
+  }
+  // Will be transformed to:
+  for (auto & elem : c) {
+    elem.func(); // No longer using operator->()
+  }
+
+Pointers and references to containers
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+While most of the check's risk analysis is dedicated to determining whether
+the iterator or container was modified within the loop, it is possible to
+circumvent the analysis by accessing and modifying the container through a
+pointer or reference.
+
+If the container were directly used instead of using the pointer or reference
+the following transformation would have only been applied at the `risky`
+level since calling a member function of the container is considered `risky`.
+The check cannot identify expressions associated with the container that are
+different than the one used in the loop header, therefore the transformation
+below ends up being performed at the `safe` level.
+
+.. code-block:: c++
+
+  vector<int> vec;
+
+  vector<int> *ptr = &vec;
+  vector<int> &ref = vec;
+
+  for (vector<int>::iterator it = vec.begin(), e = vec.end(); it != e; ++it) {
+    if (!flag) {
+      // Accessing and modifying the container is considered risky, but the risk
+      // level is not raised here.
+      ptr->push_back(*it);
+      ref.push_back(*it);
+      flag = true;
+    }
+  }
diff --git a/docs/clang-tidy/checks/modernize-make-shared.rst b/docs/clang-tidy/checks/modernize-make-shared.rst
new file mode 100644 (file)
index 0000000..cf59d19
--- /dev/null
@@ -0,0 +1,45 @@
+.. title:: clang-tidy - modernize-make-shared
+
+modernize-make-shared
+=====================
+
+This check finds the creation of ``std::shared_ptr`` objects by explicitly
+calling the constructor and a ``new`` expression, and replaces it with a call
+to ``std::make_shared``.
+
+.. code-block:: c++
+
+  auto my_ptr = std::shared_ptr<MyPair>(new MyPair(1, 2));
+
+  // becomes
+
+  auto my_ptr = std::make_shared<MyPair>(1, 2);
+
+This check also finds calls to ``std::shared_ptr::reset()`` with a ``new``
+expression, and replaces it with a call to ``std::make_shared``.
+
+.. code-block:: c++
+
+  my_ptr.reset(new MyPair(1, 2));
+
+  // becomes
+
+  my_ptr = std::make_shared<MyPair>(1, 2);
+
+Options
+-------
+
+.. option:: MakeSmartPtrFunction
+
+   A string specifying the name of make-shared-ptr function. Default is
+   `std::make_shared`.
+
+.. option:: MakeSmartPtrFunctionHeader
+
+   A string specifying the corresponding header of make-shared-ptr function.
+   Default is `memory`.
+
+.. option:: IncludeStyle
+
+   A string specifying which include-style is used, `llvm` or `google`. Default
+   is `llvm`.
diff --git a/docs/clang-tidy/checks/modernize-make-unique.rst b/docs/clang-tidy/checks/modernize-make-unique.rst
new file mode 100644 (file)
index 0000000..e6b17db
--- /dev/null
@@ -0,0 +1,45 @@
+.. title:: clang-tidy - modernize-make-unique
+
+modernize-make-unique
+=====================
+
+This check finds the creation of ``std::unique_ptr`` objects by explicitly
+calling the constructor and a ``new`` expression, and replaces it with a call
+to ``std::make_unique``, introduced in C++14.
+
+.. code-block:: c++
+
+  auto my_ptr = std::unique_ptr<MyPair>(new MyPair(1, 2));
+
+  // becomes
+
+  auto my_ptr = std::make_unique<MyPair>(1, 2);
+
+This check also finds calls to ``std::unique_ptr::reset()`` with a ``new``
+expression, and replaces it with a call to ``std::make_unique``.
+
+.. code-block:: c++
+
+  my_ptr.reset(new MyPair(1, 2));
+
+  // becomes
+
+  my_ptr = std::make_unique<MyPair>(1, 2);
+
+Options
+-------
+
+.. option:: MakeSmartPtrFunction
+
+   A string specifying the name of make-unique-ptr function. Default is
+   `std::make_unique`.
+
+.. option:: MakeSmartPtrFunctionHeader
+
+   A string specifying the corresponding header of make-unique-ptr function.
+   Default is `memory`.
+
+.. option:: IncludeStyle
+
+   A string specifying which include-style is used, `llvm` or `google`. Default
+   is `llvm`.
diff --git a/docs/clang-tidy/checks/modernize-pass-by-value.rst b/docs/clang-tidy/checks/modernize-pass-by-value.rst
new file mode 100644 (file)
index 0000000..1dfbf95
--- /dev/null
@@ -0,0 +1,166 @@
+.. title:: clang-tidy - modernize-pass-by-value
+
+modernize-pass-by-value
+=======================
+
+With move semantics added to the language and the standard library updated with
+move constructors added for many types it is now interesting to take an
+argument directly by value, instead of by const-reference, and then copy. This
+check allows the compiler to take care of choosing the best way to construct
+the copy.
+
+The transformation is usually beneficial when the calling code passes an
+*rvalue* and assumes the move construction is a cheap operation. This short
+example illustrates how the construction of the value happens:
+
+  .. code-block:: c++
+
+    void foo(std::string s);
+    std::string get_str();
+
+    void f(const std::string &str) {
+      foo(str);       // lvalue  -> copy construction
+      foo(get_str()); // prvalue -> move construction
+    }
+
+.. note::
+
+   Currently, only constructors are transformed to make use of pass-by-value.
+   Contributions that handle other situations are welcome!
+
+
+Pass-by-value in constructors
+-----------------------------
+
+Replaces the uses of const-references constructor parameters that are copied
+into class fields. The parameter is then moved with `std::move()`.
+
+Since ``std::move()`` is a library function declared in `<utility>` it may be
+necessary to add this include. The check will add the include directive when
+necessary.
+
+  .. code-block:: c++
+
+     #include <string>
+
+     class Foo {
+     public:
+    -  Foo(const std::string &Copied, const std::string &ReadOnly)
+    -    : Copied(Copied), ReadOnly(ReadOnly)
+    +  Foo(std::string Copied, const std::string &ReadOnly)
+    +    : Copied(std::move(Copied)), ReadOnly(ReadOnly)
+       {}
+
+     private:
+       std::string Copied;
+       const std::string &ReadOnly;
+     };
+
+     std::string get_cwd();
+
+     void f(const std::string &Path) {
+       // The parameter corresponding to 'get_cwd()' is move-constructed. By
+       // using pass-by-value in the Foo constructor we managed to avoid a
+       // copy-construction.
+       Foo foo(get_cwd(), Path);
+     }
+
+
+If the parameter is used more than once no transformation is performed since
+moved objects have an undefined state. It means the following code will be left
+untouched:
+
+.. code-block:: c++
+
+  #include <string>
+
+  void pass(const std::string &S);
+
+  struct Foo {
+    Foo(const std::string &S) : Str(S) {
+      pass(S);
+    }
+
+    std::string Str;
+  };
+
+
+Known limitations
+^^^^^^^^^^^^^^^^^
+
+A situation where the generated code can be wrong is when the object referenced
+is modified before the assignment in the init-list through a "hidden" reference.
+
+Example:
+
+.. code-block:: c++
+
+   std::string s("foo");
+
+   struct Base {
+     Base() {
+       s = "bar";
+     }
+   };
+
+   struct Derived : Base {
+  -  Derived(const std::string &S) : Field(S)
+  +  Derived(std::string S) : Field(std::move(S))
+     { }
+
+     std::string Field;
+   };
+
+   void f() {
+  -  Derived d(s); // d.Field holds "bar"
+  +  Derived d(s); // d.Field holds "foo"
+   }
+
+
+Note about delayed template parsing
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+When delayed template parsing is enabled, constructors part of templated
+contexts; templated constructors, constructors in class templates, constructors
+of inner classes of template classes, etc., are not transformed. Delayed
+template parsing is enabled by default on Windows as a Microsoft extension:
+`Clang Compiler User’s Manual - Microsoft extensions`_.
+
+Delayed template parsing can be enabled using the `-fdelayed-template-parsing`
+flag and disabled using `-fno-delayed-template-parsing`.
+
+Example:
+
+.. code-block:: c++
+
+   template <typename T> class C {
+     std::string S;
+
+   public:
+ =  // using -fdelayed-template-parsing (default on Windows)
+ =  C(const std::string &S) : S(S) {}
+
+ +  // using -fno-delayed-template-parsing (default on non-Windows systems)
+ +  C(std::string S) : S(std::move(S)) {}
+   };
+
+.. _Clang Compiler User’s Manual - Microsoft extensions: http://clang.llvm.org/docs/UsersManual.html#microsoft-extensions
+
+.. seealso::
+
+  For more information about the pass-by-value idiom, read: `Want Speed? Pass by Value`_.
+
+  .. _Want Speed? Pass by Value: https://web.archive.org/web/20140205194657/http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/
+
+Options
+-------
+
+.. option:: IncludeStyle
+
+   A string specifying which include-style is used, `llvm` or `google`. Default
+   is `llvm`.
+
+.. option:: ValuesOnly
+
+   When non-zero, the check only warns about copied parameters that are already
+   passed by value. Default is `0`.
diff --git a/docs/clang-tidy/checks/modernize-raw-string-literal.rst b/docs/clang-tidy/checks/modernize-raw-string-literal.rst
new file mode 100644 (file)
index 0000000..3525d57
--- /dev/null
@@ -0,0 +1,46 @@
+.. title:: clang-tidy - modernize-raw-string-literal
+
+modernize-raw-string-literal
+============================
+
+This check selectively replaces string literals containing escaped characters
+with raw string literals.
+
+Example:
+
+.. code-blocK:: c++
+
+  const char *const Quotes{"embedded \"quotes\""};
+  const char *const Paragraph{"Line one.\nLine two.\nLine three.\n"};
+  const char *const SingleLine{"Single line.\n"};
+  const char *const TrailingSpace{"Look here -> \n"};
+  const char *const Tab{"One\tTwo\n"};
+  const char *const Bell{"Hello!\a  And welcome!"};
+  const char *const Path{"C:\\Program Files\\Vendor\\Application.exe"};
+  const char *const RegEx{"\\w\\([a-z]\\)"};
+
+becomes
+
+.. code-block:: c++
+
+  const char *const Quotes{R"(embedded "quotes")"};
+  const char *const Paragraph{"Line one.\nLine two.\nLine three.\n"};
+  const char *const SingleLine{"Single line.\n"};
+  const char *const TrailingSpace{"Look here -> \n"};
+  const char *const Tab{"One\tTwo\n"};
+  const char *const Bell{"Hello!\a  And welcome!"};
+  const char *const Path{R"(C:\Program Files\Vendor\Application.exe)"};
+  const char *const RegEx{R"(\w\([a-z]\))"};
+
+The presence of any of the following escapes can cause the string to be
+converted to a raw string literal: ``\\``, ``\'``, ``\"``, ``\?``,
+and octal or hexadecimal escapes for printable ASCII characters.
+
+A string literal containing only escaped newlines is a common way of
+writing lines of text output. Introducing physical newlines with raw
+string literals in this case is likely to impede readability. These
+string literals are left unchanged.
+
+An escaped horizontal tab, form feed, or vertical tab prevents the string
+literal from being converted. The presence of a horizontal tab, form feed or
+vertical tab in source code is not visually obvious.
diff --git a/docs/clang-tidy/checks/modernize-redundant-void-arg.rst b/docs/clang-tidy/checks/modernize-redundant-void-arg.rst
new file mode 100644 (file)
index 0000000..d1a03e3
--- /dev/null
@@ -0,0 +1,18 @@
+.. title:: clang-tidy - modernize-redundant-void-arg
+
+modernize-redundant-void-arg
+============================
+
+Find and remove redundant ``void`` argument lists.
+
+Examples:
+  ===================================  ===========================
+  Initial code                         Code with applied fixes
+  ===================================  ===========================
+  ``int f(void);``                     ``int f();``
+  ``int (*f(void))(void);``            ``int (*f())();``
+  ``typedef int (*f_t(void))(void);``  ``typedef int (*f_t())();``
+  ``void (C::*p)(void);``              ``void (C::*p)();``
+  ``C::C(void) {}``                    ``C::C() {}``
+  ``C::~C(void) {}``                   ``C::~C() {}``
+  ===================================  ===========================
diff --git a/docs/clang-tidy/checks/modernize-replace-auto-ptr.rst b/docs/clang-tidy/checks/modernize-replace-auto-ptr.rst
new file mode 100644 (file)
index 0000000..1cbd68c
--- /dev/null
@@ -0,0 +1,79 @@
+.. title:: clang-tidy - modernize-replace-auto-ptr
+
+modernize-replace-auto-ptr
+==========================
+
+This check replaces the uses of the deprecated class ``std::auto_ptr`` by
+``std::unique_ptr`` (introduced in C++11). The transfer of ownership, done
+by the copy-constructor and the assignment operator, is changed to match
+``std::unique_ptr`` usage by using explicit calls to ``std::move()``.
+
+Migration example:
+
+.. code-block:: c++
+
+  -void take_ownership_fn(std::auto_ptr<int> int_ptr);
+  +void take_ownership_fn(std::unique_ptr<int> int_ptr);
+
+   void f(int x) {
+  -  std::auto_ptr<int> a(new int(x));
+  -  std::auto_ptr<int> b;
+  +  std::unique_ptr<int> a(new int(x));
+  +  std::unique_ptr<int> b;
+
+  -  b = a;
+  -  take_ownership_fn(b);
+  +  b = std::move(a);
+  +  take_ownership_fn(std::move(b));
+   }
+
+Since ``std::move()`` is a library function declared in ``<utility>`` it may be
+necessary to add this include. The check will add the include directive when
+necessary.
+
+Known Limitations
+-----------------
+* If headers modification is not activated or if a header is not allowed to be
+  changed this check will produce broken code (compilation error), where the
+  headers' code will stay unchanged while the code using them will be changed.
+
+* Client code that declares a reference to an ``std::auto_ptr`` coming from
+  code that can't be migrated (such as a header coming from a 3\ :sup:`rd`
+  party library) will produce a compilation error after migration. This is
+  because the type of the reference will be changed to ``std::unique_ptr`` but
+  the type returned by the library won't change, binding a reference to
+  ``std::unique_ptr`` from an ``std::auto_ptr``. This pattern doesn't make much
+  sense and usually ``std::auto_ptr`` are stored by value (otherwise what is
+  the point in using them instead of a reference or a pointer?).
+
+  .. code-block:: c++
+
+     // <3rd-party header...>
+     std::auto_ptr<int> get_value();
+     const std::auto_ptr<int> & get_ref();
+
+     // <calling code (with migration)...>
+    -std::auto_ptr<int> a(get_value());
+    +std::unique_ptr<int> a(get_value()); // ok, unique_ptr constructed from auto_ptr
+
+    -const std::auto_ptr<int> & p = get_ptr();
+    +const std::unique_ptr<int> & p = get_ptr(); // won't compile
+
+* Non-instantiated templates aren't modified.
+
+  .. code-block:: c++
+
+     template <typename X>
+     void f() {
+         std::auto_ptr<X> p;
+     }
+
+     // only 'f<int>()' (or similar) will trigger the replacement.
+
+Options
+-------
+
+.. option:: IncludeStyle
+
+   A string specifying which include-style is used, `llvm` or `google`. Default
+   is `llvm`.
diff --git a/docs/clang-tidy/checks/modernize-replace-random-shuffle.rst b/docs/clang-tidy/checks/modernize-replace-random-shuffle.rst
new file mode 100644 (file)
index 0000000..b0c2cd5
--- /dev/null
@@ -0,0 +1,28 @@
+.. title:: clang-tidy - modernize-replace-random-shuffle
+
+modernize-replace-random-shuffle
+================================
+
+This check will find occurrences of ``std::random_shuffle`` and replace it with ``std::shuffle``. In C++17 ``std::random_shuffle`` will no longer be available and thus we need to replace it.
+
+Below are two examples of what kind of occurrences will be found and two examples of what it will be replaced with.
+
+.. code-block:: c++
+
+  std::vector<int> v;
+
+  // First example
+  std::random_shuffle(vec.begin(), vec.end());
+
+  // Second example
+  std::random_shuffle(vec.begin(), vec.end(), randomFun);
+
+Both of these examples will be replaced with:
+
+.. code-block:: c++
+
+  std::shuffle(vec.begin(), vec.end(), std::mt19937(std::random_device()()));
+
+The second example will also receive a warning that ``randomFunc`` is no longer supported in the same way as before so if the user wants the same functionality, the user will need to change the implementation of the ``randomFunc``.
+
+One thing to be aware of here is that ``std::random_device`` is quite expensive to initialize. So if you are using the code in a performance critical place, you probably want to initialize it elsewhere. 
diff --git a/docs/clang-tidy/checks/modernize-return-braced-init-list.rst b/docs/clang-tidy/checks/modernize-return-braced-init-list.rst
new file mode 100644 (file)
index 0000000..2631d90
--- /dev/null
@@ -0,0 +1,22 @@
+.. title:: clang-tidy - modernize-return-braced-init-list
+
+modernize-return-braced-init-list
+=================================
+
+Replaces explicit calls to the constructor in a return with a braced
+initializer list. This way the return type is not needlessly duplicated in the
+function definition and the return statement.
+
+.. code:: c++
+
+  Foo bar() {
+    Baz baz;
+    return Foo(baz);
+  }
+
+  // transforms to:
+
+  Foo bar() {
+    Baz baz;
+    return {baz};
+  }
diff --git a/docs/clang-tidy/checks/modernize-shrink-to-fit.rst b/docs/clang-tidy/checks/modernize-shrink-to-fit.rst
new file mode 100644 (file)
index 0000000..4d7ffaf
--- /dev/null
@@ -0,0 +1,12 @@
+.. title:: clang-tidy - modernize-shrink-to-fit
+
+modernize-shrink-to-fit
+=======================
+
+
+Replace copy and swap tricks on shrinkable containers with the
+``shrink_to_fit()`` method call.
+
+The ``shrink_to_fit()`` method is more readable and more effective than
+the copy and swap trick to reduce the capacity of a shrinkable container.
+Note that, the ``shrink_to_fit()`` method is only available in C++11 and up.
diff --git a/docs/clang-tidy/checks/modernize-unary-static-assert.rst b/docs/clang-tidy/checks/modernize-unary-static-assert.rst
new file mode 100644 (file)
index 0000000..31ece10
--- /dev/null
@@ -0,0 +1,25 @@
+.. title:: clang-tidy - modernize-unary-static-assert
+
+modernize-unary-static-assert
+=============================
+
+The check diagnoses any ``static_assert`` declaration with an empty string literal
+and provides a fix-it to replace the declaration with a single-argument ``static_assert`` declaration.
+
+The check is only applicable for C++17 and later code.
+
+The following code:
+
+.. code-block:: c++
+
+  void f_textless(int a) {
+    static_assert(sizeof(a) <= 10, "");
+  }
+
+is replaced by:
+
+.. code-block:: c++
+
+  void f_textless(int a) {
+    static_assert(sizeof(a) <= 10);
+  }
diff --git a/docs/clang-tidy/checks/modernize-use-auto.rst b/docs/clang-tidy/checks/modernize-use-auto.rst
new file mode 100644 (file)
index 0000000..157b26c
--- /dev/null
@@ -0,0 +1,196 @@
+.. title:: clang-tidy - modernize-use-auto
+
+modernize-use-auto
+==================
+
+This check is responsible for using the ``auto`` type specifier for variable
+declarations to *improve code readability and maintainability*. For example:
+
+.. code-block:: c++
+
+  std::vector<int>::iterator I = my_container.begin();
+
+  // transforms to:
+
+  auto I = my_container.begin();
+
+The ``auto`` type specifier will only be introduced in situations where the
+variable type matches the type of the initializer expression. In other words
+``auto`` should deduce the same type that was originally spelled in the source.
+However, not every situation should be transformed:
+
+.. code-block:: c++
+
+  int val = 42;
+  InfoStruct &I = SomeObject.getInfo();
+
+  // Should not become:
+
+  auto val = 42;
+  auto &I = SomeObject.getInfo();
+
+In this example using ``auto`` for builtins doesn't improve readability. In
+other situations it makes the code less self-documenting impairing readability
+and maintainability. As a result, ``auto`` is used only introduced in specific
+situations described below.
+
+Iterators
+---------
+
+Iterator type specifiers tend to be long and used frequently, especially in
+loop constructs. Since the functions generating iterators have a common format,
+the type specifier can be replaced without obscuring the meaning of code while
+improving readability and maintainability.
+
+.. code-block:: c++
+
+  for (std::vector<int>::iterator I = my_container.begin(),
+                                  E = my_container.end();
+       I != E; ++I) {
+  }
+
+  // becomes
+
+  for (auto I = my_container.begin(), E = my_container.end(); I != E; ++I) {
+  }
+
+The check will only replace iterator type-specifiers when all of the following
+conditions are satisfied:
+
+* The iterator is for one of the standard container in ``std`` namespace:
+
+  * ``array``
+  * ``deque``
+  * ``forward_list``
+  * ``list``
+  * ``vector``
+  * ``map``
+  * ``multimap``
+  * ``set``
+  * ``multiset``
+  * ``unordered_map``
+  * ``unordered_multimap``
+  * ``unordered_set``
+  * ``unordered_multiset``
+  * ``queue``
+  * ``priority_queue``
+  * ``stack``
+
+* The iterator is one of the possible iterator types for standard containers:
+
+  * ``iterator``
+  * ``reverse_iterator``
+  * ``const_iterator``
+  * ``const_reverse_iterator``
+
+* In addition to using iterator types directly, typedefs or other ways of
+  referring to those types are also allowed. However, implementation-specific
+  types for which a type like ``std::vector<int>::iterator`` is itself a
+  typedef will not be transformed. Consider the following examples:
+
+.. code-block:: c++
+
+  // The following direct uses of iterator types will be transformed.
+  std::vector<int>::iterator I = MyVec.begin();
+  {
+    using namespace std;
+    list<int>::iterator I = MyList.begin();
+  }
+
+  // The type specifier for J would transform to auto since it's a typedef
+  // to a standard iterator type.
+  typedef std::map<int, std::string>::const_iterator map_iterator;
+  map_iterator J = MyMap.begin();
+
+  // The following implementation-specific iterator type for which
+  // std::vector<int>::iterator could be a typedef would not be transformed.
+  __gnu_cxx::__normal_iterator<int*, std::vector> K = MyVec.begin();
+
+* The initializer for the variable being declared is not a braced initializer
+  list. Otherwise, use of ``auto`` would cause the type of the variable to be
+  deduced as ``std::initializer_list``.
+
+New expressions
+---------------
+
+Frequently, when a pointer is declared and initialized with ``new``, the
+pointee type is written twice: in the declaration type and in the
+``new`` expression. In this cases, the declaration type can be replaced with
+``auto`` improving readability and maintainability.
+
+.. code-block:: c++
+
+  TypeName *my_pointer = new TypeName(my_param);
+
+  // becomes
+
+  auto *my_pointer = new TypeName(my_param);
+
+The check will also replace the declaration type in multiple declarations, if
+the following conditions are satisfied:
+
+* All declared variables have the same type (i.e. all of them are pointers to
+  the same type).
+* All declared variables are initialized with a ``new`` expression.
+* The types of all the new expressions are the same than the pointee of the
+  declaration type.
+
+.. code-block:: c++
+
+  TypeName *my_first_pointer = new TypeName, *my_second_pointer = new TypeName;
+
+  // becomes
+
+  auto *my_first_pointer = new TypeName, *my_second_pointer = new TypeName;
+
+Cast expressions
+----------------
+
+Frequently, when a variable is declared and initialized with a cast, the
+variable type is written twice: in the declaration type and in the
+cast expression. In this cases, the declaration type can be replaced with
+``auto`` improving readability and maintainability.
+
+.. code-block:: c++
+
+  TypeName *my_pointer = static_cast<TypeName>(my_param);
+
+  // becomes
+
+  auto *my_pointer = static_cast<TypeName>(my_param);
+
+The check handles ``static_cast``, ``dynamic_cast``, ``const_cast``,
+``reinterpret_cast``, functional casts, C-style casts and function templates
+that behave as casts, such as ``llvm::dyn_cast``, ``boost::lexical_cast`` and
+``gsl::narrow_cast``.  Calls to function templates are considered to behave as
+casts if the first template argument is explicit and is a type, and the function
+returns that type, or a pointer or reference to it.
+
+Known Limitations
+-----------------
+
+* If the initializer is an explicit conversion constructor, the check will not
+  replace the type specifier even though it would be safe to do so.
+
+* User-defined iterators are not handled at this time.
+
+Options
+-------
+
+.. option:: RemoveStars
+
+   If the option is set to non-zero (default is `0`), the check will remove
+   stars from the non-typedef pointer types when replacing type names with
+   ``auto``. Otherwise, the check will leave stars. For example:
+
+.. code-block:: c++
+
+  TypeName *my_first_pointer = new TypeName, *my_second_pointer = new TypeName;
+
+  // RemoveStars = 0
+
+  auto *my_first_pointer = new TypeName, *my_second_pointer = new TypeName;
+
+  // RemoveStars = 1
+
+  auto my_first_pointer = new TypeName, my_second_pointer = new TypeName;
diff --git a/docs/clang-tidy/checks/modernize-use-bool-literals.rst b/docs/clang-tidy/checks/modernize-use-bool-literals.rst
new file mode 100644 (file)
index 0000000..7ce048f
--- /dev/null
@@ -0,0 +1,20 @@
+.. title:: clang-tidy - modernize-use-bool-literals
+
+modernize-use-bool-literals
+===========================
+
+Finds integer literals which are cast to ``bool``.
+
+.. code-block:: c++
+
+  bool p = 1;
+  bool f = static_cast<bool>(1);
+  std::ios_base::sync_with_stdio(0);
+  bool x = p ? 1 : 0;
+
+  // transforms to
+
+  bool p = true;
+  bool f = true;
+  std::ios_base::sync_with_stdio(false);
+  bool x = p ? true : false;
diff --git a/docs/clang-tidy/checks/modernize-use-default-member-init.rst b/docs/clang-tidy/checks/modernize-use-default-member-init.rst
new file mode 100644 (file)
index 0000000..6a199ec
--- /dev/null
@@ -0,0 +1,54 @@
+.. title:: clang-tidy - modernize-use-default-member-init
+
+modernize-use-default-member-init
+=================================
+
+This check converts a default constructor's member initializers into the new
+default member initializers in C++11. Other member initializers that match the
+default member initializer are removed. This can reduce repeated code or allow
+use of '= default'.
+
+.. code-block:: c++
+
+  struct A {
+    A() : i(5), j(10.0) {}
+    A(int i) : i(i), j(10.0) {}
+    int i;
+    double j;
+  };
+
+  // becomes
+
+  struct A {
+    A() {}
+    A(int i) : i(i) {}
+    int i{5};
+    double j{10.0};
+  };
+
+.. note::
+  Only converts member initializers for built-in types, enums, and pointers.
+  The `readability-redundant-member-init` check will remove redundant member
+  initializers for classes.
+
+Options
+-------
+
+.. option:: UseAssignment
+
+   If this option is set to non-zero (default is `0`), the check will initialise
+   members with an assignment. For example:
+
+.. code-block:: c++
+
+  struct A {
+    A() {}
+    A(int i) : i(i) {}
+    int i = 5;
+    double j = 10.0;
+  };
+
+.. option:: IgnoreMacros
+
+   If this option is set to non-zero (default is `1`), the check will not warn
+   about members declared inside macros.
diff --git a/docs/clang-tidy/checks/modernize-use-default.rst b/docs/clang-tidy/checks/modernize-use-default.rst
new file mode 100644 (file)
index 0000000..ce58706
--- /dev/null
@@ -0,0 +1,11 @@
+:orphan:
+
+.. title:: clang-tidy - modernize-use-default
+.. meta::
+   :http-equiv=refresh: 5;URL=modernize-use-equals-default.html
+
+modernize-use-default
+=====================
+
+This check has been renamed to
+`modernize-use-equals-default <modernize-use-equals-default.html>`_.
diff --git a/docs/clang-tidy/checks/modernize-use-emplace.rst b/docs/clang-tidy/checks/modernize-use-emplace.rst
new file mode 100644 (file)
index 0000000..dee79ab
--- /dev/null
@@ -0,0 +1,135 @@
+.. title:: clang-tidy - modernize-use-emplace
+
+modernize-use-emplace
+=====================
+
+The check flags insertions to an STL-style container done by calling the
+``push_back`` method with an explicitly-constructed temporary of the container
+element type. In this case, the corresponding ``emplace_back`` method
+results in less verbose and potentially more efficient code.
+Right now the check doesn't support ``push_front`` and ``insert``.
+It also doesn't support ``insert`` functions for associative containers
+because replacing ``insert`` with ``emplace`` may result in
+`speed regression <http://htmlpreview.github.io/?https://github.com/HowardHinnant/papers/blob/master/insert_vs_emplace.html>`_, but it might get support with some addition flag in the future.
+
+By default only ``std::vector``, ``std::deque``, ``std::list`` are considered.
+This list can be modified using the :option:`ContainersWithPushBack` option.
+
+Before:
+
+.. code-block:: c++
+
+    std::vector<MyClass> v;
+    v.push_back(MyClass(21, 37));
+
+    std::vector<std::pair<int, int>> w;
+
+    w.push_back(std::pair<int, int>(21, 37));
+    w.push_back(std::make_pair(21L, 37L));
+
+After:
+
+.. code-block:: c++
+
+    std::vector<MyClass> v;
+    v.emplace_back(21, 37);
+
+    std::vector<std::pair<int, int>> w;
+    w.emplace_back(21, 37);
+    w.emplace_back(21L, 37L);
+
+By default, the check is able to remove unnecessary ``std::make_pair`` and
+``std::make_tuple`` calls from ``push_back`` calls on containers of
+``std::pair`` and ``std::tuple``. Custom tuple-like types can be modified by
+the :option:`TupleTypes` option; custom make functions can be modified by the
+:option:`TupleMakeFunctions` option.
+
+The other situation is when we pass arguments that will be converted to a type
+inside a container.
+
+Before:
+
+.. code-block:: c++
+
+    std::vector<boost::optional<std::string> > v;
+    v.push_back("abc");
+
+After:
+
+.. code-block:: c++
+
+    std::vector<boost::optional<std::string> > v;
+    v.emplace_back("abc");
+
+
+In some cases the transformation would be valid, but the code wouldn't be
+exception safe. In this case the calls of ``push_back`` won't be replaced.
+
+.. code-block:: c++
+
+    std::vector<std::unique_ptr<int>> v;
+    v.push_back(std::unique_ptr<int>(new int(0)));
+    auto *ptr = new int(1);
+    v.push_back(std::unique_ptr<int>(ptr));
+
+This is because replacing it with ``emplace_back`` could cause a leak of this
+pointer if ``emplace_back`` would throw exception before emplacement (e.g. not
+enough memory to add a new element).
+
+For more info read item 42 - "Consider emplacement instead of insertion." of
+Scott Meyers "Effective Modern C++".
+
+The default smart pointers that are considered are ``std::unique_ptr``,
+``std::shared_ptr``, ``std::auto_ptr``. To specify other smart pointers or
+other classes use the :option:`SmartPointers` option.
+
+
+Check also doesn't fire if any argument of the constructor call would be:
+
+  - a bit-field (bit-fields can't bind to rvalue/universal reference)
+
+  - a ``new`` expression (to avoid leak)
+
+  - if the argument would be converted via derived-to-base cast.
+
+This check requires C++11 or higher to run.
+
+Options
+-------
+
+.. option:: ContainersWithPushBack
+
+   Semicolon-separated list of class names of custom containers that support
+   ``push_back``.
+
+.. option:: SmartPointers
+
+   Semicolon-separated list of class names of custom smart pointers.
+
+.. option:: TupleTypes
+
+    Semicolon-separated list of ``std::tuple``-like class names.
+
+.. option:: TupleMakeFunctions
+
+    Semicolon-separated list of ``std::make_tuple``-like function names. Those
+    function calls will be removed from ``push_back`` calls and turned into
+    ``emplace_back``.
+
+Example
+^^^^^^^
+
+.. code-block:: c++
+
+  std::vector<MyTuple<int, bool, char>> x;
+  x.push_back(MakeMyTuple(1, false, 'x'));
+
+transforms to:
+
+.. code-block:: c++
+
+  std::vector<MyTuple<int, bool, char>> x;
+  x.emplace_back(1, false, 'x');
+
+when :option:`TupleTypes` is set to ``MyTuple`` and :option:`TupleMakeFunctions`
+is set to ``MakeMyTuple``.
diff --git a/docs/clang-tidy/checks/modernize-use-equals-default.rst b/docs/clang-tidy/checks/modernize-use-equals-default.rst
new file mode 100644 (file)
index 0000000..b87f883
--- /dev/null
@@ -0,0 +1,28 @@
+.. title:: clang-tidy - modernize-use-equals-default
+
+modernize-use-equals-default
+============================
+
+This check replaces default bodies of special member functions with ``=
+default;``. The explicitly defaulted function declarations enable more
+opportunities in optimization, because the compiler might treat explicitly
+defaulted functions as trivial.
+
+.. code-block:: c++
+
+  struct A {
+    A() {}
+    ~A();
+  };
+  A::~A() {}
+
+  // becomes
+
+  struct A {
+    A() = default;
+    ~A();
+  };
+  A::~A() = default;
+
+.. note::
+  Move-constructor and move-assignment operator are not supported yet.
diff --git a/docs/clang-tidy/checks/modernize-use-equals-delete.rst b/docs/clang-tidy/checks/modernize-use-equals-delete.rst
new file mode 100644 (file)
index 0000000..bfa54e4
--- /dev/null
@@ -0,0 +1,25 @@
+.. title:: clang-tidy - modernize-use-equals-delete
+
+modernize-use-equals-delete
+===========================
+
+This check marks unimplemented private special member functions with ``= delete``.
+To avoid false-positives, this check only applies in a translation unit that has
+all other member functions implemented.
+
+.. code-block:: c++
+
+  struct A {
+  private:
+    A(const A&);
+    A& operator=(const A&);
+  };
+
+  // becomes
+
+  struct A {
+  private:
+    A(const A&) = delete;
+    A& operator=(const A&) = delete;
+  };
+
diff --git a/docs/clang-tidy/checks/modernize-use-noexcept.rst b/docs/clang-tidy/checks/modernize-use-noexcept.rst
new file mode 100644 (file)
index 0000000..084dad7
--- /dev/null
@@ -0,0 +1,90 @@
+.. title:: clang-tidy - modernize-use-noexcept
+
+modernize-use-noexcept
+======================
+
+This check replaces deprecated dynamic exception specifications with
+the appropriate noexcept specification (introduced in C++11).  By
+default this check will replace ``throw()`` with ``noexcept``,
+and ``throw(<exception>[,...])`` or ``throw(...)`` with
+``noexcept(false)``.
+
+Example
+-------
+
+.. code-block:: c++
+
+  void foo() throw();
+       void bar() throw(int) {}
+
+transforms to:
+
+.. code-block:: c++
+
+  void foo() noexcept;
+       void bar() noexcept(false) {}
+
+Options
+-------
+
+.. option:: ReplacementString
+
+Users can use :option:`ReplacementString` to specify a macro to use
+instead of ``noexcept``.  This is useful when maintaining source code
+that uses custom exception specification marking other than
+``noexcept``.  Fix-it hints will only be generated for non-throwing
+specifications.
+
+Example
+^^^^^^^
+
+.. code-block:: c++
+
+  void bar() throw(int);
+  void foo() throw();
+
+transforms to:
+
+.. code-block:: c++
+
+  void bar() throw(int);  // No fix-it generated.
+  void foo() NOEXCEPT;
+
+if the :option:`ReplacementString` option is set to `NOEXCEPT`.
+
+.. option:: UseNoexceptFalse
+
+Enabled by default, disabling will generate fix-it hints that remove
+throwing dynamic exception specs, e.g., ``throw(<something>)``,
+completely without providing a replacement text, except for
+destructors and delete operators that are ``noexcept(true)`` by
+default.
+
+Example
+^^^^^^^
+
+.. code-block:: c++
+
+  void foo() throw(int) {}
+
+  struct bar {
+    void foobar() throw(int);
+    void operator delete(void *ptr) throw(int);
+    void operator delete[](void *ptr) throw(int);
+    ~bar() throw(int);
+  }
+
+transforms to:
+
+.. code-block:: c++
+
+  void foo() {}
+
+  struct bar {
+    void foobar();
+    void operator delete(void *ptr) noexcept(false);
+    void operator delete[](void *ptr) noexcept(false);
+    ~bar() noexcept(false);
+  }
+
+if the :option:`UseNoexceptFalse` option is set to `0`.
diff --git a/docs/clang-tidy/checks/modernize-use-nullptr.rst b/docs/clang-tidy/checks/modernize-use-nullptr.rst
new file mode 100644 (file)
index 0000000..e65b65b
--- /dev/null
@@ -0,0 +1,67 @@
+.. title:: clang-tidy - modernize-use-nullptr
+
+modernize-use-nullptr
+=====================
+
+The check converts the usage of null pointer constants (eg. ``NULL``, ``0``)
+to use the new C++11 ``nullptr`` keyword.
+
+Example
+-------
+
+.. code-block:: c++
+
+  void assignment() {
+    char *a = NULL;
+    char *b = 0;
+    char c = 0;
+  }
+
+  int *ret_ptr() {
+    return 0;
+  }
+
+
+transforms to:
+
+.. code-block:: c++
+
+  void assignment() {
+    char *a = nullptr;
+    char *b = nullptr;
+    char c = 0;
+  }
+
+  int *ret_ptr() {
+    return nullptr;
+  }
+
+Options
+-------
+
+.. option:: NullMacros
+
+   Comma-separated list of macro names that will be transformed along with
+   ``NULL``. By default this check will only replace the ``NULL`` macro and will
+   skip any similar user-defined macros.
+
+Example
+^^^^^^^
+
+.. code-block:: c++
+
+  #define MY_NULL (void*)0
+  void assignment() {
+    void *p = MY_NULL;
+  }
+
+transforms to:
+
+.. code-block:: c++
+
+  #define MY_NULL NULL
+  void assignment() {
+    int *p = nullptr;
+  }
+
+if the :option:`NullMacros` option is set to ``MY_NULL``.
diff --git a/docs/clang-tidy/checks/modernize-use-override.rst b/docs/clang-tidy/checks/modernize-use-override.rst
new file mode 100644 (file)
index 0000000..f2c778a
--- /dev/null
@@ -0,0 +1,7 @@
+.. title:: clang-tidy - modernize-use-override
+
+modernize-use-override
+======================
+
+
+Use C++11's ``override`` and remove ``virtual`` where applicable.
diff --git a/docs/clang-tidy/checks/modernize-use-transparent-functors.rst b/docs/clang-tidy/checks/modernize-use-transparent-functors.rst
new file mode 100644 (file)
index 0000000..8887275
--- /dev/null
@@ -0,0 +1,39 @@
+.. title:: clang-tidy - modernize-use-transparent-functors
+
+modernize-use-transparent-functors
+==================================
+
+Prefer transparent functors to non-transparent ones. When using transparent
+functors, the type does not need to be repeated. The code is easier to read,
+maintain and less prone to errors. It is not possible to introduce unwanted
+conversions.
+
+  .. code-block:: c++
+
+    // Non-transparent functor
+    std::map<int, std::string, std::greater<int>> s;
+
+    // Transparent functor.
+    std::map<int, std::string, std::greater<>> s;
+
+    // Non-transparent functor
+    using MyFunctor = std::less<MyType>;
+
+It is not always a safe transformation though. The following case will be
+untouched to preserve the semantics.
+
+  .. code-block:: c++
+
+    // Non-transparent functor
+    std::map<const char *, std::string, std::greater<std::string>> s;
+
+Options
+-------
+
+.. option:: SafeMode
+
+  If the option is set to non-zero, the check will not diagnose cases where
+  using a transparent functor cannot be guaranteed to produce identical results
+  as the original code. The default value for this option is `0`.
+
+This check requires using C++14 or higher to run.
diff --git a/docs/clang-tidy/checks/modernize-use-using.rst b/docs/clang-tidy/checks/modernize-use-using.rst
new file mode 100644 (file)
index 0000000..14172bb
--- /dev/null
@@ -0,0 +1,26 @@
+.. title:: clang-tidy - modernize-use-using
+
+modernize-use-using
+===================
+
+The check converts the usage of ``typedef`` with ``using`` keyword.
+
+Before:
+
+.. code-block:: c++
+
+  typedef int variable;
+
+  class Class{};
+  typedef void (Class::* MyPtrType)() const;
+
+After:
+
+.. code-block:: c++
+
+  using variable = int;
+
+  class Class{};
+  using MyPtrType = void (Class::*)() const;
+
+This check requires using C++11 or higher to run.
diff --git a/docs/clang-tidy/checks/mpi-buffer-deref.rst b/docs/clang-tidy/checks/mpi-buffer-deref.rst
new file mode 100644 (file)
index 0000000..ef9f391
--- /dev/null
@@ -0,0 +1,26 @@
+.. title:: clang-tidy - mpi-buffer-deref
+
+mpi-buffer-deref
+================
+
+This check verifies if a buffer passed to an MPI (Message Passing Interface)
+function is sufficiently dereferenced. Buffers should be passed as a single
+pointer or array. As MPI function signatures specify ``void *`` for their buffer
+types, insufficiently dereferenced buffers can be passed, like for example as
+double pointers or multidimensional arrays, without a compiler warning emitted.
+
+Examples:
+
+.. code-block:: c++
+
+   // A double pointer is passed to the MPI function.
+   char *buf;
+   MPI_Send(&buf, 1, MPI_CHAR, 0, 0, MPI_COMM_WORLD);
+
+   // A multidimensional array is passed to the MPI function.
+   short buf[1][1];
+   MPI_Send(buf, 1, MPI_SHORT, 0, 0, MPI_COMM_WORLD);
+
+   // A pointer to an array is passed to the MPI function.
+   short *buf[1];
+   MPI_Send(buf, 1, MPI_SHORT, 0, 0, MPI_COMM_WORLD);
diff --git a/docs/clang-tidy/checks/mpi-type-mismatch.rst b/docs/clang-tidy/checks/mpi-type-mismatch.rst
new file mode 100644 (file)
index 0000000..10752ef
--- /dev/null
@@ -0,0 +1,21 @@
+.. title:: clang-tidy - mpi-type-mismatch
+
+mpi-type-mismatch
+=================
+
+This check verifies if buffer type and MPI (Message Passing Interface) datatype
+pairs match for used MPI functions. All MPI datatypes defined by the MPI
+standard (3.1) are verified by this check. User defined typedefs, custom MPI
+datatypes and null pointer constants are skipped, in the course of verification.
+
+Example:
+
+.. code-block:: c++
+
+  // In this case, the buffer type matches MPI datatype.
+  char buf;
+  MPI_Send(&buf, 1, MPI_CHAR, 0, 0, MPI_COMM_WORLD);
+
+  // In the following case, the buffer type does not match MPI datatype.
+  int buf;
+  MPI_Send(&buf, 1, MPI_CHAR, 0, 0, MPI_COMM_WORLD);
diff --git a/docs/clang-tidy/checks/performance-faster-string-find.rst b/docs/clang-tidy/checks/performance-faster-string-find.rst
new file mode 100644 (file)
index 0000000..8d7265e
--- /dev/null
@@ -0,0 +1,28 @@
+.. title:: clang-tidy - performance-faster-string-find
+
+performance-faster-string-find
+==============================
+
+Optimize calls to ``std::string::find()`` and friends when the needle passed is
+a single character string literal. The character literal overload is more
+efficient.
+
+Examples:
+
+.. code-block:: c++
+
+  str.find("A");
+
+  // becomes
+
+  str.find('A');
+
+Options
+-------
+
+.. option:: StringLikeClasses
+
+   Semicolon-separated list of names of string-like classes. By default only
+   ``std::basic_string`` is considered. The list of methods to consired is
+   fixed.
+
diff --git a/docs/clang-tidy/checks/performance-for-range-copy.rst b/docs/clang-tidy/checks/performance-for-range-copy.rst
new file mode 100644 (file)
index 0000000..2e7db98
--- /dev/null
@@ -0,0 +1,27 @@
+.. title:: clang-tidy - performance-for-range-copy
+
+performance-for-range-copy
+==========================
+
+Finds C++11 for ranges where the loop variable is copied in each iteration but
+it would suffice to obtain it by const reference.
+
+The check is only applied to loop variables of types that are expensive to copy
+which means they are not trivially copyable or have a non-trivial copy
+constructor or destructor.
+
+To ensure that it is safe to replace the copy with a const reference the
+following heuristic is employed:
+
+1. The loop variable is const qualified.
+2. The loop variable is not const, but only const methods or operators are
+   invoked on it, or it is used as const reference or value argument in
+   constructors or function calls.
+
+Options
+-------
+
+.. option:: WarnOnAllAutoCopies
+
+   When non-zero, warns on any use of `auto` as the type of the range-based for
+   loop variable. Default is `0`.
diff --git a/docs/clang-tidy/checks/performance-implicit-cast-in-loop.rst b/docs/clang-tidy/checks/performance-implicit-cast-in-loop.rst
new file mode 100644 (file)
index 0000000..7a5cdf4
--- /dev/null
@@ -0,0 +1,21 @@
+.. title:: clang-tidy - performance-implicit-cast-in-loop
+
+performance-implicit-cast-in-loop
+=================================
+
+This warning appears in a range-based loop with a loop variable of const ref
+type where the type of the variable does not match the one returned by the
+iterator. This means that an implicit cast has been added, which can for example
+result in expensive deep copies.
+
+Example:
+
+.. code-block:: c++
+
+  map<int, vector<string>> my_map;
+  for (const pair<int, vector<string>>& p : my_map) {}
+  // The iterator type is in fact pair<const int, vector<string>>, which means
+  // that the compiler added a cast, resulting in a copy of the vectors.
+
+The easiest solution is usually to use ``const auto&`` instead of writing the type
+manually.
diff --git a/docs/clang-tidy/checks/performance-inefficient-string-concatenation.rst b/docs/clang-tidy/checks/performance-inefficient-string-concatenation.rst
new file mode 100644 (file)
index 0000000..60910c6
--- /dev/null
@@ -0,0 +1,59 @@
+.. title:: clang-tidy - performance-inefficient-string-concatenation
+
+performance-inefficient-string-concatenation
+============================================
+
+This check warns about the performance overhead arising from concatenating
+strings using the ``operator+``, for instance:
+
+.. code-block:: c++
+
+    std::string a("Foo"), b("Bar");
+    a = a + b;
+
+Instead of this structure you should use ``operator+=`` or ``std::string``'s
+(``std::basic_string``) class member function ``append()``. For instance:
+
+.. code-block:: c++
+
+   std::string a("Foo"), b("Baz");
+   for (int i = 0; i < 20000; ++i) {
+       a = a + "Bar" + b;
+   }
+
+Could be rewritten in a greatly more efficient way like:
+
+.. code-block:: c++
+
+   std::string a("Foo"), b("Baz");
+   for (int i = 0; i < 20000; ++i) {
+       a.append("Bar").append(b);
+   }
+
+And this can be rewritten too:
+
+.. code-block:: c++
+
+   void f(const std::string&) {}
+   std::string a("Foo"), b("Baz");
+   void g() {
+       f(a + "Bar" + b);
+   }
+
+In a slightly more efficient way like:
+
+.. code-block:: c++
+
+   void f(const std::string&) {}
+   std::string a("Foo"), b("Baz");
+   void g() {
+       f(std::string(a).append("Bar").append(b));
+   }
+
+Options
+-------
+
+.. option:: StrictMode
+
+   When zero, the check will only check the string usage in ``while``, ``for``
+   and ``for-range`` statements. Default is `0`.
diff --git a/docs/clang-tidy/checks/performance-inefficient-vector-operation.rst b/docs/clang-tidy/checks/performance-inefficient-vector-operation.rst
new file mode 100644 (file)
index 0000000..8cf9318
--- /dev/null
@@ -0,0 +1,49 @@
+.. title:: clang-tidy - performance-inefficient-vector-operation
+
+performance-inefficient-vector-operation
+========================================
+
+Finds possible inefficient ``std::vector`` operations (e.g. ``push_back``,
+``emplace_back``) that may cause unnecessary memory reallocations.
+
+Currently, the check only detects following kinds of loops with a single
+statement body:
+
+* Counter-based for loops start with 0:
+
+.. code-block:: c++
+
+  std::vector<int> v;
+  for (int i = 0; i < n; ++i) {
+    v.push_back(n);
+    // This will trigger the warning since the push_back may cause multiple
+    // memory reallocations in v. This can be avoid by inserting a 'reserve(n)'
+    // statement before the for statement.
+  }
+
+
+* For-range loops like ``for (range-declaration : range_expression)``, the type
+  of ``range_expression`` can be ``std::vector``, ``std::array``,
+  ``std::deque``, ``std::set``, ``std::unordered_set``, ``std::map``,
+  ``std::unordered_set``:
+
+.. code-block:: c++
+
+  std::vector<int> data;
+  std::vector<int> v;
+
+  for (auto element : data) {
+    v.push_back(element);
+    // This will trigger the warning since the 'push_back' may cause multiple
+    // memory reallocations in v. This can be avoid by inserting a
+    // 'reserve(data.size())' statement before the for statement.
+  }
+
+
+Options
+-------
+
+.. option:: VectorLikeClasses
+
+   Semicolon-separated list of names of vector-like classes. By default only
+   ``::std::vector`` is considered.
diff --git a/docs/clang-tidy/checks/performance-type-promotion-in-math-fn.rst b/docs/clang-tidy/checks/performance-type-promotion-in-math-fn.rst
new file mode 100644 (file)
index 0000000..a27a482
--- /dev/null
@@ -0,0 +1,21 @@
+.. title:: clang-tidy - performance-type-promotion-in-math-fn
+
+performance-type-promotion-in-math-fn
+=====================================
+
+Finds calls to C math library functions (from ``math.h`` or, in C++, ``cmath``)
+with implicit ``float`` to ``double`` promotions.
+
+For example, warns on ``::sin(0.f)``, because this funciton's parameter is a
+double. You probably meant to call ``std::sin(0.f)`` (in C++), or ``sinf(0.f)``
+(in C).
+
+.. code-block:: c++
+
+  float a;
+  asin(a);
+
+  // becomes
+
+  float a;
+  std::asin(a);
diff --git a/docs/clang-tidy/checks/performance-unnecessary-copy-initialization.rst b/docs/clang-tidy/checks/performance-unnecessary-copy-initialization.rst
new file mode 100644 (file)
index 0000000..2bd7755
--- /dev/null
@@ -0,0 +1,37 @@
+.. title:: clang-tidy - performance-unnecessary-copy-initialization
+
+performance-unnecessary-copy-initialization
+===========================================
+
+Finds local variable declarations that are initialized using the copy
+constructor of a non-trivially-copyable type but it would suffice to obtain a
+const reference.
+
+The check is only applied if it is safe to replace the copy by a const
+reference. This is the case when the variable is const qualified or when it is
+only used as a const, i.e. only const methods or operators are invoked on it, or
+it is used as const reference or value argument in constructors or function
+calls.
+
+Example:
+
+.. code-block:: c++
+
+  const string& constReference();
+  void Function() {
+    // The warning will suggest making this a const reference.
+    const string UnnecessaryCopy = constReference();
+  }
+
+  struct Foo {
+    const string& name() const;
+  };
+  void Function(const Foo& foo) {
+    // The warning will suggest making this a const reference.
+    string UnnecessaryCopy1 = foo.name();
+    UnnecessaryCopy1.find("bar");
+
+    // The warning will suggest making this a const reference.
+    string UnnecessaryCopy2 = UnnecessaryCopy1;
+    UnnecessaryCopy2.find("bar");
+  }
diff --git a/docs/clang-tidy/checks/performance-unnecessary-value-param.rst b/docs/clang-tidy/checks/performance-unnecessary-value-param.rst
new file mode 100644 (file)
index 0000000..e69610e
--- /dev/null
@@ -0,0 +1,63 @@
+.. title:: clang-tidy - performance-unnecessary-value-param
+
+performance-unnecessary-value-param
+===================================
+
+Flags value parameter declarations of expensive to copy types that are copied
+for each invocation but it would suffice to pass them by const reference.
+
+The check is only applied to parameters of types that are expensive to copy
+which means they are not trivially copyable or have a non-trivial copy
+constructor or destructor.
+
+To ensure that it is safe to replace the value parameter with a const reference
+the following heuristic is employed:
+
+1. the parameter is const qualified;
+2. the parameter is not const, but only const methods or operators are invoked
+   on it, or it is used as const reference or value argument in constructors or
+   function calls.
+
+Example:
+
+.. code-block:: c++
+
+  void f(const string Value) {
+    // The warning will suggest making Value a reference.
+  }
+
+  void g(ExpensiveToCopy Value) {
+    // The warning will suggest making Value a const reference.
+    Value.ConstMethd();
+    ExpensiveToCopy Copy(Value);
+  }
+
+If the parameter is not const, only copied or assigned once and has a
+non-trivial move-constructor or move-assignment operator respectively the check
+will suggest to move it.
+
+Example:
+
+.. code-block:: c++
+
+  void setValue(string Value) {
+    Field = Value;
+  }
+
+Will become:
+
+.. code-block:: c++
+
+  #include <utility>
+
+  void setValue(string Value) {
+    Field = std::move(Value);
+  }
+
+Options
+-------
+
+.. option:: IncludeStyle
+
+   A string specifying which include-style is used, `llvm` or `google`. Default
+   is `llvm`.
diff --git a/docs/clang-tidy/checks/readability-avoid-const-params-in-decls.rst b/docs/clang-tidy/checks/readability-avoid-const-params-in-decls.rst
new file mode 100644 (file)
index 0000000..3aea5d0
--- /dev/null
@@ -0,0 +1,17 @@
+.. title:: clang-tidy - readability-avoid-const-params-in-decls
+
+readability-avoid-const-params-in-decls
+=======================================
+
+Checks whether a function declaration has parameters that are top level
+``const``.
+
+``const`` values in declarations do not affect the signature of a function, so
+they should not be put there.
+
+Examples:
+
+.. code-block:: c++
+
+  void f(const string);   // Bad: const is top level.
+  void f(const string&);  // Good: const is not top level.
diff --git a/docs/clang-tidy/checks/readability-braces-around-statements.rst b/docs/clang-tidy/checks/readability-braces-around-statements.rst
new file mode 100644 (file)
index 0000000..2c08165
--- /dev/null
@@ -0,0 +1,38 @@
+.. title:: clang-tidy - readability-braces-around-statements
+
+readability-braces-around-statements
+====================================
+
+`google-readability-braces-around-statements` redirects here as an alias for
+this check.
+
+Checks that bodies of ``if`` statements and loops (``for``, ``do while``, and
+``while``) are inside braces.
+
+Before:
+
+.. code-block:: c++
+
+  if (condition)
+    statement;
+
+After:
+
+.. code-block:: c++
+
+  if (condition) {
+    statement;
+  }
+
+Options
+-------
+
+.. option:: ShortStatementLines
+
+   Defines the minimal number of lines that the statement should have in order
+   to trigger this check.
+
+   The number of lines is counted from the end of condition or initial keyword
+   (``do``/``else``) until the last line of the inner statement. Default value
+   `0` means that braces will be added to all statements (not having them
+   already).
diff --git a/docs/clang-tidy/checks/readability-container-size-empty.rst b/docs/clang-tidy/checks/readability-container-size-empty.rst
new file mode 100644 (file)
index 0000000..541320c
--- /dev/null
@@ -0,0 +1,26 @@
+.. title:: clang-tidy - readability-container-size-empty
+
+readability-container-size-empty
+================================
+
+
+Checks whether a call to the ``size()`` method can be replaced with a call to
+``empty()``.
+
+The emptiness of a container should be checked using the ``empty()`` method
+instead of the ``size()`` method. It is not guaranteed that ``size()`` is a
+constant-time function, and it is generally more efficient and also shows
+clearer intent to use ``empty()``. Furthermore some containers may implement
+the ``empty()`` method but not implement the ``size()`` method. Using
+``empty()`` whenever possible makes it easier to switch to another container in
+the future.
+
+The check issues warning if a container has ``size()`` and ``empty()`` methods
+matching following signatures:
+
+.. code-block:: c++
+
+  size_type size() const;
+  bool empty() const;
+
+`size_type` can be any kind of integer type.
diff --git a/docs/clang-tidy/checks/readability-delete-null-pointer.rst b/docs/clang-tidy/checks/readability-delete-null-pointer.rst
new file mode 100644 (file)
index 0000000..21c91b4
--- /dev/null
@@ -0,0 +1,13 @@
+.. title:: clang-tidy - readability-delete-null-pointer
+
+readability-delete-null-pointer
+===============================
+
+Checks the ``if`` statements where a pointer's existence is checked and then deletes the pointer.
+The check is unnecessary as deleting a null pointer has no effect.
+
+.. code:: c++
+
+  int *p;
+  if (p)
+    delete p;
diff --git a/docs/clang-tidy/checks/readability-deleted-default.rst b/docs/clang-tidy/checks/readability-deleted-default.rst
new file mode 100644 (file)
index 0000000..00134eb
--- /dev/null
@@ -0,0 +1,22 @@
+.. title:: clang-tidy - readability-deleted-default
+
+readability-deleted-default
+===========================
+
+Checks that constructors and assignment operators marked as ``= default`` are
+not actually deleted by the compiler.
+
+.. code-block:: c++
+
+  class Example {
+  public:
+    // This constructor is deleted because I is missing a default value.
+    Example() = default;
+    // This is fine.
+    Example(const Example& Other) = default;
+    // This operator is deleted because I cannot be assigned (it is const).
+    Example& operator=(const Example& Other) = default;
+
+  private:
+    const int I;
+  };
diff --git a/docs/clang-tidy/checks/readability-else-after-return.rst b/docs/clang-tidy/checks/readability-else-after-return.rst
new file mode 100644 (file)
index 0000000..949b5bb
--- /dev/null
@@ -0,0 +1,64 @@
+.. title:: clang-tidy - readability-else-after-return
+
+readability-else-after-return
+=============================
+
+`LLVM Coding Standards <http://llvm.org/docs/CodingStandards.html>`_ advises to
+reduce indentation where possible and where it makes understanding code easier.
+Early exit is one of the suggested enforcements of that. Please do not use
+``else`` or ``else if`` after something that interrupts control flow - like
+``return``, ``break``, ``continue``, ``throw``.
+
+The following piece of code illustrates how the check works. This piece of code:
+
+.. code-block:: c++
+
+    void foo(int Value) {
+      int Local = 0;
+      for (int i = 0; i < 42; i++) {
+        if (Value == 1) {
+          return;
+        } else {
+          Local++;
+        }
+
+        if (Value == 2)
+          continue;
+        else
+          Local++;
+
+        if (Value == 3) {
+          throw 42;
+        } else {
+          Local++;
+        }
+      }
+    }
+
+
+Would be transformed into:
+
+.. code-block:: c++
+
+    void foo(int Value) {
+      int Local = 0;
+      for (int i = 0; i < 42; i++) {
+        if (Value == 1) {
+          return;
+        }
+        Local++;
+
+        if (Value == 2)
+          continue;
+        Local++;
+
+        if (Value == 3) {
+          throw 42;
+        }
+        Local++;
+      }
+    }
+
+
+This check helps to enforce this `LLVM Coding Standards recommendation
+<http://llvm.org/docs/CodingStandards.html#don-t-use-else-after-a-return>`_.
diff --git a/docs/clang-tidy/checks/readability-function-size.rst b/docs/clang-tidy/checks/readability-function-size.rst
new file mode 100644 (file)
index 0000000..f903dd7
--- /dev/null
@@ -0,0 +1,38 @@
+.. title:: clang-tidy - readability-function-size
+
+readability-function-size
+=========================
+
+`google-readability-function-size` redirects here as an alias for this check.
+
+Checks for large functions based on various metrics.
+
+Options
+-------
+
+.. option:: LineThreshold
+
+   Flag functions exceeding this number of lines. The default is `-1` (ignore
+   the number of lines).
+
+.. option:: StatementThreshold
+
+   Flag functions exceeding this number of statements. This may differ
+   significantly from the number of lines for macro-heavy code. The default is
+   `800`.
+
+.. option:: BranchThreshold
+
+   Flag functions exceeding this number of control statements. The default is
+   `-1` (ignore the number of branches).
+
+.. option:: ParameterThreshold
+
+   Flag functions that exceed a specified number of parameters. The default
+   is `-1` (ignore the number of parameters).
+
+.. option:: NestingThreshold
+
+    Flag compound statements which create next nesting level after
+    `NestingThreshold`. This may differ significantly from the expected value
+    for macro-heavy code. The default is `-1` (ignore the nesting level).
diff --git a/docs/clang-tidy/checks/readability-identifier-naming.rst b/docs/clang-tidy/checks/readability-identifier-naming.rst
new file mode 100644 (file)
index 0000000..30408c1
--- /dev/null
@@ -0,0 +1,18 @@
+.. title:: clang-tidy - readability-identifier-naming
+
+readability-identifier-naming
+=============================
+
+Checks for identifiers naming style mismatch.
+
+This check will try to enforce coding guidelines on the identifiers naming.
+It supports `lower_case`, `UPPER_CASE`, `camelBack` and `CamelCase` casing and
+tries to convert from one to another if a mismatch is detected.
+
+It also supports a fixed prefix and suffix that will be prepended or
+appended to the identifiers, regardless of the casing.
+
+Many configuration options are available, in order to be able to create
+different rules for different kind of identifier. In general, the
+rules are falling back to a more generic rule if the specific case is not
+configured.
diff --git a/docs/clang-tidy/checks/readability-implicit-bool-cast.rst b/docs/clang-tidy/checks/readability-implicit-bool-cast.rst
new file mode 100644 (file)
index 0000000..bcd23ab
--- /dev/null
@@ -0,0 +1,130 @@
+.. title:: clang-tidy - readability-implicit-bool-cast
+
+readability-implicit-bool-cast
+==============================
+
+This check can be used to find implicit conversions between built-in types and
+booleans. Depending on use case, it may simply help with readability of the code,
+or in some cases, point to potential bugs which remain unnoticed due to implicit
+conversions.
+
+The following is a real-world example of bug which was hiding behind implicit
+``bool`` cast:
+
+.. code-block:: c++
+
+  class Foo {
+    int m_foo;
+
+  public:
+    void setFoo(bool foo) { m_foo = foo; } // warning: implicit cast bool -> int
+    int getFoo() { return m_foo; }
+  };
+
+  void use(Foo& foo) {
+    bool value = foo.getFoo(); // warning: implicit cast int -> bool
+  }
+
+This code is the result of unsuccessful refactoring, where type of ``m_foo``
+changed from ``bool`` to ``int``. The programmer forgot to change all
+occurrences of ``bool``, and the remaining code is no longer correct, yet it
+still compiles without any visible warnings.
+
+In addition to issuing warnings, fix-it hints are provided to help solve the
+reported issues. This can be used for improving readability of code, for
+example:
+
+.. code-block:: c++
+
+  void conversionsToBool() {
+    float floating;
+    bool boolean = floating;
+    // ^ propose replacement: bool boolean = floating != 0.0f;
+
+    int integer;
+    if (integer) {}
+    // ^ propose replacement: if (integer != 0) {}
+
+    int* pointer;
+    if (!pointer) {}
+    // ^ propose replacement: if (pointer == nullptr) {}
+
+    while (1) {}
+    // ^ propose replacement: while (true) {}
+  }
+
+  void functionTakingInt(int param);
+
+  void conversionsFromBool() {
+    bool boolean;
+    functionTakingInt(boolean);
+    // ^ propose replacement: functionTakingInt(static_cast<int>(boolean));
+
+    functionTakingInt(true);
+    // ^ propose replacement: functionTakingInt(1);
+  }
+
+In general, the following cast types are checked:
+
+- integer expression/literal to boolean,
+
+- floating expression/literal to boolean,
+
+- pointer/pointer to member/``nullptr``/``NULL`` to boolean,
+
+- boolean expression/literal to integer,
+
+- boolean expression/literal to floating.
+
+The rules for generating fix-it hints are:
+
+- in case of casts from other built-in type to bool, an explicit comparison
+  is proposed to make it clear what exaclty is being compared:
+
+  - ``bool boolean = floating;`` is changed to
+    ``bool boolean = floating == 0.0f;``,
+
+  - for other types, appropriate literals are used (``0``, ``0u``, ``0.0f``,
+    ``0.0``, ``nullptr``),
+
+- in case of negated expressions cast to bool, the proposed replacement with
+  comparison is simplified:
+
+  - ``if (!pointer)`` is changed to ``if (pointer == nullptr)``,
+
+- in case of casts from bool to other built-in types, an explicit ``static_cast``
+  is proposed to make it clear that a cast is taking place:
+
+  - ``int integer = boolean;`` is changed to
+    ``int integer = static_cast<int>(boolean);``,
+
+- if the cast is performed on type literals, an equivalent literal is proposed,
+  according to what type is actually expected, for example:
+
+  - ``functionTakingBool(0);`` is changed to ``functionTakingBool(false);``,
+
+  - ``functionTakingInt(true);`` is changed to ``functionTakingInt(1);``,
+
+  - for other types, appropriate literals are used (``false``, ``true``, ``0``,
+    ``1``, ``0u``, ``1u``, ``0.0f``, ``1.0f``, ``0.0``, ``1.0f``).
+
+Some additional accommodations are made for pre-C++11 dialects:
+
+- ``false`` literal cast to pointer is detected,
+
+- instead of ``nullptr`` literal, ``0`` is proposed as replacement.
+
+Occurrences of implicit casts inside macros and template instantiations are
+deliberately ignored, as it is not clear how to deal with such cases.
+
+Options
+-------
+
+.. option::  AllowConditionalIntegerCasts
+
+   When non-zero, the check will allow conditional integer casts. Default is
+   `0`.
+
+.. option::  AllowConditionalPointerCasts
+
+   When non-zero, the check will allow conditional pointer casts. Default is `0`.
diff --git a/docs/clang-tidy/checks/readability-inconsistent-declaration-parameter-name.rst b/docs/clang-tidy/checks/readability-inconsistent-declaration-parameter-name.rst
new file mode 100644 (file)
index 0000000..599f281
--- /dev/null
@@ -0,0 +1,44 @@
+.. title:: clang-tidy - readability-inconsistent-declaration-parameter-name
+
+readability-inconsistent-declaration-parameter-name
+===================================================
+
+Find function declarations which differ in parameter names.
+
+Example:
+
+.. code-block:: c++
+
+  // in foo.hpp:
+  void foo(int a, int b, int c);
+
+  // in foo.cpp:
+  void foo(int d, int e, int f); // warning
+
+This check should help to enforce consistency in large projects, where it often
+happens that a definition of function is refactored, changing the parameter
+names, but its declaration in header file is not updated. With this check, we
+can easily find and correct such inconsistencies, keeping declaration and
+definition always in sync.
+
+Unnamed parameters are allowed and are not taken into account when comparing
+function declarations, for example:
+
+.. code-block:: c++
+
+  void foo(int a);
+  void foo(int); // no warning
+
+To help with refactoring, in some cases fix-it hints are generated to align
+parameter names to a single naming convention. This works with the assumption
+that the function definition is the most up-to-date version, as it directly
+references parameter names in its body. Example:
+
+.. code-block:: c++
+
+  void foo(int a); // warning and fix-it hint (replace "a" to "b")
+  int foo(int b) { return b + 2; } // definition with use of "b"
+
+In the case of multiple redeclarations or function template specializations,
+a warning is issued for every redeclaration or specialization inconsistent with
+the definition or the first declaration seen in a translation unit.
diff --git a/docs/clang-tidy/checks/readability-misleading-indentation.rst b/docs/clang-tidy/checks/readability-misleading-indentation.rst
new file mode 100644 (file)
index 0000000..5d52c77
--- /dev/null
@@ -0,0 +1,38 @@
+.. title:: clang-tidy - readability-misleading-indentation
+
+readability-misleading-indentation
+==================================
+
+Correct indentation helps to understand code. Mismatch of the syntactical
+structure and the indentation of the code may hide serious problems.
+Missing braces can also make it significantly harder to read the code,
+therefore it is important to use braces. 
+
+The way to avoid dangling else is to always check that an ``else`` belongs
+to the ``if`` that begins in the same column.
+
+You can omit braces when your inner part of e.g. an ``if`` statement has only
+one statement in it. Although in that case you should begin the next statement
+in the same column with the ``if``.
+
+Examples:
+
+.. code-block:: c++
+
+  // Dangling else:
+  if (cond1)
+    if (cond2)
+      foo1();
+  else
+    foo2();  // Wrong indentation: else belongs to if(cond2) statement.
+
+  // Missing braces:
+  if (cond1)
+    foo1();
+    foo2();  // Not guarded by if(cond1).
+
+Limitations
+-----------
+
+Note that this check only works as expected when the tabs or spaces are used
+consistently and not mixed.
diff --git a/docs/clang-tidy/checks/readability-misplaced-array-index.rst b/docs/clang-tidy/checks/readability-misplaced-array-index.rst
new file mode 100644 (file)
index 0000000..26a5af7
--- /dev/null
@@ -0,0 +1,27 @@
+.. title:: clang-tidy - readability-misplaced-array-index
+
+readability-misplaced-array-index
+=================================
+
+This check warns for unusual array index syntax.
+
+The following code has unusual array index syntax:
+
+.. code-block:: c++
+
+  void f(int *X, int Y) {
+    Y[X] = 0;
+  }
+
+becomes
+
+.. code-block:: c++
+
+  void f(int *X, int Y) {
+    X[Y] = 0;
+  }
+
+The check warns about such unusual syntax for readability reasons:
+ * There are programmers that are not familiar with this unusual syntax.
+ * It is possible that variables are mixed up.
+
diff --git a/docs/clang-tidy/checks/readability-named-parameter.rst b/docs/clang-tidy/checks/readability-named-parameter.rst
new file mode 100644 (file)
index 0000000..8d28c0a
--- /dev/null
@@ -0,0 +1,16 @@
+.. title:: clang-tidy - readability-named-parameter
+
+readability-named-parameter
+===========================
+
+Find functions with unnamed arguments.
+
+The check implements the following rule originating in the Google C++ Style
+Guide:
+
+https://google.github.io/styleguide/cppguide.html#Function_Declarations_and_Definitions
+
+All parameters should be named, with identical names in the declaration and
+implementation.
+
+Corresponding cpplint.py check name: `readability/function`.
diff --git a/docs/clang-tidy/checks/readability-non-const-parameter.rst b/docs/clang-tidy/checks/readability-non-const-parameter.rst
new file mode 100644 (file)
index 0000000..0729470
--- /dev/null
@@ -0,0 +1,46 @@
+.. title:: clang-tidy - readability-non-const-parameter
+
+readability-non-const-parameter
+===============================
+
+The check finds function parameters of a pointer type that could be changed to
+point to a constant type instead.
+
+When ``const`` is used properly, many mistakes can be avoided. Advantages when
+using ``const`` properly:
+
+- prevent unintentional modification of data;
+
+- get additional warnings such as using uninitialized data;
+
+- make it easier for developers to see possible side effects.
+
+This check is not strict about constness, it only warns when the constness will
+make the function interface safer.
+
+.. code-block:: c++
+
+  // warning here; the declaration "const char *p" would make the function
+  // interface safer.
+  char f1(char *p) {
+    return *p;
+  }
+
+  // no warning; the declaration could be more const "const int * const p" but
+  // that does not make the function interface safer.
+  int f2(const int *p) {
+    return *p;
+  }
+
+  // no warning; making x const does not make the function interface safer
+  int f3(int x) {
+    return x;
+  }
+
+  // no warning; Technically, *p can be const ("const struct S *p"). But making
+  // *p const could be misleading. People might think that it's safe to pass
+  // const data to this function.
+  struct S { int *a; int *b; };
+  int f3(struct S *p) {
+    *(p->a) = 0;
+  }
diff --git a/docs/clang-tidy/checks/readability-redundant-control-flow.rst b/docs/clang-tidy/checks/readability-redundant-control-flow.rst
new file mode 100644 (file)
index 0000000..aeaf345
--- /dev/null
@@ -0,0 +1,50 @@
+.. title:: clang-tidy - readability-redundant-control-flow
+
+readability-redundant-control-flow
+==================================
+
+This check looks for procedures (functions returning no value) with ``return``
+statements at the end of the function. Such ``return`` statements are redundant.
+
+Loop statements (``for``, ``while``, ``do while``) are checked for redundant
+``continue`` statements at the end of the loop body.
+
+Examples:
+
+The following function `f` contains a redundant ``return`` statement:
+
+.. code-block:: c++
+
+  extern void g();
+  void f() {
+    g();
+    return;
+  }
+
+becomes
+
+.. code-block:: c++
+
+  extern void g();
+  void f() {
+    g();
+  }
+
+The following function `k` contains a redundant ``continue`` statement:
+
+.. code-block:: c++
+
+  void k() {
+    for (int i = 0; i < 10; ++i) {
+      continue;
+    }
+  }
+
+becomes
+
+.. code-block:: c++
+
+  void k() {
+    for (int i = 0; i < 10; ++i) {
+    }
+  }
diff --git a/docs/clang-tidy/checks/readability-redundant-declaration.rst b/docs/clang-tidy/checks/readability-redundant-declaration.rst
new file mode 100644 (file)
index 0000000..4b5d0a5
--- /dev/null
@@ -0,0 +1,29 @@
+.. title:: clang-tidy - readability-redundant-declaration
+
+readability-redundant-declaration
+=================================
+
+Finds redundant variable and function declarations.
+
+.. code-block:: c++
+
+  extern int X;
+  extern int X;
+
+becomes
+
+.. code-block:: c++
+
+  extern int X;
+
+Such redundant declarations can be removed without changing program behaviour.
+They can for instance be unintentional left overs from previous refactorings
+when code has been moved around. Having redundant declarations could in worst
+case mean that there are typos in the code that cause bugs.
+
+Normally the code can be automatically fixed, :program:`clang-tidy` can remove
+the second declaration. However there are 2 cases when you need to fix the code
+manually:
+
+* When the declarations are in different header files;
+* When multiple variables are declared together.
diff --git a/docs/clang-tidy/checks/readability-redundant-function-ptr-dereference.rst b/docs/clang-tidy/checks/readability-redundant-function-ptr-dereference.rst
new file mode 100644 (file)
index 0000000..d8c7ec7
--- /dev/null
@@ -0,0 +1,24 @@
+.. title:: clang-tidy - readability-redundant-function-ptr-dereference
+
+readability-redundant-function-ptr-dereference
+==============================================
+
+Finds redundant dereferences of a function pointer.
+
+Before:
+
+.. code-block:: c++
+
+  int f(int,int);
+  int (*p)(int, int) = &f;
+
+  int i = (**p)(10, 50);
+
+After:
+
+.. code-block:: c++
+
+  int f(int,int);
+  int (*p)(int, int) = &f;
+
+  int i = (*p)(10, 50);
diff --git a/docs/clang-tidy/checks/readability-redundant-member-init.rst b/docs/clang-tidy/checks/readability-redundant-member-init.rst
new file mode 100644 (file)
index 0000000..a640e16
--- /dev/null
@@ -0,0 +1,20 @@
+.. title:: clang-tidy - readability-redundant-member-init
+
+readability-redundant-member-init
+=================================
+
+Finds member initializations that are unnecessary because the same default
+constructor would be called if they were not present.
+
+Example:
+
+.. code-block:: c++
+
+  // Explicitly initializing the member s is unnecessary.
+  class Foo {
+  public:
+    Foo() : s() {}
+
+  private:
+    std::string s;
+  };
diff --git a/docs/clang-tidy/checks/readability-redundant-smartptr-get.rst b/docs/clang-tidy/checks/readability-redundant-smartptr-get.rst
new file mode 100644 (file)
index 0000000..acf7986
--- /dev/null
@@ -0,0 +1,18 @@
+.. title:: clang-tidy - readability-redundant-smartptr-get
+
+readability-redundant-smartptr-get
+==================================
+
+`google-readability-redundant-smartptr-get` redirects here as an alias for this
+check.
+
+Find and remove redundant calls to smart pointer's ``.get()`` method.
+
+Examples:
+
+.. code-block:: c++
+
+  ptr.get()->Foo()  ==>  ptr->Foo()
+  *ptr.get()  ==>  *ptr
+  *ptr->get()  ==>  **ptr
+
diff --git a/docs/clang-tidy/checks/readability-redundant-string-cstr.rst b/docs/clang-tidy/checks/readability-redundant-string-cstr.rst
new file mode 100644 (file)
index 0000000..e6760a4
--- /dev/null
@@ -0,0 +1,7 @@
+.. title:: clang-tidy - readability-redundant-string-cstr
+
+readability-redundant-string-cstr
+=================================
+
+
+Finds unnecessary calls to ``std::string::c_str()`` and ``std::string::data()``.
diff --git a/docs/clang-tidy/checks/readability-redundant-string-init.rst b/docs/clang-tidy/checks/readability-redundant-string-init.rst
new file mode 100644 (file)
index 0000000..e4136ea
--- /dev/null
@@ -0,0 +1,19 @@
+.. title:: clang-tidy - readability-redundant-string-init
+
+readability-redundant-string-init
+=================================
+
+Finds unnecessary string initializations.
+
+Examples:
+
+.. code-block:: c++
+
+  // Initializing string with empty string literal is unnecessary.
+  std::string a = "";
+  std::string b("");
+
+  // becomes
+
+  std::string a;
+  std::string b;
diff --git a/docs/clang-tidy/checks/readability-simplify-boolean-expr.rst b/docs/clang-tidy/checks/readability-simplify-boolean-expr.rst
new file mode 100644 (file)
index 0000000..b333e18
--- /dev/null
@@ -0,0 +1,86 @@
+.. title:: clang-tidy - readability-simplify-boolean-expr
+
+readability-simplify-boolean-expr
+=================================
+
+Looks for boolean expressions involving boolean constants and simplifies
+them to use the appropriate boolean expression directly.
+
+Examples:
+
+===========================================  ================
+Initial expression                           Result
+-------------------------------------------  ----------------
+``if (b == true)``                             ``if (b)``
+``if (b == false)``                            ``if (!b)``
+``if (b && true)``                             ``if (b)``
+``if (b && false)``                            ``if (false)``
+``if (b || true)``                             ``if (true)``
+``if (b || false)``                            ``if (b)``
+``e ? true : false``                           ``e``
+``e ? false : true``                           ``!e``
+``if (true) t(); else f();``                   ``t();``
+``if (false) t(); else f();``                  ``f();``
+``if (e) return true; else return false;``     ``return e;``
+``if (e) return false; else return true;``     ``return !e;``
+``if (e) b = true; else b = false;``           ``b = e;``
+``if (e) b = false; else b = true;``           ``b = !e;``
+``if (e) return true; return false;``          ``return e;``
+``if (e) return false; return true;``          ``return !e;``
+===========================================  ================
+
+The resulting expression ``e`` is modified as follows:
+  1. Unnecessary parentheses around the expression are removed.
+  2. Negated applications of ``!`` are eliminated.
+  3. Negated applications of comparison operators are changed to use the
+     opposite condition.
+  4. Implicit conversions of pointers, including pointers to members, to
+     ``bool`` are replaced with explicit comparisons to ``nullptr`` in C++11
+     or ``NULL`` in C++98/03.
+  5. Implicit casts to ``bool`` are replaced with explicit casts to ``bool``.
+  6. Object expressions with ``explicit operator bool`` conversion operators
+     are replaced with explicit casts to ``bool``.
+  7. Implicit conversions of integral types to ``bool`` are replaced with
+     explicit comparisons to ``0``.
+
+Examples:
+  1. The ternary assignment ``bool b = (i < 0) ? true : false;`` has redundant
+     parentheses and becomes ``bool b = i < 0;``.
+
+  2. The conditional return ``if (!b) return false; return true;`` has an
+     implied double negation and becomes ``return b;``.
+
+  3. The conditional return ``if (i < 0) return false; return true;`` becomes
+     ``return i >= 0;``.
+
+     The conditional return ``if (i != 0) return false; return true;`` becomes
+     ``return i == 0;``.
+
+  4. The conditional return ``if (p) return true; return false;`` has an
+     implicit conversion of a pointer to ``bool`` and becomes
+     ``return p != nullptr;``.
+
+     The ternary assignment ``bool b = (i & 1) ? true : false;`` has an
+     implicit conversion of ``i & 1`` to ``bool`` and becomes
+     ``bool b = (i & 1) != 0;``.
+
+  5. The conditional return ``if (i & 1) return true; else return false;`` has
+     an implicit conversion of an integer quantity ``i & 1`` to ``bool`` and
+     becomes ``return (i & 1) != 0;``
+
+  6. Given ``struct X { explicit operator bool(); };``, and an instance ``x`` of
+     ``struct X``, the conditional return ``if (x) return true; return false;``
+     becomes ``return static_cast<bool>(x);``
+
+Options
+-------
+
+.. option:: ChainedConditionalReturn
+
+   If non-zero, conditional boolean return statements at the end of an
+   ``if/else if`` chain will be transformed. Default is `0`.
+
+.. option:: ChainedConditionalAssignment
+
+   If non-zero, conditional boolean assignments at the end of an ``if/else
+   if`` chain will be transformed. Default is `0`.
diff --git a/docs/clang-tidy/checks/readability-static-definition-in-anonymous-namespace.rst b/docs/clang-tidy/checks/readability-static-definition-in-anonymous-namespace.rst
new file mode 100644 (file)
index 0000000..c1803d4
--- /dev/null
@@ -0,0 +1,18 @@
+.. title:: clang-tidy - readability-static-definition-in-anonymous-namespace
+
+readability-static-definition-in-anonymous-namespace
+====================================================
+
+Finds static function and variable definitions in anonymous namespace.
+
+In this case, ``static`` is redundant, because anonymous namespace limits the
+visibility of definitions to a single translation unit.
+
+.. code-block:: c++
+
+  namespace {
+    static int a = 1; // Warning.
+    static const b = 1; // Warning.
+  }
+
+The check will apply a fix by removing the redundant ``static`` qualifier.
diff --git a/docs/clang-tidy/checks/readability-uniqueptr-delete-release.rst b/docs/clang-tidy/checks/readability-uniqueptr-delete-release.rst
new file mode 100644 (file)
index 0000000..beec1d5
--- /dev/null
@@ -0,0 +1,17 @@
+.. title:: clang-tidy - readability-uniqueptr-delete-release
+
+readability-uniqueptr-delete-release
+====================================
+
+Replace ``delete <unique_ptr>.release()`` with ``<unique_ptr> = nullptr``.
+The latter is shorter, simpler and does not require use of raw pointer APIs.
+
+.. code-block:: c++
+
+  std::unique_ptr<int> P;
+  delete P.release();
+
+  // becomes
+
+  std::unique_ptr<int> P;
+  P = nullptr;
diff --git a/docs/clang-tidy/index.rst b/docs/clang-tidy/index.rst
new file mode 100644 (file)
index 0000000..69136e6
--- /dev/null
@@ -0,0 +1,665 @@
+==========
+Clang-Tidy
+==========
+
+.. contents::
+
+See also:
+
+.. toctree::
+   :maxdepth: 1
+
+   The list of clang-tidy checks <checks/list>
+
+:program:`clang-tidy` is a clang-based C++ "linter" tool. Its purpose is to
+provide an extensible framework for diagnosing and fixing typical programming
+errors, like style violations, interface misuse, or bugs that can be deduced via
+static analysis. :program:`clang-tidy` is modular and provides a convenient
+interface for writing new checks.
+
+
+Using clang-tidy
+================
+
+:program:`clang-tidy` is a `LibTooling`_-based tool, and it's easier to work
+with if you set up a compile command database for your project (for an example
+of how to do this see `How To Setup Tooling For LLVM`_). You can also specify
+compilation options on the command line after ``--``:
+
+.. code-block:: console
+
+  $ clang-tidy test.cpp -- -Imy_project/include -DMY_DEFINES ...
+
+:program:`clang-tidy` has its own checks and can also run Clang static analyzer
+checks. Each check has a name and the checks to run can be chosen using the
+``-checks=`` option, which specifies a comma-separated list of positive and
+negative (prefixed with ``-``) globs. Positive globs add subsets of checks,
+negative globs remove them. For example,
+
+.. code-block:: console
+
+  $ clang-tidy test.cpp -checks=-*,clang-analyzer-*,-clang-analyzer-cplusplus*
+
+will disable all default checks (``-*``) and enable all ``clang-analyzer-*``
+checks except for ``clang-analyzer-cplusplus*`` ones.
+
+The ``-list-checks`` option lists all the enabled checks. When used without
+``-checks=``, it shows checks enabled by default. Use ``-checks=*`` to see all
+available checks or with any other value of ``-checks=`` to see which checks are
+enabled by this value.
+
+.. _checks-groups-table:
+
+There are currently the following groups of checks:
+
+====================== =========================================================
+Name prefix            Description
+====================== =========================================================
+``android-``           Checks related to Android.
+``boost-``             Checks related to Boost library.
+``bugprone-``          Checks that target bugprone code constructs.
+``cert-``              Checks related to CERT Secure Coding Guidelines.
+``cppcoreguidelines-`` Checks related to C++ Core Guidelines.
+``clang-analyzer-``    Clang Static Analyzer checks.
+``google-``            Checks related to Google coding conventions.
+``hicpp-``             Checks related to High Integrity C++ Coding Standard.
+``llvm-``              Checks related to the LLVM coding conventions.
+``misc-``              Checks that we didn't have a better category for.
+``modernize-``         Checks that advocate usage of modern (currently "modern"
+                       means "C++11") language constructs.
+``mpi-``               Checks related to MPI (Message Passing Interface).
+``performance-``       Checks that target performance-related issues.
+``readability-``       Checks that target readability-related issues that don't
+                       relate to any particular coding style.
+====================== =========================================================
+
+Clang diagnostics are treated in a similar way as check diagnostics. Clang
+diagnostics are displayed by :program:`clang-tidy` and can be filtered out using
+``-checks=`` option. However, the ``-checks=`` option does not affect
+compilation arguments, so it can not turn on Clang warnings which are not
+already turned on in build configuration. The ``-warnings-as-errors=`` option
+upgrades any warnings emitted under the ``-checks=`` flag to errors (but it
+does not enable any checks itself).
+
+Clang diagnostics have check names starting with ``clang-diagnostic-``.
+Diagnostics which have a corresponding warning option, are named
+``clang-diagnostic-<warning-option>``, e.g. Clang warning controlled by
+``-Wliteral-conversion`` will be reported with check name
+``clang-diagnostic-literal-conversion``.
+
+The ``-fix`` flag instructs :program:`clang-tidy` to fix found errors if
+supported by corresponding checks.
+
+An overview of all the command-line options:
+
+.. code-block:: console
+
+  $ clang-tidy -help
+  USAGE: clang-tidy [options] <source0> [... <sourceN>]
+
+  OPTIONS:
+
+  Generic Options:
+
+    -help                        - Display available options (-help-hidden for more)
+    -help-list                   - Display list of available options (-help-list-hidden for more)
+    -version                     - Display the version of this program
+
+  clang-tidy options:
+
+    -analyze-temporary-dtors     -
+                                   Enable temporary destructor-aware analysis in
+                                   clang-analyzer- checks.
+                                   This option overrides the value read from a
+                                   .clang-tidy file.
+    -checks=<string>             -
+                                   Comma-separated list of globs with optional '-'
+                                   prefix. Globs are processed in order of
+                                   appearance in the list. Globs without '-'
+                                   prefix add checks with matching names to the
+                                   set, globs with the '-' prefix remove checks
+                                   with matching names from the set of enabled
+                                   checks. This option's value is appended to the
+                                   value of the 'Checks' option in .clang-tidy
+                                   file, if any.
+    -config=<string>             -
+                                   Specifies a configuration in YAML/JSON format:
+                                     -config="{Checks: '*',
+                                               CheckOptions: [{key: x,
+                                                               value: y}]}"
+                                   When the value is empty, clang-tidy will
+                                   attempt to find a file named .clang-tidy for
+                                   each source file in its parent directories.
+    -dump-config                 -
+                                   Dumps configuration in the YAML format to
+                                   stdout. This option can be used along with a
+                                   file name (and '--' if the file is outside of a
+                                   project with configured compilation database).
+                                   The configuration used for this file will be
+                                   printed.
+                                   Use along with -checks=* to include
+                                   configuration of all checks.
+    -enable-check-profile        -
+                                   Enable per-check timing profiles, and print a
+                                   report to stderr.
+    -explain-config              -
+                                   For each enabled check explains, where it is
+                                   enabled, i.e. in clang-tidy binary, command
+                                   line or a specific configuration file.
+    -export-fixes=<filename>     -
+                                   YAML file to store suggested fixes in. The
+                                   stored fixes can be applied to the input source
+                                   code with clang-apply-replacements.
+    -extra-arg=<string>          - Additional argument to append to the compiler command line
+    -extra-arg-before=<string>   - Additional argument to prepend to the compiler command line
+    -fix                         -
+                                   Apply suggested fixes. Without -fix-errors
+                                   clang-tidy will bail out if any compilation
+                                   errors were found.
+    -fix-errors                  -
+                                   Apply suggested fixes even if compilation
+                                   errors were found. If compiler errors have
+                                   attached fix-its, clang-tidy will apply them as
+                                   well.
+    -format-style=<string>       -
+                                   Style for formatting code around applied fixes:
+                                     - 'none' (default) turns off formatting
+                                     - 'file' (literally 'file', not a placeholder)
+                                       uses .clang-format file in the closest parent
+                                       directory
+                                     - '{ <json> }' specifies options inline, e.g.
+                                       -format-style='{BasedOnStyle: llvm, IndentWidth: 8}'
+                                     - 'llvm', 'google', 'webkit', 'mozilla'
+                                   See clang-format documentation for the up-to-date
+                                   information about formatting styles and options.
+                                   This option overrides the 'FormatStyle` option in
+                                   .clang-tidy file, if any.
+    -header-filter=<string>      -
+                                   Regular expression matching the names of the
+                                   headers to output diagnostics from. Diagnostics
+                                   from the main file of each translation unit are
+                                   always displayed.
+                                   Can be used together with -line-filter.
+                                   This option overrides the 'HeaderFilter' option
+                                   in .clang-tidy file, if any.
+    -line-filter=<string>        -
+                                   List of files with line ranges to filter the
+                                   warnings. Can be used together with
+                                   -header-filter. The format of the list is a
+                                   JSON array of objects:
+                                     [
+                                       {"name":"file1.cpp","lines":[[1,3],[5,7]]},
+                                       {"name":"file2.h"}
+                                     ]
+    -list-checks                 -
+                                   List all enabled checks and exit. Use with
+                                   -checks=* to list all available checks.
+    -p=<string>                  - Build path
+    -quiet                       -
+                                   Run clang-tidy in quiet mode. This suppresses
+                                   printing statistics about ignored warnings and
+                                   warnings treated as errors if the respective
+                                   options are specified.
+    -system-headers              - Display the errors from system headers.
+    -warnings-as-errors=<string> -
+                                   Upgrades warnings to errors. Same format as
+                                   '-checks'.
+                                   This option's value is appended to the value of
+                                   the 'WarningsAsErrors' option in .clang-tidy
+                                   file, if any.
+
+  -p <build-path> is used to read a compile command database.
+
+          For example, it can be a CMake build directory in which a file named
+          compile_commands.json exists (use -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
+          CMake option to get this output). When no build path is specified,
+          a search for compile_commands.json will be attempted through all
+          parent paths of the first input file . See:
+          http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html for an
+          example of setting up Clang Tooling on a source tree.
+
+  <source0> ... specify the paths of source files. These paths are
+          looked up in the compile command database. If the path of a file is
+          absolute, it needs to point into CMake's source tree. If the path is
+          relative, the current working directory needs to be in the CMake
+          source tree and the file must be in a subdirectory of the current
+          working directory. "./" prefixes in the relative files will be
+          automatically removed, but the rest of a relative path must be a
+          suffix of a path in the compile command database.
+
+
+  Configuration files:
+    clang-tidy attempts to read configuration for each source file from a
+    .clang-tidy file located in the closest parent directory of the source
+    file. If any configuration options have a corresponding command-line
+    option, command-line option takes precedence. The effective
+    configuration can be inspected using -dump-config:
+
+      $ clang-tidy -dump-config
+      ---
+      Checks:          '-*,some-check'
+      WarningsAsErrors: ''
+      HeaderFilterRegex: ''
+      AnalyzeTemporaryDtors: false
+      FormatStyle:     none
+      User:            user
+      CheckOptions:
+        - key:             some-check.SomeOption
+          value:           'some value'
+      ...
+
+.. _LibTooling: http://clang.llvm.org/docs/LibTooling.html
+.. _How To Setup Tooling For LLVM: http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html
+
+
+Getting Involved
+================
+
+:program:`clang-tidy` has several own checks and can run Clang static analyzer
+checks, but its power is in the ability to easily write custom checks.
+
+Checks are organized in modules, which can be linked into :program:`clang-tidy`
+with minimal or no code changes in :program:`clang-tidy`.
+
+Checks can plug into the analysis on the preprocessor level using `PPCallbacks`_
+or on the AST level using `AST Matchers`_. When an error is found, checks can
+report them in a way similar to how Clang diagnostics work. A fix-it hint can be
+attached to a diagnostic message.
+
+The interface provided by :program:`clang-tidy` makes it easy to write useful
+and precise checks in just a few lines of code. If you have an idea for a good
+check, the rest of this document explains how to do this.
+
+There are a few tools particularly useful when developing clang-tidy checks:
+  * ``add_new_check.py`` is a script to automate the process of adding a new
+    check, it will create the check, update the CMake file and create a test;
+  * ``rename_check.py`` does what the script name suggests, renames an existing
+    check;
+  * :program:`clang-query` is invaluable for interactive prototyping of AST
+    matchers and exploration of the Clang AST;
+  * `clang-check`_ with the ``-ast-dump`` (and optionally ``-ast-dump-filter``)
+    provides a convenient way to dump AST of a C++ program.
+
+
+.. _AST Matchers: http://clang.llvm.org/docs/LibASTMatchers.html
+.. _PPCallbacks: http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html
+.. _clang-check: http://clang.llvm.org/docs/ClangCheck.html
+
+
+Choosing the Right Place for your Check
+---------------------------------------
+
+If you have an idea of a check, you should decide whether it should be
+implemented as a:
+
++ *Clang diagnostic*: if the check is generic enough, targets code patterns that
+  most probably are bugs (rather than style or readability issues), can be
+  implemented effectively and with extremely low false positive rate, it may
+  make a good Clang diagnostic.
+
++ *Clang static analyzer check*: if the check requires some sort of control flow
+  analysis, it should probably be implemented as a static analyzer check.
+
++ *clang-tidy check* is a good choice for linter-style checks, checks that are
+  related to a certain coding style, checks that address code readability, etc.
+
+
+Preparing your Workspace
+------------------------
+
+If you are new to LLVM development, you should read the `Getting Started with
+the LLVM System`_, `Using Clang Tools`_ and `How To Setup Tooling For LLVM`_
+documents to check out and build LLVM, Clang and Clang Extra Tools with CMake.
+
+Once you are done, change to the ``llvm/tools/clang/tools/extra`` directory, and
+let's start!
+
+.. _Getting Started with the LLVM System: http://llvm.org/docs/GettingStarted.html
+.. _Using Clang Tools: http://clang.llvm.org/docs/ClangTools.html
+
+
+The Directory Structure
+-----------------------
+
+:program:`clang-tidy` source code resides in the
+``llvm/tools/clang/tools/extra`` directory and is structured as follows:
+
+::
+
+  clang-tidy/                       # Clang-tidy core.
+  |-- ClangTidy.h                   # Interfaces for users and checks.
+  |-- ClangTidyModule.h             # Interface for clang-tidy modules.
+  |-- ClangTidyModuleRegistry.h     # Interface for registering of modules.
+     ...
+  |-- google/                       # Google clang-tidy module.
+  |-+
+    |-- GoogleTidyModule.cpp
+    |-- GoogleTidyModule.h
+          ...
+  |-- llvm/                         # LLVM clang-tidy module.
+  |-+
+    |-- LLVMTidyModule.cpp
+    |-- LLVMTidyModule.h
+          ...
+  |-- tool/                         # Sources of the clang-tidy binary.
+          ...
+  test/clang-tidy/                  # Integration tests.
+      ...
+  unittests/clang-tidy/             # Unit tests.
+  |-- ClangTidyTest.h
+  |-- GoogleModuleTest.cpp
+  |-- LLVMModuleTest.cpp
+      ...
+
+
+Writing a clang-tidy Check
+--------------------------
+
+So you have an idea of a useful check for :program:`clang-tidy`.
+
+First, if you're not familiar with LLVM development, read through the `Getting
+Started with LLVM`_ document for instructions on setting up your workflow and
+the `LLVM Coding Standards`_ document to familiarize yourself with the coding
+style used in the project. For code reviews we mostly use `LLVM Phabricator`_.
+
+.. _Getting Started with LLVM: http://llvm.org/docs/GettingStarted.html
+.. _LLVM Coding Standards: http://llvm.org/docs/CodingStandards.html
+.. _LLVM Phabricator: http://llvm.org/docs/Phabricator.html
+
+Next, you need to decide which module the check belongs to. Modules
+are located in subdirectories of `clang-tidy/
+<http://reviews.llvm.org/diffusion/L/browse/clang-tools-extra/trunk/clang-tidy/>`_
+and contain checks targeting a certain aspect of code quality (performance,
+readability, etc.), certain coding style or standard (Google, LLVM, CERT, etc.)
+or a widely used API (e.g. MPI). Their names are same as user-facing check
+groups names described :ref:`above <checks-groups-table>`.
+
+After choosing the module and the name for the check, run the
+``clang-tidy/add_new_check.py`` script to create the skeleton of the check and
+plug it to :program:`clang-tidy`. It's the recommended way of adding new checks.
+
+If we want to create a `readability-awesome-function-names`, we would run:
+
+.. code-block:: console
+
+  $ clang-tidy/add_new_check.py readability awesome-function-names
+
+
+The ``add_new_check.py`` script will:
+  * create the class for your check inside the specified module's directory and
+    register it in the module and in the build system;
+  * create a lit test file in the ``test/clang-tidy/`` directory;
+  * create a documentation file and include it into the
+    ``docs/clang-tidy/checks/list.rst``.
+
+Let's see in more detail at the check class definition:
+
+.. code-block:: c++
+
+  ...
+
+  #include "../ClangTidy.h"
+
+  namespace clang {
+  namespace tidy {
+  namespace readability {
+
+  ...
+  class AwesomeFunctionNamesCheck : public ClangTidyCheck {
+  public:
+    AwesomeFunctionNamesCheck(StringRef Name, ClangTidyContext *Context)
+        : ClangTidyCheck(Name, Context) {}
+    void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+    void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+  };
+
+  } // namespace readability
+  } // namespace tidy
+  } // namespace clang
+
+  ...
+
+Constructor of the check receives the ``Name`` and ``Context`` parameters, and
+must forward them to the ``ClangTidyCheck`` constructor.
+
+In our case the check needs to operate on the AST level and it overrides the
+``registerMatchers`` and ``check`` methods. If we wanted to analyze code on the
+preprocessor level, we'd need instead to override the ``registerPPCallbacks``
+method.
+
+In the ``registerMatchers`` method we create an AST Matcher (see `AST Matchers`_
+for more information) that will find the pattern in the AST that we want to
+inspect. The results of the matching are passed to the ``check`` method, which
+can further inspect them and report diagnostics.
+
+.. code-block:: c++
+
+  using namespace ast_matchers;
+
+  void AwesomeFunctionNamesCheck::registerMatchers(MatchFinder *Finder) {
+    Finder->addMatcher(functionDecl().bind("x"), this);
+  }
+
+  void AwesomeFunctionNamesCheck::check(const MatchFinder::MatchResult &Result) {
+    const auto *MatchedDecl = Result.Nodes.getNodeAs<FunctionDecl>("x");
+    if (MatchedDecl->getName().startswith("awesome_"))
+      return;
+    diag(MatchedDecl->getLocation(), "function %0 is insufficiently awesome")
+        << MatchedDecl
+        << FixItHint::CreateInsertion(MatchedDecl->getLocation(), "awesome_");
+  }
+
+(If you want to see an example of a useful check, look at
+`clang-tidy/google/ExplicitConstructorCheck.h
+<http://reviews.llvm.org/diffusion/L/browse/clang-tools-extra/trunk/clang-tidy/google/ExplicitConstructorCheck.h>`_
+and `clang-tidy/google/ExplicitConstructorCheck.cpp
+<http://reviews.llvm.org/diffusion/L/browse/clang-tools-extra/trunk/clang-tidy/google/ExplicitConstructorCheck.cpp>`_).
+
+
+Registering your Check
+----------------------
+
+(The ``add_new_check.py`` takes care of registering the check in an existing
+module. If you want to create a new module or know the details, read on.)
+
+The check should be registered in the corresponding module with a distinct name:
+
+.. code-block:: c++
+
+  class MyModule : public ClangTidyModule {
+   public:
+    void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
+      CheckFactories.registerCheck<ExplicitConstructorCheck>(
+          "my-explicit-constructor");
+    }
+  };
+
+Now we need to register the module in the ``ClangTidyModuleRegistry`` using a
+statically initialized variable:
+
+.. code-block:: c++
+
+  static ClangTidyModuleRegistry::Add<MyModule> X("my-module",
+                                                  "Adds my lint checks.");
+
+
+When using LLVM build system, we need to use the following hack to ensure the
+module is linked into the :program:`clang-tidy` binary:
+
+Add this near the ``ClangTidyModuleRegistry::Add<MyModule>`` variable:
+
+.. code-block:: c++
+
+  // This anchor is used to force the linker to link in the generated object file
+  // and thus register the MyModule.
+  volatile int MyModuleAnchorSource = 0;
+
+And this to the main translation unit of the :program:`clang-tidy` binary (or
+the binary you link the ``clang-tidy`` library in)
+``clang-tidy/tool/ClangTidyMain.cpp``:
+
+.. code-block:: c++
+
+  // This anchor is used to force the linker to link the MyModule.
+  extern volatile int MyModuleAnchorSource;
+  static int MyModuleAnchorDestination = MyModuleAnchorSource;
+
+
+Configuring Checks
+------------------
+
+If a check needs configuration options, it can access check-specific options
+using the ``Options.get<Type>("SomeOption", DefaultValue)`` call in the check
+constructor. In this case the check should also override the
+``ClangTidyCheck::storeOptions`` method to make the options provided by the
+check discoverable. This method lets :program:`clang-tidy` know which options
+the check implements and what the current values are (e.g. for the
+``-dump-config`` command line option).
+
+.. code-block:: c++
+
+  class MyCheck : public ClangTidyCheck {
+    const unsigned SomeOption1;
+    const std::string SomeOption2;
+
+  public:
+    MyCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context),
+        SomeOption(Options.get("SomeOption1", -1U)),
+        SomeOption(Options.get("SomeOption2", "some default")) {}
+
+    void storeOptions(ClangTidyOptions::OptionMap &Opts) override {
+      Options.store(Opts, "SomeOption1", SomeOption1);
+      Options.store(Opts, "SomeOption2", SomeOption2);
+    }
+    ...
+
+Assuming the check is registered with the name "my-check", the option can then
+be set in a ``.clang-tidy`` file in the following way:
+
+.. code-block:: yaml
+
+  CheckOptions:
+    - key: my-check.SomeOption1
+      value: 123
+    - key: my-check.SomeOption2
+      value: 'some other value'
+
+If you need to specify check options on a command line, you can use the inline
+YAML format:
+
+.. code-block:: console
+
+  $ clang-tidy -config="{CheckOptions: [{key: a, value: b}, {key: x, value: y}]}" ...
+
+
+Testing Checks
+--------------
+
+To run tests for :program:`clang-tidy` use the command:
+
+.. code-block:: console
+
+  $ ninja check-clang-tools
+
+:program:`clang-tidy` checks can be tested using either unit tests or
+`lit`_ tests. Unit tests may be more convenient to test complex replacements
+with strict checks. `Lit`_ tests allow using partial text matching and regular
+expressions which makes them more suitable for writing compact tests for
+diagnostic messages.
+
+The ``check_clang_tidy.py`` script provides an easy way to test both
+diagnostic messages and fix-its. It filters out ``CHECK`` lines from the test
+file, runs :program:`clang-tidy` and verifies messages and fixes with two
+separate `FileCheck`_ invocations: once with FileCheck's directive
+prefix set to ``CHECK-MESSAGES``, validating the diagnostic messages,
+and once with the directive prefix set to ``CHECK-FIXES``, running
+against the fixed code (i.e., the code after generated fix-its are
+applied). In particular, ``CHECK-FIXES:`` can be used to check
+that code was not modified by fix-its, by checking that it is present
+unchanged in the fixed code. The full set of `FileCheck`_ directives
+is available (e.g., ``CHECK-MESSAGES-SAME:``, ``CHECK-MESSAGES-NOT:``), though
+typically the basic ``CHECK`` forms (``CHECK-MESSAGES`` and ``CHECK-FIXES``)
+are sufficient for clang-tidy tests. Note that the `FileCheck`_
+documentation mostly assumes the default prefix (``CHECK``), and hence
+describes the directive as ``CHECK:``, ``CHECK-SAME:``, ``CHECK-NOT:``, etc.
+Replace ``CHECK`` by either ``CHECK-FIXES`` or ``CHECK-MESSAGES`` for
+clang-tidy tests.
+
+An additional check enabled by ``check_clang_tidy.py`` ensures that
+if `CHECK-MESSAGES:` is used in a file then every warning or error
+must have an associated CHECK in that file.
+
+To use the ``check_clang_tidy.py`` script, put a .cpp file with the
+appropriate ``RUN`` line in the ``test/clang-tidy`` directory. Use
+``CHECK-MESSAGES:`` and ``CHECK-FIXES:`` lines to write checks against
+diagnostic messages and fixed code.
+
+It's advised to make the checks as specific as possible to avoid checks matching
+to incorrect parts of the input. Use ``[[@LINE+X]]``/``[[@LINE-X]]``
+substitutions and distinct function and variable names in the test code.
+
+Here's an example of a test using the ``check_clang_tidy.py`` script (the full
+source code is at `test/clang-tidy/google-readability-casting.cpp`_):
+
+.. code-block:: c++
+
+  // RUN: %check_clang_tidy %s google-readability-casting %t
+
+  void f(int a) {
+    int b = (int)a;
+    // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: redundant cast to the same type [google-readability-casting]
+    // CHECK-FIXES: int b = a;
+  }
+
+There are many dark corners in the C++ language, and it may be difficult to make
+your check work perfectly in all cases, especially if it issues fix-it hints. The
+most frequent pitfalls are macros and templates:
+
+1. code written in a macro body/template definition may have a different meaning
+   depending on the macro expansion/template instantiation;
+2. multiple macro expansions/template instantiations may result in the same code
+   being inspected by the check multiple times (possibly, with different
+   meanings, see 1), and the same warning (or a slightly different one) may be
+   issued by the check multiple times; :program:`clang-tidy` will deduplicate
+   _identical_ warnings, but if the warnings are slightly different, all of them
+   will be shown to the user (and used for applying fixes, if any);
+3. making replacements to a macro body/template definition may be fine for some
+   macro expansions/template instantiations, but easily break some other
+   expansions/instantiations.
+
+.. _lit: http://llvm.org/docs/CommandGuide/lit.html
+.. _FileCheck: http://llvm.org/docs/CommandGuide/FileCheck.html
+.. _test/clang-tidy/google-readability-casting.cpp: http://reviews.llvm.org/diffusion/L/browse/clang-tools-extra/trunk/test/clang-tidy/google-readability-casting.cpp
+
+
+Running clang-tidy on LLVM
+--------------------------
+
+To test a check it's best to try it out on a larger code base. LLVM and Clang
+are the natural targets as you already have the source code around. The most
+convenient way to run :program:`clang-tidy` is with a compile command database;
+CMake can automatically generate one, for a description of how to enable it see
+`How To Setup Tooling For LLVM`_. Once ``compile_commands.json`` is in place and
+a working version of :program:`clang-tidy` is in ``PATH`` the entire code base
+can be analyzed with ``clang-tidy/tool/run-clang-tidy.py``. The script executes
+:program:`clang-tidy` with the default set of checks on every translation unit
+in the compile command database and displays the resulting warnings and errors.
+The script provides multiple configuration flags.
+
+* The default set of checks can be overridden using the ``-checks`` argument,
+  taking the identical format as :program:`clang-tidy` does. For example
+  ``-checks=-*,modernize-use-override`` will run the ``modernize-use-override``
+  check only.
+
+* To restrict the files examined you can provide one or more regex arguments
+  that the file names are matched against.
+  ``run-clang-tidy.py clang-tidy/.*Check\.cpp`` will only analyze clang-tidy
+  checks. It may also be necessary to restrict the header files warnings are
+  displayed from using the ``-header-filter`` flag. It has the same behavior
+  as the corresponding :program:`clang-tidy` flag.
+
+* To apply suggested fixes ``-fix`` can be passed as an argument. This gathers
+  all changes in a temporary directory and applies them. Passing ``-format``
+  will run clang-format over changed lines.
+
diff --git a/docs/clangd.rst b/docs/clangd.rst
new file mode 100644 (file)
index 0000000..d7284a2
--- /dev/null
@@ -0,0 +1,106 @@
+============
+Clangd
+============
+
+.. contents::
+
+.. toctree::
+   :maxdepth: 1
+
+:program:`Clangd` is an implementation of the `Language Server Protocol
+<https://github.com/Microsoft/language-server-protocol>`_ leveraging Clang.
+Clangd's goal is to provide language "smartness" features like code completion,
+find references, etc. for clients such as C/C++ Editors.
+
+Using Clangd
+==================
+
+:program:`Clangd` is not meant to be used by C/C++ developers directly but
+rather from a client implementing the protocol. A client would be typically
+implemented in an IDE or an editor.
+
+At the moment, `Visual Studio Code <https://code.visualstudio.com/>`_ is mainly
+used in order to test :program:`Clangd` but more clients are likely to make
+use of :program:`Clangd` in the future as it matures and becomes a production
+quality tool. If you are interested in trying :program:`Clangd` in combination
+with Visual Studio Code, you can start by `building Clangd`_, then open Visual
+Studio Code in the clangd-vscode folder and launch the extension.
+
+Building Clangd
+==================
+
+You can follow the instructions for `building Clang
+<https://clang.llvm.org/get_started.html>`_ but "extra Clang tools" is **not**
+optional.
+
+Current Status
+==================
+
+Many features could be implemented in :program:`Clangd`.
+Here is a list of features that could be useful with the status of whether or
+not they are already implemented in :program:`Clangd` and specified in the
+Language Server Protocol. Note that for some of the features, it is not clear
+whether or not they should be part of the Language Server Protocol, so those
+features might be eventually developed outside :program:`Clangd`.
+
++-------------------------------------+------------+----------+
+| C/C++ Editor feature                |  LSP       |  Clangd  |
++=====================================+============+==========+
+| Formatting                          | Yes        |   Yes    |
++-------------------------------------+------------+----------+
+| Completion                          | Yes        |   Yes    |
++-------------------------------------+------------+----------+
+| Diagnostics                         | Yes        |   Yes    |
++-------------------------------------+------------+----------+ 
+| Fix-its                             | Yes        |   Yes    |
++-------------------------------------+------------+----------+
+| Go to Definition                    | Yes        |   Yes    |
++-------------------------------------+------------+----------+
+| Source hover                        | Yes        |   No     |
++-------------------------------------+------------+----------+
+| Signature Help                      | Yes        |   No     |
++-------------------------------------+------------+----------+
+| Find References                     | Yes        |   No     |
++-------------------------------------+------------+----------+
+| Document Highlights                 | Yes        |   No     |
++-------------------------------------+------------+----------+
+| Rename                              | Yes        |   No     |
++-------------------------------------+------------+----------+
+| Code Lens                           | Yes        |   No     |
++-------------------------------------+------------+----------+
+| Syntax and Semantic Coloring        | No         |   No     |
++-------------------------------------+------------+----------+
+| Code folding                        | No         |   No     |
++-------------------------------------+------------+----------+
+| Call hierarchy                      | No         |   No     |
++-------------------------------------+------------+----------+
+| Type hierarchy                      | No         |   No     |
++-------------------------------------+------------+----------+
+| Organize Includes                   | No         |   No     |
++-------------------------------------+------------+----------+
+| Quick Assist                        | No         |   No     |
++-------------------------------------+------------+----------+
+| Extract Local Variable              | No         |   No     |
++-------------------------------------+------------+----------+
+| Extract Function/Method             | No         |   No     |
++-------------------------------------+------------+----------+
+| Hide Method                         | No         |   No     |
++-------------------------------------+------------+----------+
+| Implement Method                    | No         |   No     |
++-------------------------------------+------------+----------+
+| Gen. Getters/Setters                | No         |   No     |
++-------------------------------------+------------+----------+
+
+Getting Involved
+==================
+
+A good place for interested contributors is the `Clang developer mailing list
+<http://lists.llvm.org/mailman/listinfo/cfe-dev>`_.
+If you're also interested in contributing patches to :program:`Clangd`, take a
+look at the `LLVM Developer Policy
+<http://llvm.org/docs/DeveloperPolicy.html>`_ and `Code Reviews
+<http://llvm.org/docs/Phabricator.html>`_ page. Contributions of new features
+to the `Language Server Protocol
+<https://github.com/Microsoft/language-server-protocol>`_ itself would also be
+very useful, so that :program:`Clangd` can eventually implement them in a
+conforming way.
diff --git a/docs/conf.py b/docs/conf.py
new file mode 100644 (file)
index 0000000..e872c55
--- /dev/null
@@ -0,0 +1,243 @@
+# -*- coding: utf-8 -*-
+#
+# Extra Clang Tools documentation build configuration file, created by
+# sphinx-quickstart on Wed Feb 13 10:00:18 2013.
+#
+# This file is execfile()d with the current directory set to its containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+import sys, os
+from datetime import date
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#sys.path.insert(0, os.path.abspath('.'))
+
+# -- General configuration -----------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be extensions
+# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
+extensions = ['sphinx.ext.todo', 'sphinx.ext.mathjax']
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The encoding of source files.
+#source_encoding = 'utf-8-sig'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = u'Extra Clang Tools'
+copyright = u'2007-%d, The Clang Team' % date.today().year
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short version.
+version = '5'
+# The full version, including alpha/beta/rc tags.
+release = '5'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+#today_fmt = '%B %d, %Y'
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+exclude_patterns = ['_build']
+
+# The reST default role (used for this markup: `text`) to use for all documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'friendly'
+
+# A list of ignored prefixes for module index sorting.
+#modindex_common_prefix = []
+
+
+# -- Options for HTML output ---------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages.  See the documentation for
+# a list of builtin themes.
+html_theme = 'haiku'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further.  For a list of options available for each theme, see the
+# documentation.
+#html_theme_options = {}
+
+# Add any paths that contain custom themes here, relative to this directory.
+#html_theme_path = []
+
+# The name for this set of Sphinx documents.  If None, it defaults to
+# "<project> v<release> documentation".
+#html_title = None
+
+# A shorter title for the navigation bar.  Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+#html_logo = None
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = []
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+#html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+#html_domain_indices = True
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+#html_show_sourcelink = True
+
+# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
+#html_show_sphinx = True
+
+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
+#html_show_copyright = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it.  The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# This is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = None
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'ExtraClangToolsdoc'
+
+
+# -- Options for LaTeX output --------------------------------------------------
+
+latex_elements = {
+# The paper size ('letterpaper' or 'a4paper').
+#'papersize': 'letterpaper',
+
+# The font size ('10pt', '11pt' or '12pt').
+#'pointsize': '10pt',
+
+# Additional stuff for the LaTeX preamble.
+#'preamble': '',
+}
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title, author, documentclass [howto/manual]).
+latex_documents = [
+  ('index', 'ExtraClangTools.tex', u'Extra Clang Tools Documentation',
+   u'The Clang Team', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# If true, show page references after internal links.
+#latex_show_pagerefs = False
+
+# If true, show URL addresses after external links.
+#latex_show_urls = False
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+#latex_domain_indices = True
+
+
+# -- Options for manual page output --------------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+    ('index', 'extraclangtools', u'Extra Clang Tools Documentation',
+     [u'The Clang Team'], 1)
+]
+
+# If true, show URL addresses after external links.
+#man_show_urls = False
+
+
+# -- Options for Texinfo output ------------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+#  dir menu entry, description, category)
+texinfo_documents = [
+  ('index', 'ExtraClangTools', u'Extra Clang Tools Documentation',
+   u'The Clang Team', 'ExtraClangTools', 'One line description of project.',
+   'Miscellaneous'),
+]
+
+# Documents to append as an appendix to all manuals.
+#texinfo_appendices = []
+
+# If false, no module index is generated.
+#texinfo_domain_indices = True
+
+# How to display URL addresses: 'footnote', 'no', or 'inline'.
+#texinfo_show_urls = 'footnote'
diff --git a/docs/cpp11-migrate.rst b/docs/cpp11-migrate.rst
new file mode 100644 (file)
index 0000000..0a42961
--- /dev/null
@@ -0,0 +1,4 @@
+:orphan:
+
+All :program:`clang-modernize` transforms have moved to :doc:`clang-tidy/index`
+(see the ``modernize`` module).
diff --git a/docs/doxygen-mainpage.dox b/docs/doxygen-mainpage.dox
new file mode 100644 (file)
index 0000000..a4527b8
--- /dev/null
@@ -0,0 +1,9 @@
+/// \mainpage clang-tools
+///
+/// \section main_intro Introduction
+/// Welcome to clang tools.
+///
+/// This documentation describes the **internal** software that makes
+/// up clang tools, not the **external** use of clang tools. For
+/// usage instructions, please see the programmer's guide or reference
+/// manual.
diff --git a/docs/doxygen.cfg.in b/docs/doxygen.cfg.in
new file mode 100644 (file)
index 0000000..6dbf6db
--- /dev/null
@@ -0,0 +1,2300 @@
+# Doxyfile 1.8.6
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a double hash (##) is considered a comment and is placed in
+# front of the TAG it is preceding.
+#
+# All text after a single hash (#) is considered a comment and will be ignored.
+# The format is:
+# TAG = value [value, ...]
+# For lists, items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (\" \").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all text
+# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv
+# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv
+# for the list of possible encodings.
+# The default value is: UTF-8.
+
+DOXYFILE_ENCODING      = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
+# double-quotes, unless you are using Doxywizard) that should identify the
+# project for which the documentation is generated. This name is used in the
+# title of most generated pages and in a few other places.
+# The default value is: My Project.
+
+PROJECT_NAME           = clang-tools
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
+# could be handy for archiving the generated documentation or if some version
+# control system is used.
+
+PROJECT_NUMBER         = @PACKAGE_VERSION@
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer a
+# quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF          =
+
+# With the PROJECT_LOGO tag one can specify an logo or icon that is included in
+# the documentation. The maximum height of the logo should not exceed 55 pixels
+# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo
+# to the output directory.
+
+PROJECT_LOGO           =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
+# into which the generated documentation will be written. If a relative path is
+# entered, it will be relative to the location where doxygen was started. If
+# left blank the current directory will be used.
+
+OUTPUT_DIRECTORY       = @abs_builddir@/doxygen
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub-
+# directories (in 2 levels) under the output directory of each output format and
+# will distribute the generated files over these directories. Enabling this
+# option can be useful when feeding doxygen a huge amount of source files, where
+# putting all generated files in the same directory would otherwise causes
+# performance problems for the file system.
+# The default value is: NO.
+
+CREATE_SUBDIRS         = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
+# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
+# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
+# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
+# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
+# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
+# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
+# Ukrainian and Vietnamese.
+# The default value is: English.
+
+OUTPUT_LANGUAGE        = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member
+# descriptions after the members that are listed in the file and class
+# documentation (similar to Javadoc). Set to NO to disable this.
+# The default value is: YES.
+
+BRIEF_MEMBER_DESC      = YES
+
+# If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief
+# description of a member or function before the detailed description
+#
+# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+# The default value is: YES.
+
+REPEAT_BRIEF           = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator that is
+# used to form the text in various listings. Each string in this list, if found
+# as the leading text of the brief description, will be stripped from the text
+# and the result, after processing the whole list, is used as the annotated
+# text. Otherwise, the brief description is used as-is. If left blank, the
+# following values are used ($name is automatically replaced with the name of
+# the entity):The $name class, The $name widget, The $name file, is, provides,
+# specifies, contains, represents, a, an and the.
+
+ABBREVIATE_BRIEF       =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# doxygen will generate a detailed section even if there is only a brief
+# description.
+# The default value is: NO.
+
+ALWAYS_DETAILED_SEC    = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+# The default value is: NO.
+
+INLINE_INHERITED_MEMB  = NO
+
+# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path
+# before files name in the file list and in the header files. If set to NO the
+# shortest path that makes the file name unique will be used
+# The default value is: YES.
+
+FULL_PATH_NAMES        = NO
+
+# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
+# Stripping is only done if one of the specified strings matches the left-hand
+# part of the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the path to
+# strip.
+#
+# Note that you can specify absolute paths here, but also relative paths, which
+# will be relative from the directory where doxygen is started.
+# This tag requires that the tag FULL_PATH_NAMES is set to YES.
+
+STRIP_FROM_PATH        = ../..
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
+# path mentioned in the documentation of a class, which tells the reader which
+# header file to include in order to use a class. If left blank only the name of
+# the header file containing the class definition is used. Otherwise one should
+# specify the list of include paths that are normally passed to the compiler
+# using the -I flag.
+
+STRIP_FROM_INC_PATH    =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
+# less readable) file names. This can be useful is your file systems doesn't
+# support long names like on DOS, Mac, or CD-ROM.
+# The default value is: NO.
+
+SHORT_NAMES            = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
+# first line (until the first dot) of a Javadoc-style comment as the brief
+# description. If set to NO, the Javadoc-style will behave just like regular Qt-
+# style comments (thus requiring an explicit @brief command for a brief
+# description.)
+# The default value is: NO.
+
+JAVADOC_AUTOBRIEF      = YES
+
+# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
+# line (until the first dot) of a Qt-style comment as the brief description. If
+# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
+# requiring an explicit \brief command for a brief description.)
+# The default value is: NO.
+
+QT_AUTOBRIEF           = YES
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
+# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
+# a brief description. This used to be the default behavior. The new default is
+# to treat a multi-line C++ comment block as a detailed description. Set this
+# tag to YES if you prefer the old behavior instead.
+#
+# Note that setting this tag to YES also means that rational rose comments are
+# not recognized any more.
+# The default value is: NO.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
+# documentation from any documented member that it re-implements.
+# The default value is: YES.
+
+INHERIT_DOCS           = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a
+# new page for each member. If set to NO, the documentation of a member will be
+# part of the file/class/namespace that contains it.
+# The default value is: NO.
+
+SEPARATE_MEMBER_PAGES  = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
+# uses this value to replace tabs by spaces in code fragments.
+# Minimum value: 1, maximum value: 16, default value: 4.
+
+TAB_SIZE               = 2
+
+# This tag can be used to specify a number of aliases that act as commands in
+# the documentation. An alias has the form:
+# name=value
+# For example adding
+# "sideeffect=@par Side Effects:\n"
+# will allow you to put the command \sideeffect (or @sideeffect) in the
+# documentation, which will result in a user-defined paragraph with heading
+# "Side Effects:". You can put \n's in the value part of an alias to insert
+# newlines.
+
+ALIASES                =
+
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding "class=itcl::class"
+# will allow you to use the command class in the itcl::class meaning.
+
+TCL_SUBST              =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C. For
+# instance, some of the names that are used will be different. The list of all
+# members will be omitted, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_FOR_C  = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
+# Python sources only. Doxygen will then generate output that is more tailored
+# for that language. For instance, namespaces will be presented as packages,
+# qualified scopes will look different, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_JAVA   = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources. Doxygen will then generate output that is tailored for Fortran.
+# The default value is: NO.
+
+OPTIMIZE_FOR_FORTRAN   = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for VHDL.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_VHDL   = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension, and
+# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
+# C#, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL. For instance to make
+# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
+# (default is Fortran), use: inc=Fortran f=C.
+#
+# Note For files without extension you can use no_extension as a placeholder.
+#
+# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
+# the files are not read by doxygen.
+
+EXTENSION_MAPPING      =
+
+# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
+# according to the Markdown format, which allows for more readable
+# documentation. See http://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you can
+# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
+# case of backward compatibilities issues.
+# The default value is: YES.
+
+MARKDOWN_SUPPORT       = YES
+
+# When enabled doxygen tries to link words that correspond to documented
+# classes, or namespaces to their corresponding documentation. Such a link can
+# be prevented in individual cases by by putting a % sign in front of the word
+# or globally by setting AUTOLINK_SUPPORT to NO.
+# The default value is: YES.
+
+AUTOLINK_SUPPORT       = YES
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should set this
+# tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string);
+# versus func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+# The default value is: NO.
+
+BUILTIN_STL_SUPPORT    = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+# The default value is: NO.
+
+CPP_CLI_SUPPORT        = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
+# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen
+# will parse them like normal C++ but will assume all classes use public instead
+# of private inheritance when no explicit protection keyword is present.
+# The default value is: NO.
+
+SIP_SUPPORT            = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES will make
+# doxygen to replace the get and set methods by a property in the documentation.
+# This will only work if the methods are indeed getting or setting a simple
+# type. If this is not the case, or you want to show the methods anyway, you
+# should set this option to NO.
+# The default value is: YES.
+
+IDL_PROPERTY_SUPPORT   = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+# The default value is: NO.
+
+DISTRIBUTE_GROUP_DOC   = NO
+
+# Set the SUBGROUPING tag to YES to allow class member groups of the same type
+# (for instance a group of public functions) to be put as a subgroup of that
+# type (e.g. under the Public Functions section). Set it to NO to prevent
+# subgrouping. Alternatively, this can be done per class using the
+# \nosubgrouping command.
+# The default value is: YES.
+
+SUBGROUPING            = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
+# are shown inside the group in which they are included (e.g. using \ingroup)
+# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
+# and RTF).
+#
+# Note that this feature does not work in combination with
+# SEPARATE_MEMBER_PAGES.
+# The default value is: NO.
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
+# with only public data fields or simple typedef fields will be shown inline in
+# the documentation of the scope in which they are defined (i.e. file,
+# namespace, or group documentation), provided this scope is documented. If set
+# to NO, structs, classes, and unions are shown on a separate page (for HTML and
+# Man pages) or section (for LaTeX and RTF).
+# The default value is: NO.
+
+INLINE_SIMPLE_STRUCTS  = NO
+
+# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
+# enum is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically be
+# useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+# The default value is: NO.
+
+TYPEDEF_HIDES_STRUCT   = NO
+
+# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
+# cache is used to resolve symbols given their name and scope. Since this can be
+# an expensive process and often the same symbol appears multiple times in the
+# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
+# doxygen will become slower. If the cache is too large, memory is wasted. The
+# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
+# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
+# symbols. At the end of a run doxygen will report the cache usage and suggest
+# the optimal cache size from a speed point of view.
+# Minimum value: 0, maximum value: 9, default value: 0.
+
+LOOKUP_CACHE_SIZE      = 2
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available. Private
+# class members and static file members will be hidden unless the
+# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
+# Note: This will also disable the warnings about undocumented members that are
+# normally produced when WARNINGS is set to YES.
+# The default value is: NO.
+
+EXTRACT_ALL            = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will
+# be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PRIVATE        = NO
+
+# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal
+# scope will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PACKAGE        = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file will be
+# included in the documentation.
+# The default value is: NO.
+
+EXTRACT_STATIC         = YES
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined
+# locally in source files will be included in the documentation. If set to NO
+# only classes defined in header files are included. Does not have any effect
+# for Java sources.
+# The default value is: YES.
+
+EXTRACT_LOCAL_CLASSES  = YES
+
+# This flag is only useful for Objective-C code. When set to YES local methods,
+# which are defined in the implementation section but not in the interface are
+# included in the documentation. If set to NO only methods in the interface are
+# included.
+# The default value is: NO.
+
+EXTRACT_LOCAL_METHODS  = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base name of
+# the file that contains the anonymous namespace. By default anonymous namespace
+# are hidden.
+# The default value is: NO.
+
+EXTRACT_ANON_NSPACES   = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
+# undocumented members inside documented classes or files. If set to NO these
+# members will be included in the various overviews, but no documentation
+# section is generated. This option has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_MEMBERS     = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy. If set
+# to NO these classes will be included in the various overviews. This option has
+# no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_CLASSES     = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
+# (class|struct|union) declarations. If set to NO these declarations will be
+# included in the documentation.
+# The default value is: NO.
+
+HIDE_FRIEND_COMPOUNDS  = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
+# documentation blocks found inside the body of a function. If set to NO these
+# blocks will be appended to the function's detailed documentation block.
+# The default value is: NO.
+
+HIDE_IN_BODY_DOCS      = NO
+
+# The INTERNAL_DOCS tag determines if documentation that is typed after a
+# \internal command is included. If the tag is set to NO then the documentation
+# will be excluded. Set it to YES to include the internal documentation.
+# The default value is: NO.
+
+INTERNAL_DOCS          = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
+# names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+# The default value is: system dependent.
+
+CASE_SENSE_NAMES       = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
+# their full class and namespace scopes in the documentation. If set to YES the
+# scope will be hidden.
+# The default value is: NO.
+
+HIDE_SCOPE_NAMES       = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
+# the files that are included by a file in the documentation of that file.
+# The default value is: YES.
+
+SHOW_INCLUDE_FILES     = YES
+
+# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
+# grouped member an include statement to the documentation, telling the reader
+# which file to include in order to use the member.
+# The default value is: NO.
+
+SHOW_GROUPED_MEMB_INC  = NO
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
+# files with double quotes in the documentation rather than with sharp brackets.
+# The default value is: NO.
+
+FORCE_LOCAL_INCLUDES   = NO
+
+# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
+# documentation for inline members.
+# The default value is: YES.
+
+INLINE_INFO            = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
+# (detailed) documentation of file and class members alphabetically by member
+# name. If set to NO the members will appear in declaration order.
+# The default value is: YES.
+
+SORT_MEMBER_DOCS       = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
+# descriptions of file, namespace and class members alphabetically by member
+# name. If set to NO the members will appear in declaration order. Note that
+# this will also influence the order of the classes in the class list.
+# The default value is: NO.
+
+SORT_BRIEF_DOCS        = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
+# (brief and detailed) documentation of class members so that constructors and
+# destructors are listed first. If set to NO the constructors will appear in the
+# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
+# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
+# member documentation.
+# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
+# detailed member documentation.
+# The default value is: NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
+# of group names into alphabetical order. If set to NO the group names will
+# appear in their defined order.
+# The default value is: NO.
+
+SORT_GROUP_NAMES       = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
+# fully-qualified names, including namespaces. If set to NO, the class list will
+# be sorted only by class name, not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the alphabetical
+# list.
+# The default value is: NO.
+
+SORT_BY_SCOPE_NAME     = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
+# type resolution of all parameters of a function it will reject a match between
+# the prototype and the implementation of a member function even if there is
+# only one candidate or it is obvious which candidate to choose by doing a
+# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
+# accept a match between prototype and implementation in such cases.
+# The default value is: NO.
+
+STRICT_PROTO_MATCHING  = NO
+
+# The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the
+# todo list. This list is created by putting \todo commands in the
+# documentation.
+# The default value is: YES.
+
+GENERATE_TODOLIST      = YES
+
+# The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the
+# test list. This list is created by putting \test commands in the
+# documentation.
+# The default value is: YES.
+
+GENERATE_TESTLIST      = YES
+
+# The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug
+# list. This list is created by putting \bug commands in the documentation.
+# The default value is: YES.
+
+GENERATE_BUGLIST       = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO)
+# the deprecated list. This list is created by putting \deprecated commands in
+# the documentation.
+# The default value is: YES.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional documentation
+# sections, marked by \if <section_label> ... \endif and \cond <section_label>
+# ... \endcond blocks.
+
+ENABLED_SECTIONS       =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
+# initial value of a variable or macro / define can have for it to appear in the
+# documentation. If the initializer consists of more lines than specified here
+# it will be hidden. Use a value of 0 to hide initializers completely. The
+# appearance of the value of individual variables and macros / defines can be
+# controlled using \showinitializer or \hideinitializer command in the
+# documentation regardless of this setting.
+# Minimum value: 0, maximum value: 10000, default value: 30.
+
+MAX_INITIALIZER_LINES  = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
+# the bottom of the documentation of classes and structs. If set to YES the list
+# will mention the files that were used to generate the documentation.
+# The default value is: YES.
+
+SHOW_USED_FILES        = YES
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
+# will remove the Files entry from the Quick Index and from the Folder Tree View
+# (if specified).
+# The default value is: YES.
+
+SHOW_FILES             = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
+# page. This will remove the Namespaces entry from the Quick Index and from the
+# Folder Tree View (if specified).
+# The default value is: YES.
+
+SHOW_NAMESPACES        = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command command input-file, where command is the value of the
+# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
+# by doxygen. Whatever the program writes to standard output is used as the file
+# version. For an example see the documentation.
+
+FILE_VERSION_FILTER    =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option. You can
+# optionally specify a file name after the option, if omitted DoxygenLayout.xml
+# will be used as the name of the layout file.
+#
+# Note that if you run doxygen from a directory containing a file called
+# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
+# tag is left empty.
+
+LAYOUT_FILE            =
+
+# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
+# the reference definitions. This must be a list of .bib files. The .bib
+# extension is automatically appended if omitted. This requires the bibtex tool
+# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.
+# For LaTeX the style of the bibliography can be controlled using
+# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
+# search path. Do not use file names with spaces, bibtex cannot handle them. See
+# also \cite for info how to create references.
+
+CITE_BIB_FILES         =
+
+#---------------------------------------------------------------------------
+# Configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated to
+# standard output by doxygen. If QUIET is set to YES this implies that the
+# messages are off.
+# The default value is: NO.
+
+QUIET                  = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES
+# this implies that the warnings are on.
+#
+# Tip: Turn warnings on while writing the documentation.
+# The default value is: YES.
+
+WARNINGS               = NO
+
+# If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate
+# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
+# will automatically be disabled.
+# The default value is: YES.
+
+WARN_IF_UNDOCUMENTED   = NO
+
+# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some parameters
+# in a documented function, or documenting parameters that don't exist or using
+# markup commands wrongly.
+# The default value is: YES.
+
+WARN_IF_DOC_ERROR      = YES
+
+# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
+# are documented, but have no documentation for their parameters or return
+# value. If set to NO doxygen will only warn about wrong or incomplete parameter
+# documentation, but not about the absence of documentation.
+# The default value is: NO.
+
+WARN_NO_PARAMDOC       = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that doxygen
+# can produce. The string should contain the $file, $line, and $text tags, which
+# will be replaced by the file and line number from which the warning originated
+# and the warning text. Optionally the format may contain $version, which will
+# be replaced by the version of the file (if it could be obtained via
+# FILE_VERSION_FILTER)
+# The default value is: $file:$line: $text.
+
+WARN_FORMAT            = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning and error
+# messages should be written. If left blank the output is written to standard
+# error (stderr).
+
+WARN_LOGFILE           =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag is used to specify the files and/or directories that contain
+# documented source files. You may enter file names like myfile.cpp or
+# directories like /usr/src/myproject. Separate the files or directories with
+# spaces.
+# Note: If this tag is empty the current directory is searched.
+
+INPUT                  = \
+                         @abs_srcdir@/../clang-tidy \
+                         @abs_srcdir@/../clang-apply-replacements \
+                         @abs_srcdir@/../clang-query \
+                         @abs_srcdir@/../clang-rename \
+                         @abs_srcdir@/../modularize \
+                         @abs_srcdir@/../pp-trace \
+                         @abs_srcdir@/../tool-template \
+                         @abs_srcdir@/doxygen-mainpage.dox
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
+# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
+# documentation (see: http://www.gnu.org/software/libiconv) for the list of
+# possible encodings.
+# The default value is: UTF-8.
+
+INPUT_ENCODING         = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank the
+# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii,
+# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp,
+# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown,
+# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf,
+# *.qsf, *.as and *.js.
+
+FILE_PATTERNS          =
+
+# The RECURSIVE tag can be used to specify whether or not subdirectories should
+# be searched for input files as well.
+# The default value is: NO.
+
+RECURSIVE              = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+#
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
+
+EXCLUDE                =
+
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+# The default value is: NO.
+
+EXCLUDE_SYMLINKS       = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories.
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories for example use the pattern */test/*
+
+EXCLUDE_PATTERNS       =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories use the pattern */test/*
+
+EXCLUDE_SYMBOLS        =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or directories
+# that contain example code fragments that are included (see the \include
+# command).
+
+EXAMPLE_PATH           =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank all
+# files are included.
+
+EXAMPLE_PATTERNS       =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude commands
+# irrespective of the value of the RECURSIVE tag.
+# The default value is: NO.
+
+EXAMPLE_RECURSIVE      = YES
+
+# The IMAGE_PATH tag can be used to specify one or more files or directories
+# that contain images that are to be included in the documentation (see the
+# \image command).
+
+IMAGE_PATH             =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command:
+#
+# <filter> <input-file>
+#
+# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the
+# name of an input file. Doxygen will then use the output that the filter
+# program writes to standard output. If FILTER_PATTERNS is specified, this tag
+# will be ignored.
+#
+# Note that the filter must not add or remove lines; it is applied before the
+# code is scanned, but not when the output code is generated. If lines are added
+# or removed, the anchors will not be placed correctly.
+
+INPUT_FILTER           =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form: pattern=filter
+# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
+# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
+# patterns match the file name, INPUT_FILTER is applied.
+
+FILTER_PATTERNS        =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER ) will also be used to filter the input files that are used for
+# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# The default value is: NO.
+
+FILTER_SOURCE_FILES    = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
+# it is also possible to disable source filtering for a specific pattern using
+# *.ext= (so without naming a filter).
+# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
+
+FILTER_SOURCE_PATTERNS =
+
+# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
+# is part of the input, its contents will be placed on the main page
+# (index.html). This can be useful if you have a project on for instance GitHub
+# and want to reuse the introduction page also for the doxygen output.
+
+USE_MDFILE_AS_MAINPAGE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
+# generated. Documented entities will be cross-referenced with these sources.
+#
+# Note: To get rid of all source code in the generated output, make sure that
+# also VERBATIM_HEADERS is set to NO.
+# The default value is: NO.
+
+SOURCE_BROWSER         = YES
+
+# Setting the INLINE_SOURCES tag to YES will include the body of functions,
+# classes and enums directly into the documentation.
+# The default value is: NO.
+
+INLINE_SOURCES         = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
+# special comment blocks from generated source code fragments. Normal C, C++ and
+# Fortran comments will always remain visible.
+# The default value is: YES.
+
+STRIP_CODE_COMMENTS    = NO
+
+# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
+# function all documented functions referencing it will be listed.
+# The default value is: NO.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES then for each documented function
+# all documented entities called/used by that function will be listed.
+# The default value is: NO.
+
+REFERENCES_RELATION    = YES
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
+# to YES, then the hyperlinks from functions in REFERENCES_RELATION and
+# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
+# link to the documentation.
+# The default value is: YES.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
+# source code will show a tooltip with additional information such as prototype,
+# brief description and links to the definition and documentation. Since this
+# will make the HTML file larger and loading of large files a bit slower, you
+# can opt to disable this feature.
+# The default value is: YES.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+SOURCE_TOOLTIPS        = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code will
+# point to the HTML generated by the htags(1) tool instead of doxygen built-in
+# source browser. The htags tool is part of GNU's global source tagging system
+# (see http://www.gnu.org/software/global/global.html). You will need version
+# 4.8.6 or higher.
+#
+# To use it do the following:
+# - Install the latest version of global
+# - Enable SOURCE_BROWSER and USE_HTAGS in the config file
+# - Make sure the INPUT points to the root of the source tree
+# - Run doxygen as normal
+#
+# Doxygen will invoke htags (and that will in turn invoke gtags), so these
+# tools must be available from the command line (i.e. in the search path).
+#
+# The result: instead of the source browser generated by doxygen, the links to
+# source code will now point to the output of htags.
+# The default value is: NO.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+USE_HTAGS              = NO
+
+# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
+# verbatim copy of the header file for each class for which an include is
+# specified. Set to NO to disable this.
+# See also: Section \class.
+# The default value is: YES.
+
+VERBATIM_HEADERS       = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
+# compounds will be generated. Enable this if the project contains a lot of
+# classes, structs, unions or interfaces.
+# The default value is: YES.
+
+ALPHABETICAL_INDEX     = YES
+
+# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
+# which the alphabetical index list will be split.
+# Minimum value: 1, maximum value: 20, default value: 5.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+COLS_IN_ALPHA_INDEX    = 4
+
+# In case all classes in a project start with a common prefix, all classes will
+# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
+# can be used to specify a prefix (or a list of prefixes) that should be ignored
+# while generating the index headers.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+IGNORE_PREFIX          = clang::
+
+#---------------------------------------------------------------------------
+# Configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES doxygen will generate HTML output
+# The default value is: YES.
+
+GENERATE_HTML          = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_OUTPUT            = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
+# generated HTML page (for example: .htm, .php, .asp).
+# The default value is: .html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FILE_EXTENSION    = .html
+
+# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
+# each generated HTML page. If the tag is left blank doxygen will generate a
+# standard header.
+#
+# To get valid HTML the header file that includes any scripts and style sheets
+# that doxygen needs, which is dependent on the configuration options used (e.g.
+# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
+# default header using
+# doxygen -w html new_header.html new_footer.html new_stylesheet.css
+# YourConfigFile
+# and then modify the file new_header.html. See also section "Doxygen usage"
+# for information on how to generate the default header that doxygen normally
+# uses.
+# Note: The header is subject to change so you typically have to regenerate the
+# default header when upgrading to a newer version of doxygen. For a description
+# of the possible markers and block names see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_HEADER            =
+
+# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
+# generated HTML page. If the tag is left blank doxygen will generate a standard
+# footer. See HTML_HEADER for more information on how to generate a default
+# footer and what special commands can be used inside the footer. See also
+# section "Doxygen usage" for information on how to generate the default footer
+# that doxygen normally uses.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FOOTER            =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
+# sheet that is used by each HTML page. It can be used to fine-tune the look of
+# the HTML output. If left blank doxygen will generate a default style sheet.
+# See also section "Doxygen usage" for information on how to generate the style
+# sheet that doxygen normally uses.
+# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
+# it is more robust and this tag (HTML_STYLESHEET) will in the future become
+# obsolete.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_STYLESHEET        =
+
+# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user-
+# defined cascading style sheet that is included after the standard style sheets
+# created by doxygen. Using this option one can overrule certain style aspects.
+# This is preferred over using HTML_STYLESHEET since it does not replace the
+# standard style sheet and is therefor more robust against future updates.
+# Doxygen will copy the style sheet file to the output directory. For an example
+# see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_STYLESHEET  =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
+# files will be copied as-is; there are no commands or markers available.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_FILES       =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
+# will adjust the colors in the stylesheet and background images according to
+# this color. Hue is specified as an angle on a colorwheel, see
+# http://en.wikipedia.org/wiki/Hue for more information. For instance the value
+# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
+# purple, and 360 is red again.
+# Minimum value: 0, maximum value: 359, default value: 220.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_HUE    = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
+# in the HTML output. For a value of 0 the output will use grayscales only. A
+# value of 255 will produce the most vivid colors.
+# Minimum value: 0, maximum value: 255, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_SAT    = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
+# luminance component of the colors in the HTML output. Values below 100
+# gradually make the output lighter, whereas values above 100 make the output
+# darker. The value divided by 100 is the actual gamma applied, so 80 represents
+# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
+# change the gamma.
+# Minimum value: 40, maximum value: 240, default value: 80.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_GAMMA  = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting this
+# to NO can help when comparing the output of multiple runs.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_TIMESTAMP         = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_SECTIONS  = NO
+
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
+# shown in the various tree structured indices initially; the user can expand
+# and collapse entries dynamically later on. Doxygen will expand the tree to
+# such a level that at most the specified number of entries are visible (unless
+# a fully collapsed tree already exceeds this amount). So setting the number of
+# entries 1 will produce a full collapsed tree by default. 0 is a special value
+# representing an infinite number of entries and will result in a full expanded
+# tree by default.
+# Minimum value: 0, maximum value: 9999, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files will be
+# generated that can be used as input for Apple's Xcode 3 integrated development
+# environment (see: http://developer.apple.com/tools/xcode/), introduced with
+# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a
+# Makefile in the HTML output directory. Running make will produce the docset in
+# that directory and running make install will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
+# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_DOCSET        = NO
+
+# This tag determines the name of the docset feed. A documentation feed provides
+# an umbrella under which multiple documentation sets from a single provider
+# (such as a company or product suite) can be grouped.
+# The default value is: Doxygen generated docs.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_FEEDNAME        = "Doxygen generated docs"
+
+# This tag specifies a string that should uniquely identify the documentation
+# set bundle. This should be a reverse domain-name style string, e.g.
+# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_BUNDLE_ID       = org.doxygen.Project
+
+# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+# The default value is: org.doxygen.Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_ID    = org.doxygen.Publisher
+
+# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
+# The default value is: Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_NAME  = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
+# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
+# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
+# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on
+# Windows.
+#
+# The HTML Help Workshop contains a compiler that can convert all HTML output
+# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
+# files are now used as the Windows 98 help format, and will replace the old
+# Windows help format (.hlp) on all Windows platforms in the future. Compressed
+# HTML files also contain an index, a table of contents, and you can search for
+# words in the documentation. The HTML workshop also contains a viewer for
+# compressed HTML files.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_HTMLHELP      = NO
+
+# The CHM_FILE tag can be used to specify the file name of the resulting .chm
+# file. You can add a path in front of the file if the result should not be
+# written to the html output directory.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_FILE               =
+
+# The HHC_LOCATION tag can be used to specify the location (absolute path
+# including file name) of the HTML help compiler ( hhc.exe). If non-empty
+# doxygen will try to run the HTML help compiler on the generated index.hhp.
+# The file has to be specified with full path.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+HHC_LOCATION           =
+
+# The GENERATE_CHI flag controls if a separate .chi index file is generated (
+# YES) or that it should be included in the master .chm file ( NO).
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+GENERATE_CHI           = NO
+
+# The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc)
+# and project file content.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_INDEX_ENCODING     =
+
+# The BINARY_TOC flag controls whether a binary table of contents is generated (
+# YES) or a normal table of contents ( NO) in the .chm file.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+BINARY_TOC             = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members to
+# the table of contents of the HTML help documentation and to the tree view.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+TOC_EXPAND             = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
+# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
+# (.qch) of the generated HTML documentation.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_QHP           = @clang_tools_doxygen_generate_qhp@
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
+# the file name of the resulting .qch file. The path specified is relative to
+# the HTML output folder.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QCH_FILE               = @clang_tools_doxygen_qch_filename@
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
+# Project output. For more information please see Qt Help Project / Namespace
+# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace).
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_NAMESPACE          = @clang_tools_doxygen_qhp_namespace@
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
+# Help Project output. For more information please see Qt Help Project / Virtual
+# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual-
+# folders).
+# The default value is: doc.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_VIRTUAL_FOLDER     = doc
+
+# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
+# filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_NAME   = @clang_tools_doxygen_qhp_cust_filter_name@
+
+# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_ATTRS  = @clang_tools_doxygen_qhp_cust_filter_attrs@
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's filter section matches. Qt Help Project / Filter Attributes (see:
+# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_SECT_FILTER_ATTRS  =
+
+# The QHG_LOCATION tag can be used to specify the location of Qt's
+# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
+# generated .qhp file.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHG_LOCATION           = @clang_tools_doxygen_qhelpgenerator_path@
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
+# generated, together with the HTML files, they form an Eclipse help plugin. To
+# install this plugin and make it available under the help contents menu in
+# Eclipse, the contents of the directory containing the HTML and XML files needs
+# to be copied into the plugins directory of eclipse. The name of the directory
+# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
+# After copying Eclipse needs to be restarted before the help appears.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_ECLIPSEHELP   = NO
+
+# A unique identifier for the Eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have this
+# name. Each documentation set should have its own identifier.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
+
+ECLIPSE_DOC_ID         = org.doxygen.Project
+
+# If you want full control over the layout of the generated HTML pages it might
+# be necessary to disable the index and replace it with your own. The
+# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
+# of each HTML page. A value of NO enables the index and the value YES disables
+# it. Since the tabs in the index contain the same information as the navigation
+# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+DISABLE_INDEX          = NO
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information. If the tag
+# value is set to YES, a side panel will be generated containing a tree-like
+# index structure (just like the one that is generated for HTML Help). For this
+# to work a browser that supports JavaScript, DHTML, CSS and frames is required
+# (i.e. any modern browser). Windows users are probably better off using the
+# HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can
+# further fine-tune the look of the index. As an example, the default style
+# sheet generated by doxygen has an example that shows how to put an image at
+# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
+# the same information as the tab index, you could consider setting
+# DISABLE_INDEX to YES when enabling this option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_TREEVIEW      = NO
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
+# doxygen will group on one line in the generated HTML documentation.
+#
+# Note that a value of 0 will completely suppress the enum values from appearing
+# in the overview section.
+# Minimum value: 0, maximum value: 20, default value: 4.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+ENUM_VALUES_PER_LINE   = 4
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
+# to set the initial width (in pixels) of the frame in which the tree is shown.
+# Minimum value: 0, maximum value: 1500, default value: 250.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+TREEVIEW_WIDTH         = 250
+
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to
+# external symbols imported via tag files in a separate window.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+EXT_LINKS_IN_WINDOW    = NO
+
+# Use this tag to change the font size of LaTeX formulas included as images in
+# the HTML documentation. When you change the font size after a successful
+# doxygen run you need to manually remove any form_*.png images from the HTML
+# output directory to force them to be regenerated.
+# Minimum value: 8, maximum value: 50, default value: 10.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_FONTSIZE       = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are not
+# supported properly for IE 6.0, but are supported on all modern browsers.
+#
+# Note that when changing this option you need to delete any form_*.png files in
+# the HTML output directory before the changes have effect.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_TRANSPARENT    = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
+# http://www.mathjax.org) which uses client side Javascript for the rendering
+# instead of using prerendered bitmaps. Use this if you do not have LaTeX
+# installed or if you want to formulas look prettier in the HTML output. When
+# enabled you may also need to install MathJax separately and configure the path
+# to it using the MATHJAX_RELPATH option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+USE_MATHJAX            = NO
+
+# When MathJax is enabled you can set the default output format to be used for
+# the MathJax output. See the MathJax site (see:
+# http://docs.mathjax.org/en/latest/output.html) for more details.
+# Possible values are: HTML-CSS (which is slower, but has the best
+# compatibility), NativeMML (i.e. MathML) and SVG.
+# The default value is: HTML-CSS.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_FORMAT         = HTML-CSS
+
+# When MathJax is enabled you need to specify the location relative to the HTML
+# output directory using the MATHJAX_RELPATH option. The destination directory
+# should contain the MathJax.js script. For instance, if the mathjax directory
+# is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
+# Content Delivery Network so you can quickly see the result without installing
+# MathJax. However, it is strongly recommended to install a local copy of
+# MathJax from http://www.mathjax.org before deployment.
+# The default value is: http://cdn.mathjax.org/mathjax/latest.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_RELPATH        = http://cdn.mathjax.org/mathjax/latest
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
+# extension names that should be enabled during MathJax rendering. For example
+# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_EXTENSIONS     =
+
+# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
+# of code that will be used on startup of the MathJax code. See the MathJax site
+# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
+# example see the documentation.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_CODEFILE       =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
+# the HTML output. The underlying search engine uses javascript and DHTML and
+# should work on any modern browser. Note that when using HTML help
+# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
+# there is already a search function so this one should typically be disabled.
+# For large projects the javascript based search engine can be slow, then
+# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
+# search using the keyboard; to jump to the search box use <access key> + S
+# (what the <access key> is depends on the OS and browser, but it is typically
+# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down
+# key> to jump into the search results window, the results can be navigated
+# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel
+# the search. The filter options can be selected when the cursor is inside the
+# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>
+# to select a filter and <Enter> or <escape> to activate or cancel the filter
+# option.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+SEARCHENGINE           = @enable_searchengine@
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a web server instead of a web client using Javascript. There
+# are two flavours of web server based searching depending on the
+# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for
+# searching and an index file used by the script. When EXTERNAL_SEARCH is
+# enabled the indexing and searching needs to be provided by external tools. See
+# the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SERVER_BASED_SEARCH    = @enable_server_based_search@
+
+# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
+# search results.
+#
+# Doxygen ships with an example indexer ( doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/).
+#
+# See the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH        = @enable_external_search@
+
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will return the search results when EXTERNAL_SEARCH is enabled.
+#
+# Doxygen ships with an example indexer ( doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/). See the section "External Indexing and
+# Searching" for details.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHENGINE_URL       = @searchengine_url@
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
+# SEARCHDATA_FILE tag the name of this file can be specified.
+# The default file is: searchdata.xml.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHDATA_FILE        = searchdata.xml
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
+# projects and redirect the results back to the right project.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH_ID     = clang-tools
+
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
+# projects other than the one defined by this configuration file, but that are
+# all added to the same external search index. Each project needs to have a
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
+# to a relative location where the documentation can be found. The format is:
+# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTRA_SEARCH_MAPPINGS  = @extra_search_mappings@
+
+#---------------------------------------------------------------------------
+# Configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES doxygen will generate LaTeX output.
+# The default value is: YES.
+
+GENERATE_LATEX         = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_OUTPUT           = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked.
+#
+# Note that when enabling USE_PDFLATEX this option is only used for generating
+# bitmaps for formulas in the HTML output, but not in the Makefile that is
+# written to the output directory.
+# The default file is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_CMD_NAME         = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
+# index for LaTeX.
+# The default file is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# If the COMPACT_LATEX tag is set to YES doxygen generates more compact LaTeX
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+COMPACT_LATEX          = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used by the
+# printer.
+# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
+# 14 inches) and executive (7.25 x 10.5 inches).
+# The default value is: a4.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PAPER_TYPE             = a4
+
+# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
+# that should be included in the LaTeX output. To get the times font for
+# instance you can specify
+# EXTRA_PACKAGES=times
+# If left blank no extra packages will be included.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+EXTRA_PACKAGES         =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
+# generated LaTeX document. The header should contain everything until the first
+# chapter. If it is left blank doxygen will generate a standard header. See
+# section "Doxygen usage" for information on how to let doxygen write the
+# default header to a separate file.
+#
+# Note: Only use a user-defined header if you know what you are doing! The
+# following commands have a special meaning inside the header: $title,
+# $datetime, $date, $doxygenversion, $projectname, $projectnumber. Doxygen will
+# replace them by respectively the title of the page, the current date and time,
+# only the current date, the version number of doxygen, the project name (see
+# PROJECT_NAME), or the project number (see PROJECT_NUMBER).
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HEADER           =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
+# generated LaTeX document. The footer should contain everything after the last
+# chapter. If it is left blank doxygen will generate a standard footer.
+#
+# Note: Only use a user-defined footer if you know what you are doing!
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_FOOTER           =
+
+# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the LATEX_OUTPUT output
+# directory. Note that the files will be copied as-is; there are no commands or
+# markers available.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_FILES      =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
+# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
+# contain links (just like the HTML output) instead of page references. This
+# makes the output suitable for online browsing using a PDF viewer.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PDF_HYPERLINKS         = YES
+
+# If the LATEX_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
+# the PDF file directly from the LaTeX files. Set this option to YES to get a
+# higher quality PDF documentation.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+USE_PDFLATEX           = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
+# command to the generated LaTeX files. This will instruct LaTeX to keep running
+# if errors occur, instead of asking the user for help. This option is also used
+# when generating formulas in HTML.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BATCHMODE        = NO
+
+# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
+# index chapters (such as File Index, Compound Index, etc.) in the output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HIDE_INDICES     = NO
+
+# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
+# code with syntax highlighting in the LaTeX output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_SOURCE_CODE      = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. See
+# http://en.wikipedia.org/wiki/BibTeX and \cite for more info.
+# The default value is: plain.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BIB_STYLE        = plain
+
+#---------------------------------------------------------------------------
+# Configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES doxygen will generate RTF output. The
+# RTF output is optimized for Word 97 and may not look too pretty with other RTF
+# readers/editors.
+# The default value is: NO.
+
+GENERATE_RTF           = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: rtf.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_OUTPUT             = rtf
+
+# If the COMPACT_RTF tag is set to YES doxygen generates more compact RTF
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+COMPACT_RTF            = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
+# contain hyperlink fields. The RTF file will contain links (just like the HTML
+# output) instead of page references. This makes the output suitable for online
+# browsing using Word or some other Word compatible readers that support those
+# fields.
+#
+# Note: WordPad (write) and others do not support links.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_HYPERLINKS         = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's config
+# file, i.e. a series of assignments. You only have to provide replacements,
+# missing definitions are set to their default value.
+#
+# See also section "Doxygen usage" for information on how to generate the
+# default style sheet that doxygen normally uses.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_STYLESHEET_FILE    =
+
+# Set optional variables used in the generation of an RTF document. Syntax is
+# similar to doxygen's config file. A template extensions file can be generated
+# using doxygen -e rtf extensionFile.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_EXTENSIONS_FILE    =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES doxygen will generate man pages for
+# classes and files.
+# The default value is: NO.
+
+GENERATE_MAN           = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it. A directory man3 will be created inside the directory specified by
+# MAN_OUTPUT.
+# The default directory is: man.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_OUTPUT             = man
+
+# The MAN_EXTENSION tag determines the extension that is added to the generated
+# man pages. In case the manual section does not start with a number, the number
+# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
+# optional.
+# The default value is: .3.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_EXTENSION          = .3
+
+# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
+# will generate one additional man file for each entity documented in the real
+# man page(s). These additional files only source the real man page, but without
+# them the man command would be unable to find the correct page.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_LINKS              = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES doxygen will generate an XML file that
+# captures the structure of the code including all documentation.
+# The default value is: NO.
+
+GENERATE_XML           = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: xml.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_OUTPUT             = xml
+
+# If the XML_PROGRAMLISTING tag is set to YES doxygen will dump the program
+# listings (including syntax highlighting and cross-referencing information) to
+# the XML output. Note that enabling this will significantly increase the size
+# of the XML output.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_PROGRAMLISTING     = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the DOCBOOK output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_DOCBOOK tag is set to YES doxygen will generate Docbook files
+# that can be used to generate PDF.
+# The default value is: NO.
+
+GENERATE_DOCBOOK       = NO
+
+# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
+# front of it.
+# The default directory is: docbook.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_OUTPUT         = docbook
+
+#---------------------------------------------------------------------------
+# Configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES doxygen will generate an AutoGen
+# Definitions (see http://autogen.sf.net) file that captures the structure of
+# the code including all documentation. Note that this feature is still
+# experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES doxygen will generate a Perl module
+# file that captures the structure of the code including all documentation.
+#
+# Note that this feature is still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_PERLMOD       = NO
+
+# If the PERLMOD_LATEX tag is set to YES doxygen will generate the necessary
+# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
+# output from the Perl module output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_LATEX          = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be nicely
+# formatted so it can be parsed by a human reader. This is useful if you want to
+# understand what is going on. On the other hand, if this tag is set to NO the
+# size of the Perl module output will be much smaller and Perl will parse it
+# just the same.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_PRETTY         = YES
+
+# The names of the make variables in the generated doxyrules.make file are
+# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
+# so different doxyrules.make files included by the same Makefile don't
+# overwrite each other's variables.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES doxygen will evaluate all
+# C-preprocessor directives found in the sources and include files.
+# The default value is: YES.
+
+ENABLE_PREPROCESSING   = YES
+
+# If the MACRO_EXPANSION tag is set to YES doxygen will expand all macro names
+# in the source code. If set to NO only conditional compilation will be
+# performed. Macro expansion can be done in a controlled way by setting
+# EXPAND_ONLY_PREDEF to YES.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+MACRO_EXPANSION        = YES
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
+# the macro expansion is limited to the macros specified with the PREDEFINED and
+# EXPAND_AS_DEFINED tags.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_ONLY_PREDEF     = YES
+
+# If the SEARCH_INCLUDES tag is set to YES the includes files in the
+# INCLUDE_PATH will be searched if a #include is found.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SEARCH_INCLUDES        = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by the
+# preprocessor.
+# This tag requires that the tag SEARCH_INCLUDES is set to YES.
+
+INCLUDE_PATH           = @abs_srcdir@/../../../include \
+                         @abs_srcdir@/../../../../../include
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will be
+# used.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+INCLUDE_FILE_PATTERNS  =
+
+# The PREDEFINED tag can be used to specify one or more macro names that are
+# defined before the preprocessor is started (similar to the -D option of e.g.
+# gcc). The argument of the tag is a list of macros of the form: name or
+# name=definition (no spaces). If the definition and the "=" are omitted, "=1"
+# is assumed. To prevent a macro definition from being undefined via #undef or
+# recursively expanded use the := operator instead of the = operator.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+PREDEFINED             = LLVM_ALIGNAS(x)=
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
+# tag can be used to specify a list of macro names that should be expanded. The
+# macro definition that is found in the sources will be used. Use the PREDEFINED
+# tag if you want to use a different macro definition that overrules the
+# definition found in the source code.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_AS_DEFINED      =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
+# remove all references to function-like macros that are alone on a line, have an
+# all uppercase name, and do not end with a semicolon. Such function macros are
+# typically used for boiler-plate code, and will confuse the parser if not
+# removed.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SKIP_FUNCTION_MACROS   = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES tag can be used to specify one or more tag files. For each tag
+# file the location of the external documentation should be added. The format of
+# a tag file without this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where loc1 and loc2 can be relative or absolute paths or URLs. See the
+# section "Linking to external documentation" for more information about the use
+# of tag files.
+# Note: Each tag file must have an unique name (where the name does NOT include
+# the path). If a tag file is not located in the directory in which doxygen is
+# run, you must also specify the path to the tagfile here.
+
+TAGFILES               =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
+# tag file that is based on the input files it reads. See section "Linking to
+# external documentation" for more information about the usage of tag files.
+
+GENERATE_TAGFILE       =
+
+# If the ALLEXTERNALS tag is set to YES all external class will be listed in the
+# class index. If set to NO only the inherited external classes will be listed.
+# The default value is: NO.
+
+ALLEXTERNALS           = YES
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed in
+# the modules index. If set to NO, only the current project's groups will be
+# listed.
+# The default value is: YES.
+
+EXTERNAL_GROUPS        = YES
+
+# If the EXTERNAL_PAGES tag is set to YES all external pages will be listed in
+# the related pages index. If set to NO, only the current project's pages will
+# be listed.
+# The default value is: YES.
+
+EXTERNAL_PAGES         = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of 'which perl').
+# The default file (with absolute path) is: /usr/bin/perl.
+
+PERL_PATH              = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES doxygen will generate a class diagram
+# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
+# NO turns the diagrams off. Note that this option also works with HAVE_DOT
+# disabled, but it is recommended to install and use dot, since it yields more
+# powerful graphs.
+# The default value is: YES.
+
+CLASS_DIAGRAMS         = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see:
+# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH            =
+
+# You can include diagrams made with dia in doxygen documentation. Doxygen will
+# then run dia to produce the diagram and insert it in the documentation. The
+# DIA_PATH tag allows you to specify the directory where the dia binary resides.
+# If left empty dia is assumed to be found in the default search path.
+
+DIA_PATH               =
+
+# If set to YES, the inheritance and collaboration graphs will hide inheritance
+# and usage relations if the target is undocumented or is not a class.
+# The default value is: YES.
+
+HIDE_UNDOC_RELATIONS   = NO
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz (see:
+# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
+# Bell Labs. The other options in this section have no effect if this option is
+# set to NO
+# The default value is: NO.
+
+HAVE_DOT               = YES
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
+# to run in parallel. When set to 0 doxygen will base this on the number of
+# processors available in the system. You can set it explicitly to a value
+# larger than 0 to get control over the balance between CPU load and processing
+# speed.
+# Minimum value: 0, maximum value: 32, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_NUM_THREADS        = 0
+
+# When you want a differently looking font n the dot files that doxygen
+# generates you can specify the font name using DOT_FONTNAME. You need to make
+# sure dot is able to find the font, which can be done by putting it in a
+# standard location or by setting the DOTFONTPATH environment variable or by
+# setting DOT_FONTPATH to the directory containing the font.
+# The default value is: Helvetica.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTNAME           = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
+# dot graphs.
+# Minimum value: 4, maximum value: 24, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTSIZE           = 10
+
+# By default doxygen will tell dot to use the default font as specified with
+# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
+# the path where dot can find it using this tag.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTPATH           =
+
+# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
+# each documented class showing the direct and indirect inheritance relations.
+# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CLASS_GRAPH            = YES
+
+# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
+# graph for each documented class showing the direct and indirect implementation
+# dependencies (inheritance, containment, and class references variables) of the
+# class with other documented classes.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+COLLABORATION_GRAPH    = YES
+
+# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
+# groups, showing the direct groups dependencies.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GROUP_GRAPHS           = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LOOK               = NO
+
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
+# class node. If there are many fields or methods and many nodes the graph may
+# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
+# number of items for each type to make the size more manageable. Set this to 0
+# for no limit. Note that the threshold may be exceeded by 50% before the limit
+# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
+# but if the number exceeds 15, the total amount of fields shown is limited to
+# 10.
+# Minimum value: 0, maximum value: 100, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LIMIT_NUM_FIELDS   = 10
+
+# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
+# collaboration graphs will show the relations between templates and their
+# instances.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+TEMPLATE_RELATIONS     = YES
+
+# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
+# YES then doxygen will generate a graph for each documented file showing the
+# direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDE_GRAPH          = YES
+
+# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
+# set to YES then doxygen will generate a graph for each documented file showing
+# the direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDED_BY_GRAPH      = YES
+
+# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALL_GRAPH             = NO
+
+# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable caller graphs for selected
+# functions only using the \callergraph command.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALLER_GRAPH           = NO
+
+# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
+# hierarchy of all classes instead of a textual one.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GRAPHICAL_HIERARCHY    = YES
+
+# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
+# dependencies a directory has on other directories in a graphical way. The
+# dependency relations are determined by the #include relations between the
+# files in the directories.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DIRECTORY_GRAPH        = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot.
+# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
+# to make the SVG files visible in IE 9+ (other browsers do not have this
+# requirement).
+# Possible values are: png, jpg, gif and svg.
+# The default value is: png.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_IMAGE_FORMAT       = @DOT_IMAGE_FORMAT@
+
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+#
+# Note that this requires a modern browser other than Internet Explorer. Tested
+# and working are Firefox, Chrome, Safari, and Opera.
+# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
+# the SVG files visible. Older versions of IE do not have SVG support.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INTERACTIVE_SVG        = NO
+
+# The DOT_PATH tag can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_PATH               = @DOT@
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the \dotfile
+# command).
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOTFILE_DIRS           =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the \mscfile
+# command).
+
+MSCFILE_DIRS           =
+
+# The DIAFILE_DIRS tag can be used to specify one or more directories that
+# contain dia files that are included in the documentation (see the \diafile
+# command).
+
+DIAFILE_DIRS           =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
+# that will be shown in the graph. If the number of nodes in a graph becomes
+# larger than this value, doxygen will truncate the graph, which is visualized
+# by representing a node as a red box. Note that doxygen if the number of direct
+# children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that
+# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+# Minimum value: 0, maximum value: 10000, default value: 50.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_GRAPH_MAX_NODES    = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
+# generated by dot. A depth value of 3 means that only nodes reachable from the
+# root by following a path via at most 3 edges will be shown. Nodes that lay
+# further from the root node will be omitted. Note that setting this option to 1
+# or 2 may greatly reduce the computation time needed for large code bases. Also
+# note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+# Minimum value: 0, maximum value: 1000, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+MAX_DOT_GRAPH_DEPTH    = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not seem
+# to support this out of the box.
+#
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_TRANSPARENT        = YES
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10) support
+# this, this feature is disabled by default.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_MULTI_TARGETS      = YES
+
+# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
+# explaining the meaning of the various boxes and arrows in the dot generated
+# graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GENERATE_LEGEND        = YES
+
+# If the DOT_CLEANUP tag is set to YES doxygen will remove the intermediate dot
+# files that are used to generate the various graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_CLEANUP            = YES
diff --git a/docs/include-fixer.rst b/docs/include-fixer.rst
new file mode 100644 (file)
index 0000000..fcd2998
--- /dev/null
@@ -0,0 +1,155 @@
+===================
+Clang-Include-Fixer
+===================
+
+.. contents::
+
+One of the major nuisances of C++ compared to other languages is the manual
+management of ``#include`` directives in any file.
+:program:`clang-include-fixer` addresses one aspect of this problem by providing
+an automated way of adding ``#include`` directives for missing symbols in one
+translation unit.
+
+While inserting missing ``#include``, :program:`clang-include-fixer` adds
+missing namespace qualifiers to all instances of an unidentified symbol if
+the symbol is missing some prefix namespace qualifiers.
+
+Setup
+=====
+
+To use :program:`clang-include-fixer` two databases are required. Both can be
+generated with existing tools.
+
+- Compilation database. Contains the compiler commands for any given file in a
+  project and can be generated by CMake, see `How To Setup Tooling For LLVM`_.
+- Symbol index. Contains all symbol information in a project to match a given
+  identifier to a header file.
+
+Ideally both databases (``compile_commands.json`` and
+``find_all_symbols_db.yaml``) are linked into the root of the source tree they
+correspond to. Then the :program:`clang-include-fixer` can automatically pick
+them up if called with a source file from that tree. Note that by default
+``compile_commands.json`` as generated by CMake does not include header files,
+so only implementation files can be handled by tools.
+
+.. _How To Setup Tooling For LLVM: http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html
+
+Creating a Symbol Index From a Compilation Database
+---------------------------------------------------
+
+The include fixer contains :program:`find-all-symbols`, a tool to create a
+symbol database in YAML format from a compilation database by parsing all
+source files listed in it. The following list of commands shows how to set up a
+database for LLVM, any project built by CMake should follow similar steps.
+
+.. code-block:: console
+
+  $ cd path/to/llvm-build
+  $ ninja find-all-symbols // build find-all-symbols tool.
+  $ ninja clang-include-fixer // build clang-include-fixer tool.
+  $ ls compile_commands.json # Make sure compile_commands.json exists.
+    compile_commands.json
+  $ path/to/llvm/source/tools/clang/tools/extra/include-fixer/find-all-symbols/tool/run-find-all-symbols.py
+    ... wait as clang indexes the code base ...
+  $ ln -s $PWD/find_all_symbols_db.yaml path/to/llvm/source/ # Link database into the source tree.
+  $ ln -s $PWD/compile_commands.json path/to/llvm/source/ # Also link compilation database if it's not there already.
+  $ cd path/to/llvm/source
+  $ /path/to/clang-include-fixer -db=yaml path/to/file/with/missing/include.cpp
+    Added #include "foo.h"
+
+Integrate with Vim
+------------------
+To run `clang-include-fixer` on a potentially unsaved buffer in Vim. Add the
+following key binding to your ``.vimrc``:
+
+.. code-block:: console
+
+  noremap <leader>cf :pyf path/to/llvm/source/tools/clang/tools/extra/include-fixer/tool/clang-include-fixer.py<cr>
+
+This enables `clang-include-fixer` for NORMAL and VISUAL mode. Change
+`<leader>cf` to another binding if you need clang-include-fixer on a different
+key. The `<leader> key
+<http://vim.wikia.com/wiki/Mapping_keys_in_Vim_-_Tutorial_(Part_3)#Map_leader>`_
+is a reference to a specific key defined by the mapleader variable and is bound
+to backslash by default.
+
+Make sure vim can find :program:`clang-include-fixer`:
+
+- Add the path to :program:`clang-include-fixer` to the PATH environment variable.
+- Or set ``g:clang_include_fixer_path`` in vimrc: ``let g:clang_include_fixer_path=path/to/clang-include-fixer``
+
+You can customize the number of headers being shown by setting
+``let g:clang_include_fixer_maximum_suggested_headers=5``
+
+Customized settings in `.vimrc`:
+
+- ``let g:clang_include_fixer_path = "clang-include-fixer"``
+
+  Set clang-include-fixer binary file path.
+
+- ``let g:clang_include_fixer_maximum_suggested_headers = 3``
+
+  Set the maximum number of ``#includes`` to show. Default is 3.
+
+- ``let g:clang_include_fixer_increment_num = 5``
+
+  Set the increment number of #includes to show every time when pressing ``m``.
+  Default is 5.
+
+- ``let g:clang_include_fixer_jump_to_include = 0``
+
+  Set to 1 if you want to jump to the new inserted ``#include`` line. Default is
+  0.
+
+- ``let g:clang_include_fixer_query_mode = 0``
+
+  Set to 1 if you want to insert ``#include`` for the symbol under the cursor.
+  Default is 0. Compared to normal mode, this mode won't parse the source file
+  and only search the sysmbol from database, which is faster than normal mode.
+
+See ``clang-include-fixer.py`` for more details.
+
+Integrate with Emacs
+--------------------
+To run `clang-include-fixer` on a potentially unsaved buffer in Emacs.
+Ensure that Emacs finds ``clang-include-fixer.el`` by adding the directory
+containing the file to the ``load-path`` and requiring the `clang-include-fixer`
+in your ``.emacs``:
+
+.. code-block:: console
+
+ (add-to-list 'load-path "path/to/llvm/source/tools/clang/tools/extra/include-fixer/tool/"
+ (require 'clang-include-fixer)
+
+Within Emacs the tool can be invoked with the command
+``M-x clang-include-fixer``. This will insert the header that defines the
+first undefined symbol; if there is more than one header that would define the
+symbol, the user is prompted to select one.
+
+To include the header that defines the symbol at point, run
+``M-x clang-include-fixer-at-point``.
+
+Make sure Emacs can find :program:`clang-include-fixer`:
+
+- Either add the parent directory of :program:`clang-include-fixer` to the PATH
+  environment variable, or customize the Emacs user option
+  ``clang-include-fixer-executable`` to point to the file name of the program.
+
+How it Works
+============
+
+To get the most information out of Clang at parse time,
+:program:`clang-include-fixer` runs in tandem with the parse and receives
+callbacks from Clang's semantic analysis. In particular it reuses the existing
+support for typo corrections. Whenever Clang tries to correct a potential typo
+it emits a callback to the include fixer which then looks for a corresponding
+file. At this point rich lookup information is still available, which is not
+available in the AST at a later stage.
+
+The identifier that should be typo corrected is then sent to the database, if a
+header file is returned it is added as an include directive at the top of the
+file.
+
+Currently :program:`clang-include-fixer` only inserts a single include at a
+time to avoid getting caught in follow-up errors. If multiple `#include`
+additions are desired the program can be rerun until a fix-point is reached.
diff --git a/docs/index.rst b/docs/index.rst
new file mode 100644 (file)
index 0000000..722543e
--- /dev/null
@@ -0,0 +1,35 @@
+.. Extra Clang Tools documentation master file, created by
+   sphinx-quickstart on Wed Feb 13 10:00:18 2013.
+   You can adapt this file completely to your liking, but it should at least
+   contain the root `toctree` directive.
+
+.. title:: Welcome to Extra Clang Tools's documentation!
+
+Introduction
+============
+Welcome to the clang-tools-extra project which contains extra tools built using
+Clang's tooling API's.
+
+.. toctree::
+   :maxdepth: 1
+
+   ReleaseNotes
+
+Contents
+========
+.. toctree::
+   :maxdepth: 2
+
+   clang-tidy/index
+   include-fixer
+   modularize
+   pp-trace
+   clang-rename
+   clangd
+
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`search`
diff --git a/docs/make.bat b/docs/make.bat
new file mode 100644 (file)
index 0000000..09b798c
--- /dev/null
@@ -0,0 +1,190 @@
+@ECHO OFF
+
+REM Command file for Sphinx documentation
+
+if "%SPHINXBUILD%" == "" (
+       set SPHINXBUILD=sphinx-build
+)
+set BUILDDIR=_build
+set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
+set I18NSPHINXOPTS=%SPHINXOPTS% .
+if NOT "%PAPER%" == "" (
+       set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
+       set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
+)
+
+if "%1" == "" goto help
+
+if "%1" == "help" (
+       :help
+       echo.Please use `make ^<target^>` where ^<target^> is one of
+       echo.  html       to make standalone HTML files
+       echo.  dirhtml    to make HTML files named index.html in directories
+       echo.  singlehtml to make a single large HTML file
+       echo.  pickle     to make pickle files
+       echo.  json       to make JSON files
+       echo.  htmlhelp   to make HTML files and a HTML help project
+       echo.  qthelp     to make HTML files and a qthelp project
+       echo.  devhelp    to make HTML files and a Devhelp project
+       echo.  epub       to make an epub
+       echo.  latex      to make LaTeX files, you can set PAPER=a4 or PAPER=letter
+       echo.  text       to make text files
+       echo.  man        to make manual pages
+       echo.  texinfo    to make Texinfo files
+       echo.  gettext    to make PO message catalogs
+       echo.  changes    to make an overview over all changed/added/deprecated items
+       echo.  linkcheck  to check all external links for integrity
+       echo.  doctest    to run all doctests embedded in the documentation if enabled
+       goto end
+)
+
+if "%1" == "clean" (
+       for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
+       del /q /s %BUILDDIR%\*
+       goto end
+)
+
+if "%1" == "html" (
+       %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
+       if errorlevel 1 exit /b 1
+       echo.
+       echo.Build finished. The HTML pages are in %BUILDDIR%/html.
+       goto end
+)
+
+if "%1" == "dirhtml" (
+       %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
+       if errorlevel 1 exit /b 1
+       echo.
+       echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
+       goto end
+)
+
+if "%1" == "singlehtml" (
+       %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
+       if errorlevel 1 exit /b 1
+       echo.
+       echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
+       goto end
+)
+
+if "%1" == "pickle" (
+       %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
+       if errorlevel 1 exit /b 1
+       echo.
+       echo.Build finished; now you can process the pickle files.
+       goto end
+)
+
+if "%1" == "json" (
+       %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
+       if errorlevel 1 exit /b 1
+       echo.
+       echo.Build finished; now you can process the JSON files.
+       goto end
+)
+
+if "%1" == "htmlhelp" (
+       %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
+       if errorlevel 1 exit /b 1
+       echo.
+       echo.Build finished; now you can run HTML Help Workshop with the ^
+.hhp project file in %BUILDDIR%/htmlhelp.
+       goto end
+)
+
+if "%1" == "qthelp" (
+       %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
+       if errorlevel 1 exit /b 1
+       echo.
+       echo.Build finished; now you can run "qcollectiongenerator" with the ^
+.qhcp project file in %BUILDDIR%/qthelp, like this:
+       echo.^> qcollectiongenerator %BUILDDIR%\qthelp\ExtraClangTools.qhcp
+       echo.To view the help file:
+       echo.^> assistant -collectionFile %BUILDDIR%\qthelp\ExtraClangTools.ghc
+       goto end
+)
+
+if "%1" == "devhelp" (
+       %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
+       if errorlevel 1 exit /b 1
+       echo.
+       echo.Build finished.
+       goto end
+)
+
+if "%1" == "epub" (
+       %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
+       if errorlevel 1 exit /b 1
+       echo.
+       echo.Build finished. The epub file is in %BUILDDIR%/epub.
+       goto end
+)
+
+if "%1" == "latex" (
+       %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+       if errorlevel 1 exit /b 1
+       echo.
+       echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
+       goto end
+)
+
+if "%1" == "text" (
+       %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
+       if errorlevel 1 exit /b 1
+       echo.
+       echo.Build finished. The text files are in %BUILDDIR%/text.
+       goto end
+)
+
+if "%1" == "man" (
+       %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
+       if errorlevel 1 exit /b 1
+       echo.
+       echo.Build finished. The manual pages are in %BUILDDIR%/man.
+       goto end
+)
+
+if "%1" == "texinfo" (
+       %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
+       if errorlevel 1 exit /b 1
+       echo.
+       echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
+       goto end
+)
+
+if "%1" == "gettext" (
+       %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
+       if errorlevel 1 exit /b 1
+       echo.
+       echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
+       goto end
+)
+
+if "%1" == "changes" (
+       %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
+       if errorlevel 1 exit /b 1
+       echo.
+       echo.The overview file is in %BUILDDIR%/changes.
+       goto end
+)
+
+if "%1" == "linkcheck" (
+       %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
+       if errorlevel 1 exit /b 1
+       echo.
+       echo.Link check complete; look for any errors in the above output ^
+or in %BUILDDIR%/linkcheck/output.txt.
+       goto end
+)
+
+if "%1" == "doctest" (
+       %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
+       if errorlevel 1 exit /b 1
+       echo.
+       echo.Testing of doctests in the sources finished, look at the ^
+results in %BUILDDIR%/doctest/output.txt.
+       goto end
+)
+
+:end
diff --git a/docs/modularize.rst b/docs/modularize.rst
new file mode 100644 (file)
index 0000000..6a62c42
--- /dev/null
@@ -0,0 +1,265 @@
+.. index:: modularize
+
+==================================
+Modularize User's Manual
+==================================
+
+.. toctree::
+   :hidden:
+
+   ModularizeUsage
+
+:program:`modularize` is a standalone tool that checks whether a set of headers
+provides the consistent definitions required to use modules. For example, it
+detects whether the same entity (say, a NULL macro or size_t typedef) is
+defined in multiple headers or whether a header produces different definitions
+under different circumstances. These conditions cause modules built from the
+headers to behave poorly, and should be fixed before introducing a module
+map.
+
+:program:`modularize` also has an assistant mode option for generating
+a module map file based on the provided header list. The generated file
+is a functional module map that can be used as a starting point for a
+module.map file.
+
+Getting Started
+===============
+
+To build from source:
+
+1. Read `Getting Started with the LLVM System`_ and `Clang Tools
+   Documentation`_ for information on getting sources for LLVM, Clang, and
+   Clang Extra Tools.
+
+2. `Getting Started with the LLVM System`_ and `Building LLVM with CMake`_ give
+   directions for how to build. With sources all checked out into the
+   right place the LLVM build will build Clang Extra Tools and their
+   dependencies automatically.
+
+   * If using CMake, you can also use the ``modularize`` target to build
+     just the modularize tool and its dependencies.
+
+Before continuing, take a look at :doc:`ModularizeUsage` to see how to invoke
+modularize.
+
+.. _Getting Started with the LLVM System: http://llvm.org/docs/GettingStarted.html
+.. _Building LLVM with CMake: http://llvm.org/docs/CMake.html
+.. _Clang Tools Documentation: http://clang.llvm.org/docs/ClangTools.html
+
+What Modularize Checks
+======================
+
+Modularize will check for the following:
+
+* Duplicate global type and variable definitions
+* Duplicate macro definitions
+* Macro instances, 'defined(macro)', or #if, #elif, #ifdef, #ifndef conditions
+  that evaluate differently in a header
+* #include directives inside 'extern "C/C++" {}' or 'namespace (name) {}' blocks
+* Module map header coverage completeness (in the case of a module map input
+  only)
+
+Modularize will do normal C/C++ parsing, reporting normal errors and warnings,
+but will also report special error messages like the following::
+
+  error: '(symbol)' defined at multiple locations:
+     (file):(row):(column)
+     (file):(row):(column)
+
+  error: header '(file)' has different contents depending on how it was included
+
+The latter might be followed by messages like the following::
+
+  note: '(symbol)' in (file) at (row):(column) not always provided
+
+Checks will also be performed for macro expansions, defined(macro)
+expressions, and preprocessor conditional directives that evaluate
+inconsistently, and can produce error messages like the following::
+
+   (...)/SubHeader.h:11:5:
+  #if SYMBOL == 1
+      ^
+  error: Macro instance 'SYMBOL' has different values in this header,
+         depending on how it was included.
+    'SYMBOL' expanded to: '1' with respect to these inclusion paths:
+      (...)/Header1.h
+        (...)/SubHeader.h
+  (...)/SubHeader.h:3:9:
+  #define SYMBOL 1
+          ^
+  Macro defined here.
+    'SYMBOL' expanded to: '2' with respect to these inclusion paths:
+      (...)/Header2.h
+          (...)/SubHeader.h
+  (...)/SubHeader.h:7:9:
+  #define SYMBOL 2
+          ^
+  Macro defined here.
+
+Checks will also be performed for '#include' directives that are
+nested inside 'extern "C/C++" {}' or 'namespace (name) {}' blocks,
+and can produce error message like the following::
+
+  IncludeInExtern.h:2:3:
+  #include "Empty.h"
+  ^
+  error: Include directive within extern "C" {}.
+  IncludeInExtern.h:1:1:
+  extern "C" {
+  ^
+  The "extern "C" {}" block is here.
+
+.. _module-map-coverage:
+
+Module Map Coverage Check
+=========================
+
+The coverage check uses the Clang library to read and parse the
+module map file. Starting at the module map file directory, or just the
+include paths, if specified, it will collect the names of all the files it
+considers headers (no extension, .h, or .inc--if you need more, modify the
+isHeader function). It then compares the headers against those referenced
+in the module map, either explicitly named, or implicitly named via an
+umbrella directory or umbrella file, as parsed by the ModuleMap object.
+If headers are found which are not referenced or covered by an umbrella
+directory or file, warning messages will be produced, and this program
+will return an error code of 1. If no problems are found, an error code of
+0 is returned.
+
+Note that in the case of umbrella headers, this tool invokes the compiler
+to preprocess the file, and uses a callback to collect the header files
+included by the umbrella header or any of its nested includes. If any
+front end options are needed for these compiler invocations, these
+can be included on the command line after the module map file argument.
+
+Warning message have the form:
+
+  warning: module.modulemap does not account for file: Level3A.h
+
+Note that for the case of the module map referencing a file that does
+not exist, the module map parser in Clang will (at the time of this
+writing) display an error message.
+
+To limit the checks :program:`modularize` does to just the module
+map coverage check, use the ``-coverage-check-only option``.
+
+For example::
+
+  modularize -coverage-check-only module.modulemap
+
+.. _module-map-generation:
+
+Module Map Generation
+=====================
+
+If you specify the ``-module-map-path=<module map file>``,
+:program:`modularize` will output a module map based on the input header list.
+A module will be created for each header. Also, if the header in the header
+list is a partial path, a nested module hierarchy will be created in which a
+module will be created for each subdirectory component in the header path,
+with the header itself represented by the innermost module. If other headers
+use the same subdirectories, they will be enclosed in these same modules also.
+
+For example, for the header list::
+
+  SomeTypes.h
+  SomeDecls.h
+  SubModule1/Header1.h
+  SubModule1/Header2.h
+  SubModule2/Header3.h
+  SubModule2/Header4.h
+  SubModule2.h
+
+The following module map will be generated::
+
+  // Output/NoProblemsAssistant.txt\r
+  // Generated by: modularize -module-map-path=Output/NoProblemsAssistant.txt \
+       -root-module=Root NoProblemsAssistant.modularize\r
+  \r
+  module SomeTypes {\r
+    header "SomeTypes.h"\r
+    export *\r
+  }\r
+  module SomeDecls {\r
+    header "SomeDecls.h"\r
+    export *\r
+  }\r
+  module SubModule1 {\r
+    module Header1 {\r
+      header "SubModule1/Header1.h"\r
+      export *\r
+    }\r
+    module Header2 {\r
+      header "SubModule1/Header2.h"\r
+      export *\r
+    }\r
+  }\r
+  module SubModule2 {\r
+    module Header3 {\r
+      header "SubModule2/Header3.h"\r
+      export *\r
+    }\r
+    module Header4 {\r
+      header "SubModule2/Header4.h"\r
+      export *\r
+    }\r
+    header "SubModule2.h"\r
+    export *\r
+  }\r
+
+An optional ``-root-module=<root-name>`` option can be used to cause a root module
+to be created which encloses all the modules.
+
+An optional ``-problem-files-list=<problem-file-name>`` can be used to input
+a list of files to be excluded, perhaps as a temporary stop-gap measure until
+problem headers can be fixed.
+
+For example, with the same header list from above::
+
+  // Output/NoProblemsAssistant.txt\r
+  // Generated by: modularize -module-map-path=Output/NoProblemsAssistant.txt \
+       -root-module=Root NoProblemsAssistant.modularize\r
+  \r
+  module Root {\r
+    module SomeTypes {\r
+      header "SomeTypes.h"\r
+      export *\r
+    }\r
+    module SomeDecls {\r
+      header "SomeDecls.h"\r
+      export *\r
+    }\r
+    module SubModule1 {\r
+      module Header1 {\r
+        header "SubModule1/Header1.h"\r
+        export *\r
+      }\r
+      module Header2 {\r
+        header "SubModule1/Header2.h"\r
+        export *\r
+      }\r
+    }\r
+    module SubModule2 {\r
+      module Header3 {\r
+        header "SubModule2/Header3.h"\r
+        export *\r
+      }\r
+      module Header4 {\r
+        header "SubModule2/Header4.h"\r
+        export *\r
+      }\r
+      header "SubModule2.h"\r
+      export *\r
+    }\r
+  }\r
+\r
+Note that headers with dependents will be ignored with a warning, as the\r
+Clang module mechanism doesn't support headers the rely on other headers\r
+to be included first.\r
+\r
+The module map format defines some keywords which can't be used in module\r
+names. If a header has one of these names, an underscore ('_') will be\r
+prepended to the name. For example, if the header name is ``header.h``,
+because ``header`` is a keyword, the module name will be ``_header``.
+For a list of the module map keywords, please see:
+`Lexical structure <http://clang.llvm.org/docs/Modules.html#lexical-structure>`_\r
diff --git a/docs/pp-trace.rst b/docs/pp-trace.rst
new file mode 100644 (file)
index 0000000..902db6f
--- /dev/null
@@ -0,0 +1,825 @@
+.. index:: pp-trace
+
+==================================
+pp-trace User's Manual
+==================================
+
+.. toctree::
+   :hidden:
+
+:program:`pp-trace` is a standalone tool that traces preprocessor
+activity. It's also used as a test of Clang's PPCallbacks interface.
+It runs a given source file through the Clang preprocessor, displaying
+selected information from callback functions overridden in a
+`PPCallbacks <http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html>`_
+derivation. The output is in a high-level YAML format, described in
+:ref:`OutputFormat`.
+
+.. _Usage:
+
+pp-trace Usage
+==============
+
+Command Line Format
+-------------------
+
+``pp-trace [<pp-trace-options>] <source-file> [<front-end-options>]``
+
+``<pp-trace-options>`` is a place-holder for options
+specific to pp-trace, which are described below in
+:ref:`CommandLineOptions`.
+
+``<source-file>`` specifies the source file to run through the preprocessor.
+
+``<front-end-options>`` is a place-holder for regular
+`Clang Compiler Options <http://clang.llvm.org/docs/UsersManual.html#command-line-options>`_,
+which must follow the <source-file>.
+
+.. _CommandLineOptions:
+
+Command Line Options
+--------------------
+
+.. option:: -ignore <callback-name-list>
+
+  This option specifies a comma-separated list of names of callbacks
+  that shouldn't be traced. It can be used to eliminate unwanted
+  trace output. The callback names are the name of the actual
+  callback function names in the PPCallbacks class:
+
+  * FileChanged
+  * FileSkipped
+  * FileNotFound
+  * InclusionDirective
+  * moduleImport
+  * EndOfMainFile
+  * Ident
+  * PragmaDirective
+  * PragmaComment
+  * PragmaDetectMismatch
+  * PragmaDebug
+  * PragmaMessage
+  * PragmaDiagnosticPush
+  * PragmaDiagnosticPop
+  * PragmaDiagnostic
+  * PragmaOpenCLExtension
+  * PragmaWarning
+  * PragmaWarningPush
+  * PragmaWarningPop
+  * MacroExpands
+  * MacroDefined
+  * MacroUndefined
+  * Defined
+  * SourceRangeSkipped
+  * If
+  * Elif
+  * Ifdef
+  * Ifndef
+  * Else
+  * Endif
+
+.. option:: -output <output-file>
+
+  By default, pp-trace outputs the trace information to stdout. Use this
+  option to output the trace information to a file.
+
+.. _OutputFormat:
+
+pp-trace Output Format
+======================
+
+The pp-trace output is formatted as YAML. See http://yaml.org/ for general
+YAML information. It's arranged as a sequence of information about the
+callback call, including the callback name and argument information, for
+example:::
+
+  ---
+  - Callback: Name
+    Argument1: Value1
+    Argument2: Value2
+  (etc.)
+  ...
+
+With real data:::
+
+  ---
+  - Callback: FileChanged\r
+    Loc: "c:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-include.cpp:1:1"\r
+    Reason: EnterFile\r
+    FileType: C_User\r
+    PrevFID: (invalid)\r
+    (etc.)\r
+  - Callback: FileChanged\r
+    Loc: "D:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-include.cpp:5:1"\r
+    Reason: ExitFile\r
+    FileType: C_User\r
+    PrevFID: "D:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/Input/Level1B.h"\r
+  - Callback: EndOfMainFile\r
+  ...
+
+In all but one case (MacroDirective) the "Argument" scalars have the same
+name as the argument in the corresponding PPCallbacks callback function.
+
+Callback Details
+----------------
+
+The following sections describe the pupose and output format for each callback.
+
+Click on the callback name in the section heading to see the Doxygen
+documentation for the callback.
+
+The argument descriptions table describes the callback argument information
+displayed.
+
+The Argument Name field in most (but not all) cases is the same name as the
+callback function parameter.
+
+The Argument Value Syntax field describes the values that will be displayed
+for the argument value. It uses an ad hoc representation that mixes literal
+and symbolic representations. Enumeration member symbols are shown as the
+actual enum member in a (member1|member2|...) form. A name in parentheses
+can either represent a place holder for the described value, or confusingly,
+it might be a literal, such as (null), for a null pointer.
+Locations are shown as quoted only to avoid confusing the documentation generator.
+
+The Clang C++ Type field is the type from the callback function declaration.
+
+The description describes the argument or what is displayed for it.
+
+Note that in some cases, such as when a structure pointer is an argument
+value, only some key member or members are shown to represent the value,
+instead of trying to display all members of the structure.
+
+`FileChanged <http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a7cc8cfaf34114fc65e92af621cd6464e>`_ Callback
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+FileChanged is called when the preprocessor enters or exits a file, both the
+top level file being compiled, as well as any #include directives. It will
+also be called as a result of a system header pragma or in internal renaming
+of a file.
+
+Argument descriptions:
+
+==============   ==================================================   ============================== ==============================
+Argument Name    Argument Value Syntax                                Clang C++ Type                 Description           
+==============   ==================================================   ============================== ==============================
+Loc              "(file):(line):(col)"                                SourceLocation                 The location of the directive.
+Reason           (EnterFile|ExitFile|SystemHeaderPragma|RenameFile)   PPCallbacks::FileChangeReason  Reason for change.
+FileType         (C_User|C_System|C_ExternCSystem)                    SrcMgr::CharacteristicKind     Include type.
+PrevFID          ((file)|(invalid))                                   FileID                         Previous file, if any.
+==============   ==================================================   ============================== ==============================
+
+Example:::
+
+  - Callback: FileChanged\r
+    Loc: "D:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-include.cpp:1:1"\r
+    Reason: EnterFile\r
+    FileType: C_User\r
+    PrevFID: (invalid)\r
+
+`FileSkipped <http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#ab5b338a0670188eb05fa7685bbfb5128>`_ Callback
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+FileSkipped is called when a source file is skipped as the result of header
+guard optimization.
+
+Argument descriptions:
+
+==============   ==================================================   ============================== ========================================================
+Argument Name    Argument Value Syntax                                Clang C++ Type                 Description           
+==============   ==================================================   ============================== ========================================================
+ParentFile       ("(file)" or (null))                                 const FileEntry                The file that #included the skipped file.
+FilenameTok      (token)                                              const Token                    The token in ParentFile that indicates the skipped file.
+FileType         (C_User|C_System|C_ExternCSystem)                    SrcMgr::CharacteristicKind     The file type.
+==============   ==================================================   ============================== ========================================================
+
+Example:::
+
+  - Callback: FileSkipped\r
+    ParentFile: "/path/filename.h"\r
+    FilenameTok: "filename.h"\r
+    FileType: C_User\r
+
+`FileNotFound <http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a3045151545f987256bfa8d978916ef00>`_ Callback
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+FileNotFound is called when an inclusion directive results in a file-not-found error.
+
+Argument descriptions:
+
+==============   ==================================================   ============================== =====================================================================================================================================
+Argument Name    Argument Value Syntax                                Clang C++ Type                 Description           
+==============   ==================================================   ============================== =====================================================================================================================================
+FileName         "(file)"                                             StringRef                      The name of the file being included, as written in the source code.
+RecoveryPath     (path)                                               SmallVectorImpl<char>          If this client indicates that it can recover from this missing file, the client should set this as an additional header search patch.
+==============   ==================================================   ============================== =====================================================================================================================================
+
+Example:::
+
+  - Callback: FileNotFound\r
+    FileName: "/path/filename.h"
+    RecoveryPath:
+
+`InclusionDirective <http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a557d9738c329793513a6f57d6b60de52>`_ Callback
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+InclusionDirective is called when an inclusion directive of any kind (#include</code>, #import</code>, etc.) has been processed, regardless of whether the inclusion will actually result in an inclusion.
+
+Argument descriptions:
+
+==============   ==================================================   ============================== ============================================================================================================
+Argument Name    Argument Value Syntax                                Clang C++ Type                 Description           
+==============   ==================================================   ============================== ============================================================================================================
+HashLoc          "(file):(line):(col)"                                SourceLocation                 The location of the '#' that starts the inclusion directive.
+IncludeTok       (token)                                              const Token                    The token that indicates the kind of inclusion directive, e.g., 'include' or 'import'.
+FileName         "(file)"                                             StringRef                      The name of the file being included, as written in the source code.
+IsAngled         (true|false)                                         bool                           Whether the file name was enclosed in angle brackets; otherwise, it was enclosed in quotes.
+FilenameRange    "(file)"                                             CharSourceRange                The character range of the quotes or angle brackets for the written file name.
+File             "(file)"                                             const FileEntry                The actual file that may be included by this inclusion directive.
+SearchPath       "(path)"                                             StringRef                      Contains the search path which was used to find the file in the file system.
+RelativePath     "(path)"                                             StringRef                      The path relative to SearchPath, at which the include file was found.
+Imported         ((module name)|(null))                               const Module                   The module, whenever an inclusion directive was automatically turned into a module import or null otherwise.
+==============   ==================================================   ============================== ============================================================================================================
+
+Example:::
+
+  - Callback: InclusionDirective\r
+    IncludeTok: include\r
+    FileName: "Input/Level1B.h"\r
+    IsAngled: false\r
+    FilenameRange: "Input/Level1B.h"\r
+    File: "D:/Clang/llvmnewmod/tools/clang/tools/extra/test/pp-trace/Input/Level1B.h"\r
+    SearchPath: "D:/Clang/llvmnewmod/tools/clang/tools/extra/test/pp-trace"\r
+    RelativePath: "Input/Level1B.h"\r
+    Imported: (null)\r
+
+`moduleImport <http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#af32dcf1b8b7c179c7fcd3e24e89830fe>`_ Callback
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+moduleImport is called when there was an explicit module-import syntax.
+
+Argument descriptions:
+
+==============   ==================================================   ============================== ===========================================================
+Argument Name    Argument Value Syntax                                Clang C++ Type                 Description           
+==============   ==================================================   ============================== ===========================================================
+ImportLoc        "(file):(line):(col)"                                SourceLocation                 The location of import directive token.
+Path             "(path)"                                             ModuleIdPath                   The identifiers (and their locations) of the module "path".
+Imported         ((module name)|(null))                               const Module                   The imported module; can be null if importing failed.
+==============   ==================================================   ============================== ===========================================================
+
+Example:::
+
+  - Callback: moduleImport\r
+    ImportLoc: "d:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-modules.cpp:4:2"
+    Path: [{Name: Level1B, Loc: "d:/Clang/llvmnewmod/tools/clang/tools/extra/test/pp-trace/pp-trace-modules.cpp:4:9"}, {Name: Level2B, Loc: "d:/Clang/llvmnewmod/tools/clang/tools/extra/test/pp-trace/pp-trace-modules.cpp:4:17"}]
+    Imported: Level2B
+
+`EndOfMainFile <http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a63e170d069e99bc1c9c7ea0f3bed8bcc>`_ Callback
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+EndOfMainFile is called when the end of the main file is reached.
+
+Argument descriptions:
+
+==============   ==================================================   ============================== ======================
+Argument Name    Argument Value Syntax                                Clang C++ Type                 Description           
+==============   ==================================================   ============================== ======================
+(no arguments)
+==============   ==================================================   ============================== ======================
+
+Example:::
+
+  - Callback: EndOfMainFile\r
+
+`Ident <http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a3683f1d1fa513e9b6193d446a5cc2b66>`_ Callback
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Ident is called when a #ident or #sccs directive is read.
+
+Argument descriptions:
+
+==============   ==================================================   ============================== ==============================
+Argument Name    Argument Value Syntax                                Clang C++ Type                 Description           
+==============   ==================================================   ============================== ==============================
+Loc              "(file):(line):(col)"                                SourceLocation                 The location of the directive.
+str              (name)                                               const std::string              The text of the directive.
+==============   ==================================================   ============================== ==============================
+
+Example:::
+
+  - Callback: Ident\r
+    Loc: "D:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-ident.cpp:3:1"
+    str: "$Id$"
+
+`PragmaDirective <http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a0a2d7a72c62184b3cbde31fb62c6f2f7>`_ Callback
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+PragmaDirective is called when start reading any pragma directive.
+
+Argument descriptions:
+
+==============   ==================================================   ============================== =================================
+Argument Name    Argument Value Syntax                                Clang C++ Type                 Description           
+==============   ==================================================   ============================== =================================
+Loc              "(file):(line):(col)"                                SourceLocation                 The location of the directive.
+Introducer       (PIK_HashPragma|PIK__Pragma|PIK___pragma)            PragmaIntroducerKind           The type of the pragma directive.
+==============   ==================================================   ============================== =================================
+
+Example:::
+
+  - Callback: PragmaDirective\r
+    Loc: "D:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-pragma.cpp:3:1"
+    Introducer: PIK_HashPragma
+
+`PragmaComment <http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#ace0d940fc2c12ab76441466aab58dc37>`_ Callback
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+PragmaComment is called when a #pragma comment directive is read.
+
+Argument descriptions:
+
+==============   ==================================================   ============================== ==============================
+Argument Name    Argument Value Syntax                                Clang C++ Type                 Description           
+==============   ==================================================   ============================== ==============================
+Loc              "(file):(line):(col)"                                SourceLocation                 The location of the directive.
+Kind             ((name)|(null))                                      const IdentifierInfo           The comment kind symbol.
+Str              (message directive)                                  const std::string              The comment message directive.
+==============   ==================================================   ============================== ==============================
+
+Example:::
+
+  - Callback: PragmaComment\r
+    Loc: "D:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-pragma.cpp:3:1"
+    Kind: library
+    Str: kernel32.lib
+
+`PragmaDetectMismatch <http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#ab11158c9149fb8ad8af1903f4a6cd65d>`_ Callback
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+PragmaDetectMismatch is called when a #pragma detect_mismatch directive is read.
+
+Argument descriptions:
+
+==============   ==================================================   ============================== ==============================
+Argument Name    Argument Value Syntax                                Clang C++ Type                 Description           
+==============   ==================================================   ============================== ==============================
+Loc              "(file):(line):(col)"                                SourceLocation                 The location of the directive.
+Name             "(name)"                                             const std::string              The name.
+Value            (string)                                             const std::string              The value.
+==============   ==================================================   ============================== ==============================
+
+Example:::
+
+  - Callback: PragmaDetectMismatch\r
+    Loc: "D:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-pragma.cpp:3:1"
+    Name: name
+    Value: value
+
+`PragmaDebug <http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a57cdccb6dcc07e926513ac3d5b121466>`_ Callback
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+PragmaDebug is called when a #pragma clang __debug directive is read.
+
+Argument descriptions:
+
+==============   ==================================================   ============================== ================================
+Argument Name    Argument Value Syntax                                Clang C++ Type                 Description           
+==============   ==================================================   ============================== ================================
+Loc              "(file):(line):(col)"                                SourceLocation                 The location of the directive.
+DebugType        (string)                                             StringRef                      Indicates type of debug message.
+==============   ==================================================   ============================== ================================
+
+Example:::
+
+  - Callback: PragmaDebug\r
+    Loc: "D:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-pragma.cpp:3:1"
+    DebugType: warning
+
+`PragmaMessage <http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#abb42935d9a9fd8e2c4f51cfdc4ea2ae1>`_ Callback
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+PragmaMessage is called when a #pragma message directive is read.
+
+Argument descriptions:
+
+==============   ==================================================   ============================== =======================================
+Argument Name    Argument Value Syntax                                Clang C++ Type                 Description           
+==============   ==================================================   ============================== =======================================
+Loc              "(file):(line):(col)"                                SourceLocation                 The location of the directive.
+Namespace        (name)                                               StringRef                      The namespace of the message directive.
+Kind             (PMK_Message|PMK_Warning|PMK_Error)                  PPCallbacks::PragmaMessageKind The type of the message directive.
+Str              (string)                                             StringRef                      The text of the message directive.
+==============   ==================================================   ============================== =======================================
+
+Example:::
+
+  - Callback: PragmaMessage\r
+    Loc: "D:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-pragma.cpp:3:1"
+    Namespace: "GCC"
+    Kind: PMK_Message
+    Str: The message text.
+
+`PragmaDiagnosticPush <http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a0f3ff19762baa38fe6c5c58022d32979>`_ Callback
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+PragmaDiagnosticPush is called when a #pragma gcc dianostic push directive is read.
+
+Argument descriptions:
+
+==============   ==================================================   ============================== ==============================
+Argument Name    Argument Value Syntax                                Clang C++ Type                 Description           
+==============   ==================================================   ============================== ==============================
+Loc              "(file):(line):(col)"                                SourceLocation                 The location of the directive.
+Namespace        (name)                                               StringRef                      Namespace name.
+==============   ==================================================   ============================== ==============================
+
+Example:::
+
+  - Callback: PragmaDiagnosticPush\r
+    Loc: "D:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-pragma.cpp:3:1"
+    Namespace: "GCC"
+
+`PragmaDiagnosticPop <http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#ac94d789873122221fba8d76f6c5ea45e>`_ Callback
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+PragmaDiagnosticPop is called when a #pragma gcc dianostic pop directive is read.
+
+Argument descriptions:
+
+==============   ==================================================   ============================== ==============================
+Argument Name    Argument Value Syntax                                Clang C++ Type                 Description           
+==============   ==================================================   ============================== ==============================
+Loc              "(file):(line):(col)"                                SourceLocation                 The location of the directive.
+Namespace        (name)                                               StringRef                      Namespace name.
+==============   ==================================================   ============================== ==============================
+
+Example:::
+
+  - Callback: PragmaDiagnosticPop\r
+    Loc: "D:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-pragma.cpp:3:1"
+    Namespace: "GCC"
+
+`PragmaDiagnostic <http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#afe7938f38a83cb7b4b25a13edfdd7bdd>`_ Callback
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+PragmaDiagnostic is called when a #pragma gcc dianostic directive is read.
+
+Argument descriptions:
+
+==============   ==================================================   ============================== ==============================
+Argument Name    Argument Value Syntax                                Clang C++ Type                 Description           
+==============   ==================================================   ============================== ==============================
+Loc              "(file):(line):(col)"                                SourceLocation                 The location of the directive.
+Namespace        (name)                                               StringRef                      Namespace name.
+mapping          (0|MAP_IGNORE|MAP_WARNING|MAP_ERROR|MAP_FATAL)       diag::Severity                 Mapping type.
+Str              (string)                                             StringRef                      Warning/error name.
+==============   ==================================================   ============================== ==============================
+
+Example:::
+
+  - Callback: PragmaDiagnostic\r
+    Loc: "D:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-pragma.cpp:3:1"
+    Namespace: "GCC"
+    mapping: MAP_WARNING
+    Str: WarningName
+
+`PragmaOpenCLExtension <http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a92a20a21fadbab4e2c788f4e27fe07e7>`_ Callback
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+PragmaOpenCLExtension is called when OpenCL extension is either disabled or enabled with a pragma.
+
+Argument descriptions:
+
+==============   ==================================================   ============================== ==========================
+Argument Name    Argument Value Syntax                                Clang C++ Type                 Description           
+==============   ==================================================   ============================== ==========================
+NameLoc          "(file):(line):(col)"                                SourceLocation                 The location of the name.
+Name             (name)                                               const IdentifierInfo           Name symbol.
+StateLoc         "(file):(line):(col)"                                SourceLocation                 The location of the state.
+State            (1|0)                                                unsigned                       Enabled/disabled state.
+==============   ==================================================   ============================== ==========================
+
+Example:::
+
+  - Callback: PragmaOpenCLExtension\r
+    NameLoc: "D:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-pragma.cpp:3:10"
+    Name: Name
+    StateLoc: "D:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-pragma.cpp:3:18"
+    State: 1
+
+`PragmaWarning <http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#aa17169d25fa1cf0a6992fc944d1d8730>`_ Callback
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+PragmaWarning is called when a #pragma warning directive is read.
+
+Argument descriptions:
+
+==============   ==================================================   ============================== ==============================
+Argument Name    Argument Value Syntax                                Clang C++ Type                 Description           
+==============   ==================================================   ============================== ==============================
+Loc              "(file):(line):(col)"                                SourceLocation                 The location of the directive.
+WarningSpec      (string)                                             StringRef                      The warning specifier.
+Ids              [(number)[, ...]]                                    ArrayRef<int>                  The warning numbers.
+==============   ==================================================   ============================== ==============================
+
+Example:::
+
+  - Callback: PragmaWarning\r
+    Loc: "D:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-pragma.cpp:3:1"
+    WarningSpec: disable
+    Ids: 1,2,3
+
+`PragmaWarningPush <http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#ae5626ef70502687a859f323a809ed0b6>`_ Callback
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+PragmaWarningPush is called when a #pragma warning(push) directive is read.
+
+Argument descriptions:
+
+==============   ==================================================   ============================== ==============================
+Argument Name    Argument Value Syntax                                Clang C++ Type                 Description           
+==============   ==================================================   ============================== ==============================
+Loc              "(file):(line):(col)"                                SourceLocation                 The location of the directive.
+Level            (number)                                             int                            Warning level.
+==============   ==================================================   ============================== ==============================
+
+Example:::
+
+  - Callback: PragmaWarningPush\r
+    Loc: "D:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-pragma.cpp:3:1"
+    Level: 1
+
+`PragmaWarningPop <http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#ac98d502af8811b8a6e7342d7cd2b3b95>`_ Callback
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+PragmaWarningPop is called when a #pragma warning(pop) directive is read.
+
+Argument descriptions:
+
+==============   ==================================================   ============================== ==============================
+Argument Name    Argument Value Syntax                                Clang C++ Type                 Description           
+==============   ==================================================   ============================== ==============================
+Loc              "(file):(line):(col)"                                SourceLocation                 The location of the directive.
+==============   ==================================================   ============================== ==============================
+
+Example:::
+
+  - Callback: PragmaWarningPop\r
+    Loc: "D:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-pragma.cpp:3:1"
+
+`MacroExpands <http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a9bc725209d3a071ea649144ab996d515>`_ Callback
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+MacroExpands is called when ::HandleMacroExpandedIdentifier when a macro invocation is found.
+
+Argument descriptions:
+
+==============   ==================================================   ============================== ======================================================================================================
+Argument Name    Argument Value Syntax                                Clang C++ Type                 Description           
+==============   ==================================================   ============================== ======================================================================================================
+MacroNameTok     (token)                                              const Token                    The macro name token.
+MacroDirective   (MD_Define|MD_Undefine|MD_Visibility)                const MacroDirective           The kind of macro directive from the MacroDirective structure.
+Range            ["(file):(line):(col)", "(file):(line):(col)"]       SourceRange                    The source range for the expansion.
+Args             [(name)|(number)|<(token name)>[, ...]]              const MacroArgs                The argument tokens. Names and numbers are literal, everything else is of the form '<' tokenName '>'.
+==============   ==================================================   ============================== ======================================================================================================
+
+Example:::
+
+  - Callback: MacroExpands\r
+    MacroNameTok: X_IMPL\r
+    MacroDirective: MD_Define\r
+    Range: [(nonfile), (nonfile)]\r
+    Args: [a <plus> y, b]\r
+
+`MacroDefined <http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a8448fc9f96f22ad1b93ff393cffc5a76>`_ Callback
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+MacroDefined is called when a macro definition is seen.
+
+Argument descriptions:
+
+==============   ==================================================   ============================== ==============================================================
+Argument Name    Argument Value Syntax                                Clang C++ Type                 Description           
+==============   ==================================================   ============================== ==============================================================
+MacroNameTok     (token)                                              const Token                    The macro name token.
+MacroDirective   (MD_Define|MD_Undefine|MD_Visibility)                const MacroDirective           The kind of macro directive from the MacroDirective structure.
+==============   ==================================================   ============================== ==============================================================
+
+Example:::
+
+  - Callback: MacroDefined\r
+    MacroNameTok: X_IMPL\r
+    MacroDirective: MD_Define\r
+
+`MacroUndefined <http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#acb80fc6171a839db8e290945bf2c9d7a>`_ Callback
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+MacroUndefined is called when a macro #undef is seen.
+
+Argument descriptions:
+
+==============   ==================================================   ============================== ==============================================================
+Argument Name    Argument Value Syntax                                Clang C++ Type                 Description           
+==============   ==================================================   ============================== ==============================================================
+MacroNameTok     (token)                                              const Token                    The macro name token.
+MacroDirective   (MD_Define|MD_Undefine|MD_Visibility)                const MacroDirective           The kind of macro directive from the MacroDirective structure.
+==============   ==================================================   ============================== ==============================================================
+
+Example:::
+
+  - Callback: MacroUndefined\r
+    MacroNameTok: X_IMPL\r
+    MacroDirective: MD_Define\r
+
+`Defined <http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a3cc2a644533d0e4088a13d2baf90db94>`_ Callback
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Defined is called when the 'defined' operator is seen.
+
+Argument descriptions:
+
+==============   ==================================================   ============================== ==============================================================
+Argument Name    Argument Value Syntax                                Clang C++ Type                 Description           
+==============   ==================================================   ============================== ==============================================================
+MacroNameTok     (token)                                              const Token                    The macro name token.
+MacroDirective   (MD_Define|MD_Undefine|MD_Visibility)                const MacroDirective           The kind of macro directive from the MacroDirective structure.
+Range            ["(file):(line):(col)", "(file):(line):(col)"]       SourceRange                    The source range for the directive.
+==============   ==================================================   ============================== ==============================================================
+
+Example:::
+
+  - Callback: Defined\r
+    MacroNameTok: MACRO
+    MacroDirective: (null)
+    Range: ["D:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-macro.cpp:8:5", "D:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-macro.cpp:8:19"]
+
+`SourceRangeSkipped <http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#abdb4ebe11610f079ac33515965794b46>`_ Callback
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+SourceRangeSkipped is called when a source range is skipped.
+
+Argument descriptions:
+
+==============   ==================================================   ============================== =========================
+Argument Name    Argument Value Syntax                                Clang C++ Type                 Description           
+==============   ==================================================   ============================== =========================
+Range            ["(file):(line):(col)", "(file):(line):(col)"]       SourceRange                    The source range skipped.
+==============   ==================================================   ============================== =========================
+
+Example:::
+
+  - Callback: SourceRangeSkipped\r
+    Range: [":/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-macro.cpp:8:2", ":/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-macro.cpp:9:2"]
+
+`If <http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a645edcb0d6becbc6f256f02fd1287778>`_ Callback
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+If is called when an #if is seen.
+
+Argument descriptions:
+
+==============   ==================================================   ============================== ===================================
+Argument Name    Argument Value Syntax                                Clang C++ Type                 Description           
+==============   ==================================================   ============================== ===================================
+Loc              "(file):(line):(col)"                                SourceLocation                 The location of the directive.
+ConditionRange   ["(file):(line):(col)", "(file):(line):(col)"]       SourceRange                    The source range for the condition.
+ConditionValue   (true|false)                                         bool                           The condition value.
+==============   ==================================================   ============================== ===================================
+
+Example:::
+
+  - Callback: If\r
+    Loc: "D:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-macro.cpp:8:2"\r
+    ConditionRange: ["D:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-macro.cpp:8:4", "D:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-macro.cpp:9:1"]\r
+    ConditionValue: false\r
+
+`Elif <http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a180c9e106a28d60a6112e16b1bb8302a>`_ Callback
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Elif is called when an #elif is seen.
+
+Argument descriptions:
+
+==============   ==================================================   ============================== ===================================
+Argument Name    Argument Value Syntax                                Clang C++ Type                 Description           
+==============   ==================================================   ============================== ===================================
+Loc              "(file):(line):(col)"                                SourceLocation                 The location of the directive.
+ConditionRange   ["(file):(line):(col)", "(file):(line):(col)"]       SourceRange                    The source range for the condition.
+ConditionValue   (true|false)                                         bool                           The condition value.
+IfLoc            "(file):(line):(col)"                                SourceLocation                 The location of the directive.
+==============   ==================================================   ============================== ===================================
+
+Example:::
+
+  - Callback: Elif\r
+    Loc: "D:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-macro.cpp:10:2"\r
+    ConditionRange: ["D:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-macro.cpp:10:4", "D:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-macro.cpp:11:1"]\r
+    ConditionValue: false\r
+    IfLoc: "D:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-macro.cpp:8:2"\r
+
+`Ifdef <http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a0ce79575dda307784fd51a6dd4eec33d>`_ Callback
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Ifdef is called when an #ifdef is seen.
+
+Argument descriptions:
+
+==============   ==================================================   ============================== ==============================================================
+Argument Name    Argument Value Syntax                                Clang C++ Type                 Description           
+==============   ==================================================   ============================== ==============================================================
+Loc              "(file):(line):(col)"                                SourceLocation                 The location of the directive.
+MacroNameTok     (token)                                              const Token                    The macro name token.
+MacroDirective   (MD_Define|MD_Undefine|MD_Visibility)                const MacroDirective           The kind of macro directive from the MacroDirective structure.
+==============   ==================================================   ============================== ==============================================================
+
+Example:::
+
+  - Callback: Ifdef\r
+    Loc: "D:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-conditional.cpp:3:1"
+    MacroNameTok: MACRO\r
+    MacroDirective: MD_Define\r
+
+`Ifndef <http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#a767af69f1cdcc4cd880fa2ebf77ad3ad>`_ Callback
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Ifndef is called when an #ifndef is seen.
+
+Argument descriptions:
+
+==============   ==================================================   ============================== ==============================================================
+Argument Name    Argument Value Syntax                                Clang C++ Type                 Description           
+==============   ==================================================   ============================== ==============================================================
+Loc              "(file):(line):(col)"                                SourceLocation                 The location of the directive.
+MacroNameTok     (token)                                              const Token                    The macro name token.
+MacroDirective   (MD_Define|MD_Undefine|MD_Visibility)                const MacroDirective           The kind of macro directive from the MacroDirective structure.
+==============   ==================================================   ============================== ==============================================================
+
+Example:::
+
+  - Callback: Ifndef\r
+    Loc: "D:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-conditional.cpp:3:1"
+    MacroNameTok: MACRO\r
+    MacroDirective: MD_Define\r
+
+`Else <http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#ad57f91b6d9c3cbcca326a2bfb49e0314>`_ Callback
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Else is called when an #else is seen.
+
+Argument descriptions:
+
+==============   ==================================================   ============================== ===================================
+Argument Name    Argument Value Syntax                                Clang C++ Type                 Description           
+==============   ==================================================   ============================== ===================================
+Loc              "(file):(line):(col)"                                SourceLocation                 The location of the else directive.
+IfLoc            "(file):(line):(col)"                                SourceLocation                 The location of the if directive.
+==============   ==================================================   ============================== ===================================
+
+Example:::
+
+  - Callback: Else\r
+    Loc: "D:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-macro.cpp:10:2"\r
+    IfLoc: "D:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-macro.cpp:8:2"\r
+
+`Endif <http://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html#afc62ca1401125f516d58b1629a2093ce>`_ Callback
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Endif is called when an #endif is seen.
+
+Argument descriptions:
+
+==============   ==================================================   ============================== ====================================
+Argument Name    Argument Value Syntax                                Clang C++ Type                 Description           
+==============   ==================================================   ============================== ====================================
+Loc              "(file):(line):(col)"                                SourceLocation                 The location of the endif directive.
+IfLoc            "(file):(line):(col)"                                SourceLocation                 The location of the if directive.
+==============   ==================================================   ============================== ====================================
+
+Example:::
+
+  - Callback: Endif\r
+    Loc: "D:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-macro.cpp:10:2"\r
+    IfLoc: "D:/Clang/llvm/tools/clang/tools/extra/test/pp-trace/pp-trace-macro.cpp:8:2"\r
+
+Building pp-trace
+=================
+
+To build from source:
+
+1. Read `Getting Started with the LLVM System`_ and `Clang Tools
+   Documentation`_ for information on getting sources for LLVM, Clang, and
+   Clang Extra Tools.
+
+2. `Getting Started with the LLVM System`_ and `Building LLVM with CMake`_ give
+   directions for how to build. With sources all checked out into the
+   right place the LLVM build will build Clang Extra Tools and their
+   dependencies automatically.
+
+   * If using CMake, you can also use the ``pp-trace`` target to build
+     just the pp-trace tool and its dependencies.
+
+.. _Getting Started with the LLVM System: http://llvm.org/docs/GettingStarted.html
+.. _Building LLVM with CMake: http://llvm.org/docs/CMake.html
+.. _Clang Tools Documentation: http://clang.llvm.org/docs/ClangTools.html
+
diff --git a/include-fixer/CMakeLists.txt b/include-fixer/CMakeLists.txt
new file mode 100644 (file)
index 0000000..3981fcd
--- /dev/null
@@ -0,0 +1,28 @@
+set(LLVM_LINK_COMPONENTS
+  support
+  )
+
+add_clang_library(clangIncludeFixer
+  IncludeFixer.cpp
+  IncludeFixerContext.cpp
+  InMemorySymbolIndex.cpp
+  FuzzySymbolIndex.cpp
+  SymbolIndexManager.cpp
+  YamlSymbolIndex.cpp
+
+  LINK_LIBS
+  clangAST
+  clangBasic
+  clangFormat
+  clangFrontend
+  clangLex
+  clangParse
+  clangSema
+  clangTooling
+  clangToolingCore
+  findAllSymbols
+  )
+
+add_subdirectory(plugin)
+add_subdirectory(tool)
+add_subdirectory(find-all-symbols)
diff --git a/include-fixer/FuzzySymbolIndex.cpp b/include-fixer/FuzzySymbolIndex.cpp
new file mode 100644 (file)
index 0000000..d91f455
--- /dev/null
@@ -0,0 +1,143 @@
+//===--- FuzzySymbolIndex.cpp - Lookup symbols for autocomplete -*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+#include "FuzzySymbolIndex.h"
+#include "llvm/Support/Regex.h"
+
+using clang::find_all_symbols::SymbolAndSignals;
+using llvm::StringRef;
+
+namespace clang {
+namespace include_fixer {
+namespace {
+
+class MemSymbolIndex : public FuzzySymbolIndex {
+public:
+  MemSymbolIndex(std::vector<SymbolAndSignals> Symbols) {
+    for (auto &Symbol : Symbols) {
+      auto Tokens = tokenize(Symbol.Symbol.getName());
+      this->Symbols.emplace_back(
+          StringRef(llvm::join(Tokens.begin(), Tokens.end(), " ")),
+          std::move(Symbol));
+    }
+  }
+
+  std::vector<SymbolAndSignals> search(StringRef Query) override {
+    auto Tokens = tokenize(Query);
+    llvm::Regex Pattern("^" + queryRegexp(Tokens));
+    std::vector<SymbolAndSignals> Results;
+    for (const Entry &E : Symbols)
+      if (Pattern.match(E.first))
+        Results.push_back(E.second);
+    return Results;
+  }
+
+private:
+  using Entry = std::pair<llvm::SmallString<32>, SymbolAndSignals>;
+  std::vector<Entry> Symbols;
+};
+
+// Helpers for tokenize state machine.
+enum TokenizeState {
+  EMPTY,      // No pending characters.
+  ONE_BIG,    // Read one uppercase letter, could be WORD or Word.
+  BIG_WORD,   // Reading an uppercase WORD.
+  SMALL_WORD, // Reading a lowercase word.
+  NUMBER      // Reading a number.
+};
+
+enum CharType { UPPER, LOWER, DIGIT, MISC };
+CharType classify(char c) {
+  if (isupper(c))
+    return UPPER;
+  if (islower(c))
+    return LOWER;
+  if (isdigit(c))
+    return DIGIT;
+  return MISC;
+}
+
+} // namespace
+
+std::vector<std::string> FuzzySymbolIndex::tokenize(StringRef Text) {
+  std::vector<std::string> Result;
+  // State describes the treatment of text from Start to I.
+  // Once text is Flush()ed into Result, we're done with it and advance Start.
+  TokenizeState State = EMPTY;
+  size_t Start = 0;
+  auto Flush = [&](size_t End) {
+    if (State != EMPTY) {
+      Result.push_back(Text.substr(Start, End - Start).lower());
+      State = EMPTY;
+    }
+    Start = End;
+  };
+  for (size_t I = 0; I < Text.size(); ++I) {
+    CharType Type = classify(Text[I]);
+    if (Type == MISC)
+      Flush(I);
+    else if (Type == LOWER)
+      switch (State) {
+      case BIG_WORD:
+        Flush(I - 1); // FOOBar: first token is FOO, not FOOB.
+        LLVM_FALLTHROUGH;
+      case ONE_BIG:
+        State = SMALL_WORD;
+        LLVM_FALLTHROUGH;
+      case SMALL_WORD:
+        break;
+      default:
+        Flush(I);
+        State = SMALL_WORD;
+      }
+    else if (Type == UPPER)
+      switch (State) {
+      case ONE_BIG:
+        State = BIG_WORD;
+        LLVM_FALLTHROUGH;
+      case BIG_WORD:
+        break;
+      default:
+        Flush(I);
+        State = ONE_BIG;
+      }
+    else if (Type == DIGIT && State != NUMBER) {
+      Flush(I);
+      State = NUMBER;
+    }
+  }
+  Flush(Text.size());
+  return Result;
+}
+
+std::string
+FuzzySymbolIndex::queryRegexp(const std::vector<std::string> &Tokens) {
+  std::string Result;
+  for (size_t I = 0; I < Tokens.size(); ++I) {
+    if (I)
+      Result.append("[[:alnum:]]* ");
+    for (size_t J = 0; J < Tokens[I].size(); ++J) {
+      if (J)
+        Result.append("([[:alnum:]]* )?");
+      Result.push_back(Tokens[I][J]);
+    }
+  }
+  return Result;
+}
+
+llvm::Expected<std::unique_ptr<FuzzySymbolIndex>>
+FuzzySymbolIndex::createFromYAML(StringRef FilePath) {
+  auto Buffer = llvm::MemoryBuffer::getFile(FilePath);
+  if (!Buffer)
+    return llvm::errorCodeToError(Buffer.getError());
+  return llvm::make_unique<MemSymbolIndex>(
+      find_all_symbols::ReadSymbolInfosFromYAML(Buffer.get()->getBuffer()));
+}
+
+} // namespace include_fixer
+} // namespace clang
diff --git a/include-fixer/FuzzySymbolIndex.h b/include-fixer/FuzzySymbolIndex.h
new file mode 100644 (file)
index 0000000..245191e
--- /dev/null
@@ -0,0 +1,55 @@
+//===--- FuzzySymbolIndex.h - Lookup symbols for autocomplete ---*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_FUZZY_SYMBOL_INDEX_H
+#define LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_FUZZY_SYMBOL_INDEX_H
+
+#include "SymbolIndex.h"
+#include "find-all-symbols/SymbolInfo.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Error.h"
+#include <string>
+#include <vector>
+
+namespace clang {
+namespace include_fixer {
+
+// A FuzzySymbolIndex retrieves top-level symbols matching a query string.
+//
+// It refines the contract of SymbolIndex::search to do fuzzy matching:
+// - symbol names are tokenized: "unique ptr", "string ref".
+// - query must match prefixes of symbol tokens: [upt]
+// - if the query has multiple tokens, splits must match: [StR], not [STr].
+// Helpers for tokenization and regex matching are provided.
+//
+// Implementations may choose to truncate results, refuse short queries, etc.
+class FuzzySymbolIndex : public SymbolIndex {
+public:
+  // Loads the specified include-fixer database and returns an index serving it.
+  static llvm::Expected<std::unique_ptr<FuzzySymbolIndex>>
+  createFromYAML(llvm::StringRef File);
+
+  // Helpers for implementing indexes:
+
+  // Transforms a symbol name or query into a sequence of tokens.
+  // - URLHandlerCallback --> [url, handler, callback]
+  // - snake_case11 --> [snake, case, 11]
+  // - _WTF$ --> [wtf]
+  static std::vector<std::string> tokenize(llvm::StringRef Text);
+
+  // Transforms query tokens into an unanchored regexp to match symbol tokens.
+  // - [fe f] --> /f(\w* )?e\w* f/, matches [fee fie foe].
+  static std::string queryRegexp(const std::vector<std::string> &Tokens);
+};
+
+} // namespace include_fixer
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_FUZZY_SYMBOL_INDEX_H
diff --git a/include-fixer/InMemorySymbolIndex.cpp b/include-fixer/InMemorySymbolIndex.cpp
new file mode 100644 (file)
index 0000000..14753bd
--- /dev/null
@@ -0,0 +1,32 @@
+//===-- InMemorySymbolIndex.cpp--------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "InMemorySymbolIndex.h"
+
+using clang::find_all_symbols::SymbolAndSignals;
+
+namespace clang {
+namespace include_fixer {
+
+InMemorySymbolIndex::InMemorySymbolIndex(
+    const std::vector<SymbolAndSignals> &Symbols) {
+  for (const auto &Symbol : Symbols)
+    LookupTable[Symbol.Symbol.getName()].push_back(Symbol);
+}
+
+std::vector<SymbolAndSignals>
+InMemorySymbolIndex::search(llvm::StringRef Identifier) {
+  auto I = LookupTable.find(Identifier);
+  if (I != LookupTable.end())
+    return I->second;
+  return {};
+}
+
+} // namespace include_fixer
+} // namespace clang
diff --git a/include-fixer/InMemorySymbolIndex.h b/include-fixer/InMemorySymbolIndex.h
new file mode 100644 (file)
index 0000000..0e1310e
--- /dev/null
@@ -0,0 +1,38 @@
+//===-- InMemorySymbolIndex.h -----------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+#ifndef LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_INMEMORYSYMBOLINDEX_H
+#define LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_INMEMORYSYMBOLINDEX_H
+
+#include "SymbolIndex.h"
+#include <map>
+#include <string>
+#include <vector>
+
+namespace clang {
+namespace include_fixer {
+
+/// Xref database with fixed content.
+class InMemorySymbolIndex : public SymbolIndex {
+public:
+  InMemorySymbolIndex(
+      const std::vector<find_all_symbols::SymbolAndSignals> &Symbols);
+
+  std::vector<find_all_symbols::SymbolAndSignals>
+  search(llvm::StringRef Identifier) override;
+
+private:
+  std::map<std::string, std::vector<find_all_symbols::SymbolAndSignals>>
+      LookupTable;
+};
+
+} // namespace include_fixer
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_INMEMORYSYMBOLINDEX_H
diff --git a/include-fixer/IncludeFixer.cpp b/include-fixer/IncludeFixer.cpp
new file mode 100644 (file)
index 0000000..e68231b
--- /dev/null
@@ -0,0 +1,443 @@
+//===-- IncludeFixer.cpp - Include inserter based on sema callbacks -------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "IncludeFixer.h"
+#include "clang/Format/Format.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Lex/HeaderSearch.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Parse/ParseAST.h"
+#include "clang/Sema/Sema.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/raw_ostream.h"
+
+#define DEBUG_TYPE "include-fixer"
+
+using namespace clang;
+
+namespace clang {
+namespace include_fixer {
+namespace {
+/// Manages the parse, gathers include suggestions.
+class Action : public clang::ASTFrontendAction {
+public:
+  explicit Action(SymbolIndexManager &SymbolIndexMgr, bool MinimizeIncludePaths)
+      : SemaSource(SymbolIndexMgr, MinimizeIncludePaths,
+                   /*GenerateDiagnostics=*/false) {}
+
+  std::unique_ptr<clang::ASTConsumer>
+  CreateASTConsumer(clang::CompilerInstance &Compiler,
+                    StringRef InFile) override {
+    SemaSource.setFilePath(InFile);
+    return llvm::make_unique<clang::ASTConsumer>();
+  }
+
+  void ExecuteAction() override {
+    clang::CompilerInstance *Compiler = &getCompilerInstance();
+    assert(!Compiler->hasSema() && "CI already has Sema");
+
+    // Set up our hooks into sema and parse the AST.
+    if (hasCodeCompletionSupport() &&
+        !Compiler->getFrontendOpts().CodeCompletionAt.FileName.empty())
+      Compiler->createCodeCompletionConsumer();
+
+    clang::CodeCompleteConsumer *CompletionConsumer = nullptr;
+    if (Compiler->hasCodeCompletionConsumer())
+      CompletionConsumer = &Compiler->getCodeCompletionConsumer();
+
+    Compiler->createSema(getTranslationUnitKind(), CompletionConsumer);
+    SemaSource.setCompilerInstance(Compiler);
+    Compiler->getSema().addExternalSource(&SemaSource);
+
+    clang::ParseAST(Compiler->getSema(), Compiler->getFrontendOpts().ShowStats,
+                    Compiler->getFrontendOpts().SkipFunctionBodies);
+  }
+
+  IncludeFixerContext
+  getIncludeFixerContext(const clang::SourceManager &SourceManager,
+                         clang::HeaderSearch &HeaderSearch) const {
+    return SemaSource.getIncludeFixerContext(SourceManager, HeaderSearch,
+                                             SemaSource.getMatchedSymbols());
+  }
+
+private:
+  IncludeFixerSemaSource SemaSource;
+};
+
+} // namespace
+
+IncludeFixerActionFactory::IncludeFixerActionFactory(
+    SymbolIndexManager &SymbolIndexMgr,
+    std::vector<IncludeFixerContext> &Contexts, StringRef StyleName,
+    bool MinimizeIncludePaths)
+    : SymbolIndexMgr(SymbolIndexMgr), Contexts(Contexts),
+      MinimizeIncludePaths(MinimizeIncludePaths) {}
+
+IncludeFixerActionFactory::~IncludeFixerActionFactory() = default;
+
+bool IncludeFixerActionFactory::runInvocation(
+    std::shared_ptr<clang::CompilerInvocation> Invocation,
+    clang::FileManager *Files,
+    std::shared_ptr<clang::PCHContainerOperations> PCHContainerOps,
+    clang::DiagnosticConsumer *Diagnostics) {
+  assert(Invocation->getFrontendOpts().Inputs.size() == 1);
+
+  // Set up Clang.
+  clang::CompilerInstance Compiler(PCHContainerOps);
+  Compiler.setInvocation(std::move(Invocation));
+  Compiler.setFileManager(Files);
+
+  // Create the compiler's actual diagnostics engine. We want to drop all
+  // diagnostics here.
+  Compiler.createDiagnostics(new clang::IgnoringDiagConsumer,
+                             /*ShouldOwnClient=*/true);
+  Compiler.createSourceManager(*Files);
+
+  // We abort on fatal errors so don't let a large number of errors become
+  // fatal. A missing #include can cause thousands of errors.
+  Compiler.getDiagnostics().setErrorLimit(0);
+
+  // Run the parser, gather missing includes.
+  auto ScopedToolAction =
+      llvm::make_unique<Action>(SymbolIndexMgr, MinimizeIncludePaths);
+  Compiler.ExecuteAction(*ScopedToolAction);
+
+  Contexts.push_back(ScopedToolAction->getIncludeFixerContext(
+      Compiler.getSourceManager(),
+      Compiler.getPreprocessor().getHeaderSearchInfo()));
+
+  // Technically this should only return true if we're sure that we have a
+  // parseable file. We don't know that though. Only inform users of fatal
+  // errors.
+  return !Compiler.getDiagnostics().hasFatalErrorOccurred();
+}
+
+static bool addDiagnosticsForContext(TypoCorrection &Correction,
+                                     const IncludeFixerContext &Context,
+                                     StringRef Code, SourceLocation StartOfFile,
+                                     ASTContext &Ctx) {
+  auto Reps = createIncludeFixerReplacements(
+      Code, Context, format::getLLVMStyle(), /*AddQualifiers=*/false);
+  if (!Reps || Reps->size() != 1)
+    return false;
+
+  unsigned DiagID = Ctx.getDiagnostics().getCustomDiagID(
+      DiagnosticsEngine::Note, "Add '#include %0' to provide the missing "
+                               "declaration [clang-include-fixer]");
+
+  // FIXME: Currently we only generate a diagnostic for the first header. Give
+  // the user choices.
+  const tooling::Replacement &Placed = *Reps->begin();
+
+  auto Begin = StartOfFile.getLocWithOffset(Placed.getOffset());
+  auto End = Begin.getLocWithOffset(std::max(0, (int)Placed.getLength() - 1));
+  PartialDiagnostic PD(DiagID, Ctx.getDiagAllocator());
+  PD << Context.getHeaderInfos().front().Header
+     << FixItHint::CreateReplacement(CharSourceRange::getCharRange(Begin, End),
+                                     Placed.getReplacementText());
+  Correction.addExtraDiagnostic(std::move(PD));
+  return true;
+}
+
+/// Callback for incomplete types. If we encounter a forward declaration we
+/// have the fully qualified name ready. Just query that.
+bool IncludeFixerSemaSource::MaybeDiagnoseMissingCompleteType(
+    clang::SourceLocation Loc, clang::QualType T) {
+  // Ignore spurious callbacks from SFINAE contexts.
+  if (CI->getSema().isSFINAEContext())
+    return false;
+
+  clang::ASTContext &context = CI->getASTContext();
+  std::string QueryString = QualType(T->getUnqualifiedDesugaredType(), 0)
+                                .getAsString(context.getPrintingPolicy());
+  DEBUG(llvm::dbgs() << "Query missing complete type '" << QueryString << "'");
+  // Pass an empty range here since we don't add qualifier in this case.
+  std::vector<find_all_symbols::SymbolInfo> MatchedSymbols =
+      query(QueryString, "", tooling::Range());
+
+  if (!MatchedSymbols.empty() && GenerateDiagnostics) {
+    TypoCorrection Correction;
+    FileID FID = CI->getSourceManager().getFileID(Loc);
+    StringRef Code = CI->getSourceManager().getBufferData(FID);
+    SourceLocation StartOfFile =
+        CI->getSourceManager().getLocForStartOfFile(FID);
+    addDiagnosticsForContext(
+        Correction,
+        getIncludeFixerContext(CI->getSourceManager(),
+                               CI->getPreprocessor().getHeaderSearchInfo(),
+                               MatchedSymbols),
+        Code, StartOfFile, CI->getASTContext());
+    for (const PartialDiagnostic &PD : Correction.getExtraDiagnostics())
+      CI->getSema().Diag(Loc, PD);
+  }
+  return true;
+}
+
+/// Callback for unknown identifiers. Try to piece together as much
+/// qualification as we can get and do a query.
+clang::TypoCorrection IncludeFixerSemaSource::CorrectTypo(
+    const DeclarationNameInfo &Typo, int LookupKind, Scope *S, CXXScopeSpec *SS,
+    CorrectionCandidateCallback &CCC, DeclContext *MemberContext,
+    bool EnteringContext, const ObjCObjectPointerType *OPT) {
+  // Ignore spurious callbacks from SFINAE contexts.
+  if (CI->getSema().isSFINAEContext())
+    return clang::TypoCorrection();
+
+  // We currently ignore the unidentified symbol which is not from the
+  // main file.
+  //
+  // However, this is not always true due to templates in a non-self contained
+  // header, consider the case:
+  //
+  //   // header.h
+  //   template <typename T>
+  //   class Foo {
+  //     T t;
+  //   };
+  //
+  //   // test.cc
+  //   // We need to add <bar.h> in test.cc instead of header.h.
+  //   class Bar;
+  //   Foo<Bar> foo;
+  //
+  // FIXME: Add the missing header to the header file where the symbol comes
+  // from.
+  if (!CI->getSourceManager().isWrittenInMainFile(Typo.getLoc()))
+    return clang::TypoCorrection();
+
+  std::string TypoScopeString;
+  if (S) {
+    // FIXME: Currently we only use namespace contexts. Use other context
+    // types for query.
+    for (const auto *Context = S->getEntity(); Context;
+         Context = Context->getParent()) {
+      if (const auto *ND = dyn_cast<NamespaceDecl>(Context)) {
+        if (!ND->getName().empty())
+          TypoScopeString = ND->getNameAsString() + "::" + TypoScopeString;
+      }
+    }
+  }
+
+  auto ExtendNestedNameSpecifier = [this](CharSourceRange Range) {
+    StringRef Source =
+        Lexer::getSourceText(Range, CI->getSourceManager(), CI->getLangOpts());
+
+    // Skip forward until we find a character that's neither identifier nor
+    // colon. This is a bit of a hack around the fact that we will only get a
+    // single callback for a long nested name if a part of the beginning is
+    // unknown. For example:
+    //
+    // llvm::sys::path::parent_path(...)
+    // ^~~~  ^~~
+    //    known
+    //            ^~~~
+    //      unknown, last callback
+    //                  ^~~~~~~~~~~
+    //                  no callback
+    //
+    // With the extension we get the full nested name specifier including
+    // parent_path.
+    // FIXME: Don't rely on source text.
+    const char *End = Source.end();
+    while (isIdentifierBody(*End) || *End == ':')
+      ++End;
+
+    return std::string(Source.begin(), End);
+  };
+
+  /// If we have a scope specification, use that to get more precise results.
+  std::string QueryString;
+  tooling::Range SymbolRange;
+  const auto &SM = CI->getSourceManager();
+  auto CreateToolingRange = [&QueryString, &SM](SourceLocation BeginLoc) {
+    return tooling::Range(SM.getDecomposedLoc(BeginLoc).second,
+                          QueryString.size());
+  };
+  if (SS && SS->getRange().isValid()) {
+    auto Range = CharSourceRange::getTokenRange(SS->getRange().getBegin(),
+                                                Typo.getLoc());
+
+    QueryString = ExtendNestedNameSpecifier(Range);
+    SymbolRange = CreateToolingRange(Range.getBegin());
+  } else if (Typo.getName().isIdentifier() && !Typo.getLoc().isMacroID()) {
+    auto Range =
+        CharSourceRange::getTokenRange(Typo.getBeginLoc(), Typo.getEndLoc());
+
+    QueryString = ExtendNestedNameSpecifier(Range);
+    SymbolRange = CreateToolingRange(Range.getBegin());
+  } else {
+    QueryString = Typo.getAsString();
+    SymbolRange = CreateToolingRange(Typo.getLoc());
+  }
+
+  DEBUG(llvm::dbgs() << "TypoScopeQualifiers: " << TypoScopeString << "\n");
+  std::vector<find_all_symbols::SymbolInfo> MatchedSymbols =
+      query(QueryString, TypoScopeString, SymbolRange);
+
+  if (!MatchedSymbols.empty() && GenerateDiagnostics) {
+    TypoCorrection Correction(Typo.getName());
+    Correction.setCorrectionRange(SS, Typo);
+    FileID FID = SM.getFileID(Typo.getLoc());
+    StringRef Code = SM.getBufferData(FID);
+    SourceLocation StartOfFile = SM.getLocForStartOfFile(FID);
+    if (addDiagnosticsForContext(
+            Correction, getIncludeFixerContext(
+                            SM, CI->getPreprocessor().getHeaderSearchInfo(),
+                            MatchedSymbols),
+            Code, StartOfFile, CI->getASTContext()))
+      return Correction;
+  }
+  return TypoCorrection();
+}
+
+/// Get the minimal include for a given path.
+std::string IncludeFixerSemaSource::minimizeInclude(
+    StringRef Include, const clang::SourceManager &SourceManager,
+    clang::HeaderSearch &HeaderSearch) const {
+  if (!MinimizeIncludePaths)
+    return Include;
+
+  // Get the FileEntry for the include.
+  StringRef StrippedInclude = Include.trim("\"<>");
+  const FileEntry *Entry =
+      SourceManager.getFileManager().getFile(StrippedInclude);
+
+  // If the file doesn't exist return the path from the database.
+  // FIXME: This should never happen.
+  if (!Entry)
+    return Include;
+
+  bool IsSystem;
+  std::string Suggestion =
+      HeaderSearch.suggestPathToFileForDiagnostics(Entry, &IsSystem);
+
+  return IsSystem ? '<' + Suggestion + '>' : '"' + Suggestion + '"';
+}
+
+/// Get the include fixer context for the queried symbol.
+IncludeFixerContext IncludeFixerSemaSource::getIncludeFixerContext(
+    const clang::SourceManager &SourceManager,
+    clang::HeaderSearch &HeaderSearch,
+    ArrayRef<find_all_symbols::SymbolInfo> MatchedSymbols) const {
+  std::vector<find_all_symbols::SymbolInfo> SymbolCandidates;
+  for (const auto &Symbol : MatchedSymbols) {
+    std::string FilePath = Symbol.getFilePath().str();
+    std::string MinimizedFilePath = minimizeInclude(
+        ((FilePath[0] == '"' || FilePath[0] == '<') ? FilePath
+                                                    : "\"" + FilePath + "\""),
+        SourceManager, HeaderSearch);
+    SymbolCandidates.emplace_back(Symbol.getName(), Symbol.getSymbolKind(),
+                                  MinimizedFilePath, Symbol.getContexts());
+  }
+  return IncludeFixerContext(FilePath, QuerySymbolInfos, SymbolCandidates);
+}
+
+std::vector<find_all_symbols::SymbolInfo>
+IncludeFixerSemaSource::query(StringRef Query, StringRef ScopedQualifiers,
+                              tooling::Range Range) {
+  assert(!Query.empty() && "Empty query!");
+
+  // Save all instances of an unidentified symbol.
+  //
+  // We use conservative behavior for detecting the same unidentified symbol
+  // here. The symbols which have the same ScopedQualifier and RawIdentifier
+  // are considered equal. So that include-fixer avoids false positives, and
+  // always adds missing qualifiers to correct symbols.
+  if (!GenerateDiagnostics && !QuerySymbolInfos.empty()) {
+    if (ScopedQualifiers == QuerySymbolInfos.front().ScopedQualifiers &&
+        Query == QuerySymbolInfos.front().RawIdentifier) {
+      QuerySymbolInfos.push_back({Query.str(), ScopedQualifiers, Range});
+    }
+    return {};
+  }
+
+  DEBUG(llvm::dbgs() << "Looking up '" << Query << "' at ");
+  DEBUG(CI->getSourceManager()
+            .getLocForStartOfFile(CI->getSourceManager().getMainFileID())
+            .getLocWithOffset(Range.getOffset())
+            .print(llvm::dbgs(), CI->getSourceManager()));
+  DEBUG(llvm::dbgs() << " ...");
+  llvm::StringRef FileName = CI->getSourceManager().getFilename(
+      CI->getSourceManager().getLocForStartOfFile(
+          CI->getSourceManager().getMainFileID()));
+
+  QuerySymbolInfos.push_back({Query.str(), ScopedQualifiers, Range});
+
+  // Query the symbol based on C++ name Lookup rules.
+  // Firstly, lookup the identifier with scoped namespace contexts;
+  // If that fails, falls back to look up the identifier directly.
+  //
+  // For example:
+  //
+  // namespace a {
+  // b::foo f;
+  // }
+  //
+  // 1. lookup a::b::foo.
+  // 2. lookup b::foo.
+  std::string QueryString = ScopedQualifiers.str() + Query.str();
+  // It's unsafe to do nested search for the identifier with scoped namespace
+  // context, it might treat the identifier as a nested class of the scoped
+  // namespace.
+  std::vector<find_all_symbols::SymbolInfo> MatchedSymbols =
+      SymbolIndexMgr.search(QueryString, /*IsNestedSearch=*/false, FileName);
+  if (MatchedSymbols.empty())
+    MatchedSymbols =
+        SymbolIndexMgr.search(Query, /*IsNestedSearch=*/true, FileName);
+  DEBUG(llvm::dbgs() << "Having found " << MatchedSymbols.size()
+                     << " symbols\n");
+  // We store a copy of MatchedSymbols in a place where it's globally reachable.
+  // This is used by the standalone version of the tool.
+  this->MatchedSymbols = MatchedSymbols;
+  return MatchedSymbols;
+}
+
+llvm::Expected<tooling::Replacements> createIncludeFixerReplacements(
+    StringRef Code, const IncludeFixerContext &Context,
+    const clang::format::FormatStyle &Style, bool AddQualifiers) {
+  if (Context.getHeaderInfos().empty())
+    return tooling::Replacements();
+  StringRef FilePath = Context.getFilePath();
+  std::string IncludeName =
+      "#include " + Context.getHeaderInfos().front().Header + "\n";
+  // Create replacements for the new header.
+  clang::tooling::Replacements Insertions;
+  auto Err =
+      Insertions.add(tooling::Replacement(FilePath, UINT_MAX, 0, IncludeName));
+  if (Err)
+    return std::move(Err);
+
+  auto CleanReplaces = cleanupAroundReplacements(Code, Insertions, Style);
+  if (!CleanReplaces)
+    return CleanReplaces;
+
+  auto Replaces = std::move(*CleanReplaces);
+  if (AddQualifiers) {
+    for (const auto &Info : Context.getQuerySymbolInfos()) {
+      // Ignore the empty range.
+      if (Info.Range.getLength() > 0) {
+        auto R = tooling::Replacement(
+            {FilePath, Info.Range.getOffset(), Info.Range.getLength(),
+             Context.getHeaderInfos().front().QualifiedName});
+        auto Err = Replaces.add(R);
+        if (Err) {
+          llvm::consumeError(std::move(Err));
+          R = tooling::Replacement(
+              R.getFilePath(), Replaces.getShiftedCodePosition(R.getOffset()),
+              R.getLength(), R.getReplacementText());
+          Replaces = Replaces.merge(tooling::Replacements(R));
+        }
+      }
+    }
+  }
+  return formatReplacements(Code, Replaces, Style);
+}
+
+} // namespace include_fixer
+} // namespace clang
diff --git a/include-fixer/IncludeFixer.h b/include-fixer/IncludeFixer.h
new file mode 100644 (file)
index 0000000..13092a3
--- /dev/null
@@ -0,0 +1,158 @@
+//===-- IncludeFixer.h - Include inserter -----------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_INCLUDEFIXER_H
+#define LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_INCLUDEFIXER_H
+
+#include "IncludeFixerContext.h"
+#include "SymbolIndexManager.h"
+#include "clang/Format/Format.h"
+#include "clang/Sema/ExternalSemaSource.h"
+#include "clang/Tooling/Core/Replacement.h"
+#include "clang/Tooling/Tooling.h"
+#include <memory>
+#include <vector>
+
+namespace clang {
+
+class CompilerInvocation;
+class DiagnosticConsumer;
+class FileManager;
+class PCHContainerOperations;
+
+namespace include_fixer {
+
+class IncludeFixerActionFactory : public clang::tooling::ToolAction {
+public:
+  /// \param SymbolIndexMgr A source for matching symbols to header files.
+  /// \param Contexts The contexts for the symbols being queried.
+  /// \param StyleName Fallback style for reformatting.
+  /// \param MinimizeIncludePaths whether inserted include paths are optimized.
+  IncludeFixerActionFactory(SymbolIndexManager &SymbolIndexMgr,
+                            std::vector<IncludeFixerContext> &Contexts,
+                            StringRef StyleName,
+                            bool MinimizeIncludePaths = true);
+
+  ~IncludeFixerActionFactory() override;
+
+  bool
+  runInvocation(std::shared_ptr<clang::CompilerInvocation> Invocation,
+                clang::FileManager *Files,
+                std::shared_ptr<clang::PCHContainerOperations> PCHContainerOps,
+                clang::DiagnosticConsumer *Diagnostics) override;
+
+private:
+  /// The client to use to find cross-references.
+  SymbolIndexManager &SymbolIndexMgr;
+
+  /// Multiple contexts for files being processed.
+  std::vector<IncludeFixerContext> &Contexts;
+
+  /// Whether inserted include paths should be optimized.
+  bool MinimizeIncludePaths;
+
+  /// The fallback format style for formatting after insertion if no
+  /// clang-format config file was found.
+  std::string FallbackStyle;
+};
+
+/// Create replacements, which are generated by clang-format, for the
+/// missing header and mising qualifiers insertions. The function uses the
+/// first header for insertion.
+///
+/// \param Code The source code.
+/// \param Context The context which contains all information for creating
+/// include-fixer replacements.
+/// \param Style clang-format style being used.
+/// \param AddQualifiers  Whether we should add qualifiers to all instances of
+/// an unidentified symbol.
+///
+/// \return Formatted replacements for inserting, sorting headers and adding
+/// qualifiers on success; otherwise, an llvm::Error carrying llvm::StringError
+/// is returned.
+llvm::Expected<tooling::Replacements> createIncludeFixerReplacements(
+    StringRef Code, const IncludeFixerContext &Context,
+    const format::FormatStyle &Style = format::getLLVMStyle(),
+    bool AddQualifiers = true);
+
+/// Handles callbacks from sema, does the include lookup and turns it into an
+/// IncludeFixerContext.
+class IncludeFixerSemaSource : public clang::ExternalSemaSource {
+public:
+  explicit IncludeFixerSemaSource(SymbolIndexManager &SymbolIndexMgr,
+                                  bool MinimizeIncludePaths,
+                                  bool GenerateDiagnostics)
+      : SymbolIndexMgr(SymbolIndexMgr),
+        MinimizeIncludePaths(MinimizeIncludePaths),
+        GenerateDiagnostics(GenerateDiagnostics) {}
+
+  void setCompilerInstance(CompilerInstance *CI) { this->CI = CI; }
+  void setFilePath(StringRef FilePath) { this->FilePath = FilePath; }
+
+  /// Callback for incomplete types. If we encounter a forward declaration we
+  /// have the fully qualified name ready. Just query that.
+  bool MaybeDiagnoseMissingCompleteType(clang::SourceLocation Loc,
+                                        clang::QualType T) override;
+
+  /// Callback for unknown identifiers. Try to piece together as much
+  /// qualification as we can get and do a query.
+  clang::TypoCorrection CorrectTypo(const DeclarationNameInfo &Typo,
+                                    int LookupKind, Scope *S, CXXScopeSpec *SS,
+                                    CorrectionCandidateCallback &CCC,
+                                    DeclContext *MemberContext,
+                                    bool EnteringContext,
+                                    const ObjCObjectPointerType *OPT) override;
+
+  /// Get the minimal include for a given path.
+  std::string minimizeInclude(StringRef Include,
+                              const clang::SourceManager &SourceManager,
+                              clang::HeaderSearch &HeaderSearch) const;
+
+  /// Get the include fixer context for the queried symbol.
+  IncludeFixerContext getIncludeFixerContext(
+      const clang::SourceManager &SourceManager,
+      clang::HeaderSearch &HeaderSearch,
+      ArrayRef<find_all_symbols::SymbolInfo> MatchedSymbols) const;
+
+  /// Get the global matched symbols.
+  ArrayRef<find_all_symbols::SymbolInfo> getMatchedSymbols() const {
+    return MatchedSymbols;
+  }
+
+private:
+  /// Query the database for a given identifier.
+  std::vector<find_all_symbols::SymbolInfo>
+  query(StringRef Query, StringRef ScopedQualifiers, tooling::Range Range);
+
+  CompilerInstance *CI;
+
+  /// The client to use to find cross-references.
+  SymbolIndexManager &SymbolIndexMgr;
+
+  /// The information of the symbols being queried.
+  std::vector<IncludeFixerContext::QuerySymbolInfo> QuerySymbolInfos;
+
+  /// All symbol candidates which match QuerySymbol. We only include the first
+  /// discovered identifier to avoid getting caught in results from error
+  /// recovery.
+  std::vector<find_all_symbols::SymbolInfo> MatchedSymbols;
+
+  /// The file path to the file being processed.
+  std::string FilePath;
+
+  /// Whether we should use the smallest possible include path.
+  bool MinimizeIncludePaths = true;
+
+  /// Whether we should generate diagnostics with fixits for missing symbols.
+  bool GenerateDiagnostics = false;
+};
+} // namespace include_fixer
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_INCLUDEFIXER_H
diff --git a/include-fixer/IncludeFixerContext.cpp b/include-fixer/IncludeFixerContext.cpp
new file mode 100644 (file)
index 0000000..8106e20
--- /dev/null
@@ -0,0 +1,116 @@
+//===-- IncludeFixerContext.cpp - Include fixer context ---------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "IncludeFixerContext.h"
+#include <algorithm>
+
+namespace clang {
+namespace include_fixer {
+
+namespace {
+
+// Splits a multiply qualified names (e.g. a::b::c).
+llvm::SmallVector<llvm::StringRef, 8>
+SplitQualifiers(llvm::StringRef StringQualifiers) {
+  llvm::SmallVector<llvm::StringRef, 8> Qualifiers;
+  StringQualifiers.split(Qualifiers, "::");
+  return Qualifiers;
+}
+
+std::string createQualifiedNameForReplacement(
+    llvm::StringRef RawSymbolName,
+    llvm::StringRef SymbolScopedQualifiersName,
+    const find_all_symbols::SymbolInfo &MatchedSymbol) {
+  // No need to add missing qualifiers if SymbolIndentifer has a global scope
+  // operator "::".
+  if (RawSymbolName.startswith("::"))
+    return RawSymbolName;
+
+  std::string QualifiedName = MatchedSymbol.getQualifiedName();
+
+  // For nested classes, the qualified name constructed from database misses
+  // some stripped qualifiers, because when we search a symbol in database,
+  // we strip qualifiers from the end until we find a result. So append the
+  // missing stripped qualifiers here.
+  //
+  // Get stripped qualifiers.
+  auto SymbolQualifiers = SplitQualifiers(RawSymbolName);
+  std::string StrippedQualifiers;
+  while (!SymbolQualifiers.empty() &&
+         !llvm::StringRef(QualifiedName).endswith(SymbolQualifiers.back())) {
+    StrippedQualifiers =
+        "::" + SymbolQualifiers.back().str() + StrippedQualifiers;
+    SymbolQualifiers.pop_back();
+  }
+  // Append the missing stripped qualifiers.
+  std::string FullyQualifiedName = QualifiedName + StrippedQualifiers;
+
+  // Try to find and skip the common prefix qualifiers.
+  auto FullySymbolQualifiers = SplitQualifiers(FullyQualifiedName);
+  auto ScopedQualifiers = SplitQualifiers(SymbolScopedQualifiersName);
+  auto FullySymbolQualifiersIter = FullySymbolQualifiers.begin();
+  auto SymbolScopedQualifiersIter = ScopedQualifiers.begin();
+  while (FullySymbolQualifiersIter != FullySymbolQualifiers.end() &&
+         SymbolScopedQualifiersIter != ScopedQualifiers.end()) {
+    if (*FullySymbolQualifiersIter != *SymbolScopedQualifiersIter)
+      break;
+    ++FullySymbolQualifiersIter;
+    ++SymbolScopedQualifiersIter;
+  }
+  std::string Result;
+  for (; FullySymbolQualifiersIter != FullySymbolQualifiers.end();
+       ++FullySymbolQualifiersIter) {
+    if (!Result.empty())
+      Result += "::";
+    Result += *FullySymbolQualifiersIter;
+  }
+  return Result;
+}
+
+} // anonymous namespace
+
+IncludeFixerContext::IncludeFixerContext(
+    StringRef FilePath, std::vector<QuerySymbolInfo> QuerySymbols,
+    std::vector<find_all_symbols::SymbolInfo> Symbols)
+    : FilePath(FilePath), QuerySymbolInfos(std::move(QuerySymbols)),
+      MatchedSymbols(std::move(Symbols)) {
+  // Remove replicated QuerySymbolInfos with the same range.
+  //
+  // QuerySymbolInfos may contain replicated elements. Because CorrectTypo
+  // callback doesn't always work as we expected. In somecases, it will be
+  // triggered at the same position or unidentified symbol multiple times.
+  std::sort(QuerySymbolInfos.begin(), QuerySymbolInfos.end(),
+            [&](const QuerySymbolInfo &A, const QuerySymbolInfo &B) {
+              return std::make_pair(A.Range.getOffset(), A.Range.getLength()) <
+                     std::make_pair(B.Range.getOffset(), B.Range.getLength());
+            });
+  QuerySymbolInfos.erase(
+      std::unique(QuerySymbolInfos.begin(), QuerySymbolInfos.end(),
+                  [](const QuerySymbolInfo &A, const QuerySymbolInfo &B) {
+                    return A.Range == B.Range;
+                  }),
+      QuerySymbolInfos.end());
+  for (const auto &Symbol : MatchedSymbols) {
+    HeaderInfos.push_back(
+        {Symbol.getFilePath().str(),
+         createQualifiedNameForReplacement(
+             QuerySymbolInfos.front().RawIdentifier,
+             QuerySymbolInfos.front().ScopedQualifiers, Symbol)});
+  }
+  // Deduplicate header infos.
+  HeaderInfos.erase(std::unique(HeaderInfos.begin(), HeaderInfos.end(),
+                                [](const HeaderInfo &A, const HeaderInfo &B) {
+                                  return A.Header == B.Header &&
+                                         A.QualifiedName == B.QualifiedName;
+                                }),
+                    HeaderInfos.end());
+}
+
+} // include_fixer
+} // clang
diff --git a/include-fixer/IncludeFixerContext.h b/include-fixer/IncludeFixerContext.h
new file mode 100644 (file)
index 0000000..3c44574
--- /dev/null
@@ -0,0 +1,95 @@
+//===-- IncludeFixerContext.h - Include fixer context -----------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_INCLUDEFIXERCONTEXT_H
+#define LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_INCLUDEFIXERCONTEXT_H
+
+#include "find-all-symbols/SymbolInfo.h"
+#include "clang/Tooling/Core/Replacement.h"
+#include <string>
+#include <vector>
+
+namespace clang {
+namespace include_fixer {
+
+/// \brief A context for a file being processed. It includes all query
+/// information, e.g. symbols being queried in database, all header candidates.
+class IncludeFixerContext {
+public:
+  struct HeaderInfo {
+    /// \brief The header where QualifiedName comes from.
+    std::string Header;
+    /// \brief A symbol name with completed namespace qualifiers which will
+    /// replace the original symbol.
+    std::string QualifiedName;
+  };
+
+  struct QuerySymbolInfo {
+    /// \brief The raw symbol name being queried in database. This name might
+    /// miss some namespace qualifiers, and will be replaced by a fully
+    /// qualified one.
+    std::string RawIdentifier;
+
+    /// \brief The qualifiers of the scope in which SymbolIdentifier lookup
+    /// occurs. It is represented as a sequence of names and scope resolution
+    /// operatiors ::, ending with a scope resolution operator (e.g. a::b::).
+    /// Empty if SymbolIdentifier is not in a specific scope.
+    std::string ScopedQualifiers;
+
+    /// \brief The replacement range of RawIdentifier.
+    tooling::Range Range;
+  };
+
+  IncludeFixerContext() = default;
+  IncludeFixerContext(StringRef FilePath,
+                      std::vector<QuerySymbolInfo> QuerySymbols,
+                      std::vector<find_all_symbols::SymbolInfo> Symbols);
+
+  /// \brief Get symbol name.
+  llvm::StringRef getSymbolIdentifier() const {
+    return QuerySymbolInfos.front().RawIdentifier;
+  }
+
+  /// \brief Get replacement range of the symbol.
+  tooling::Range getSymbolRange() const {
+    return QuerySymbolInfos.front().Range;
+  }
+
+  /// \brief Get the file path to the file being processed.
+  StringRef getFilePath() const { return FilePath; }
+
+  /// \brief Get header information.
+  const std::vector<HeaderInfo> &getHeaderInfos() const { return HeaderInfos; }
+
+  /// \brief Get information of symbols being querid.
+  const std::vector<QuerySymbolInfo> &getQuerySymbolInfos() const {
+    return QuerySymbolInfos;
+  }
+
+private:
+  friend struct llvm::yaml::MappingTraits<IncludeFixerContext>;
+
+  /// \brief The file path to the file being processed.
+  std::string FilePath;
+
+  /// \brief All instances of an unidentified symbol being queried.
+  std::vector<QuerySymbolInfo> QuerySymbolInfos;
+
+  /// \brief The symbol candidates which match SymbolIdentifier. The symbols are
+  /// sorted in a descending order based on the popularity info in SymbolInfo.
+  std::vector<find_all_symbols::SymbolInfo> MatchedSymbols;
+
+  /// \brief The header information.
+  std::vector<HeaderInfo> HeaderInfos;
+};
+
+} // namespace include_fixer
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_INCLUDEFIXERCONTEXT_H
diff --git a/include-fixer/SymbolIndex.h b/include-fixer/SymbolIndex.h
new file mode 100644 (file)
index 0000000..f3e6ecd
--- /dev/null
@@ -0,0 +1,38 @@
+//===-- SymbolIndex.h - Interface for symbol-header matching ----*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_SYMBOLINDEX_H
+#define LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_SYMBOLINDEX_H
+
+#include "find-all-symbols/SymbolInfo.h"
+#include "llvm/ADT/StringRef.h"
+#include <vector>
+
+namespace clang {
+namespace include_fixer {
+
+/// This class provides an interface for finding all `SymbolInfo`s corresponding
+/// to a symbol name from a symbol database.
+class SymbolIndex {
+public:
+  virtual ~SymbolIndex() = default;
+
+  /// Search for all `SymbolInfo`s corresponding to an identifier.
+  /// \param Identifier The unqualified identifier being searched for.
+  /// \returns A list of `SymbolInfo` candidates.
+  // FIXME: Expose the type name so we can also insert using declarations (or
+  // fix the usage)
+  virtual std::vector<find_all_symbols::SymbolAndSignals>
+  search(llvm::StringRef Identifier) = 0;
+};
+
+} // namespace include_fixer
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_SYMBOLINDEX_H
diff --git a/include-fixer/SymbolIndexManager.cpp b/include-fixer/SymbolIndexManager.cpp
new file mode 100644 (file)
index 0000000..31ecc61
--- /dev/null
@@ -0,0 +1,159 @@
+//===-- SymbolIndexManager.cpp - Managing multiple SymbolIndices-*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "SymbolIndexManager.h"
+#include "find-all-symbols/SymbolInfo.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/Path.h"
+
+#define DEBUG_TYPE "include-fixer"
+
+namespace clang {
+namespace include_fixer {
+
+using find_all_symbols::SymbolInfo;
+using find_all_symbols::SymbolAndSignals;
+
+// Calculate a score based on whether we think the given header is closely
+// related to the given source file.
+static double similarityScore(llvm::StringRef FileName,
+                              llvm::StringRef Header) {
+  // Compute the maximum number of common path segements between Header and
+  // a suffix of FileName.
+  // We do not do a full longest common substring computation, as Header
+  // specifies the path we would directly #include, so we assume it is rooted
+  // relatively to a subproject of the repository.
+  int MaxSegments = 1;
+  for (auto FileI = llvm::sys::path::begin(FileName),
+            FileE = llvm::sys::path::end(FileName);
+       FileI != FileE; ++FileI) {
+    int Segments = 0;
+    for (auto HeaderI = llvm::sys::path::begin(Header),
+              HeaderE = llvm::sys::path::end(Header), I = FileI;
+         HeaderI != HeaderE && *I == *HeaderI && I != FileE; ++I, ++HeaderI) {
+      ++Segments;
+    }
+    MaxSegments = std::max(Segments, MaxSegments);
+  }
+  return MaxSegments;
+}
+
+static void rank(std::vector<SymbolAndSignals> &Symbols,
+                 llvm::StringRef FileName) {
+  llvm::DenseMap<llvm::StringRef, double> Score;
+  for (const auto &Symbol : Symbols) {
+    // Calculate a score from the similarity of the header the symbol is in
+    // with the current file and the popularity of the symbol.
+    double NewScore = similarityScore(FileName, Symbol.Symbol.getFilePath()) *
+                      (1.0 + std::log2(1 + Symbol.Signals.Seen));
+    double &S = Score[Symbol.Symbol.getFilePath()];
+    S = std::max(S, NewScore);
+  }
+  // Sort by the gathered scores. Use file name as a tie breaker so we can
+  // deduplicate.
+  std::sort(Symbols.begin(), Symbols.end(),
+            [&](const SymbolAndSignals &A, const SymbolAndSignals &B) {
+              auto AS = Score[A.Symbol.getFilePath()];
+              auto BS = Score[B.Symbol.getFilePath()];
+              if (AS != BS)
+                return AS > BS;
+              return A.Symbol.getFilePath() < B.Symbol.getFilePath();
+            });
+}
+
+std::vector<find_all_symbols::SymbolInfo>
+SymbolIndexManager::search(llvm::StringRef Identifier,
+                           bool IsNestedSearch,
+                           llvm::StringRef FileName) const {
+  // The identifier may be fully qualified, so split it and get all the context
+  // names.
+  llvm::SmallVector<llvm::StringRef, 8> Names;
+  Identifier.split(Names, "::");
+
+  bool IsFullyQualified = false;
+  if (Identifier.startswith("::")) {
+    Names.erase(Names.begin()); // Drop first (empty) element.
+    IsFullyQualified = true;
+  }
+
+  // As long as we don't find a result keep stripping name parts from the end.
+  // This is to support nested classes which aren't recorded in the database.
+  // Eventually we will either hit a class (namespaces aren't in the database
+  // either) and can report that result.
+  bool TookPrefix = false;
+  std::vector<SymbolAndSignals> MatchedSymbols;
+  do {
+    std::vector<SymbolAndSignals> Symbols;
+    for (const auto &DB : SymbolIndices) {
+      auto Res = DB.get()->search(Names.back());
+      Symbols.insert(Symbols.end(), Res.begin(), Res.end());
+    }
+
+    DEBUG(llvm::dbgs() << "Searching " << Names.back() << "... got "
+                       << Symbols.size() << " results...\n");
+
+    for (auto &SymAndSig : Symbols) {
+      const SymbolInfo &Symbol = SymAndSig.Symbol;
+      // Match the identifier name without qualifier.
+      bool IsMatched = true;
+      auto SymbolContext = Symbol.getContexts().begin();
+      auto IdentiferContext = Names.rbegin() + 1; // Skip identifier name.
+      // Match the remaining context names.
+      while (IdentiferContext != Names.rend() &&
+             SymbolContext != Symbol.getContexts().end()) {
+        if (SymbolContext->second == *IdentiferContext) {
+          ++IdentiferContext;
+          ++SymbolContext;
+        } else if (SymbolContext->first ==
+                   find_all_symbols::SymbolInfo::ContextType::EnumDecl) {
+          // Skip non-scoped enum context.
+          ++SymbolContext;
+        } else {
+          IsMatched = false;
+          break;
+        }
+      }
+
+      // If the name was qualified we only want to add results if we evaluated
+      // all contexts.
+      if (IsFullyQualified)
+        IsMatched &= (SymbolContext == Symbol.getContexts().end());
+
+      // FIXME: Support full match. At this point, we only find symbols in
+      // database which end with the same contexts with the identifier.
+      if (IsMatched && IdentiferContext == Names.rend()) {
+        // If we're in a situation where we took a prefix but the thing we
+        // found couldn't possibly have a nested member ignore it.
+        if (TookPrefix &&
+            (Symbol.getSymbolKind() == SymbolInfo::SymbolKind::Function ||
+             Symbol.getSymbolKind() == SymbolInfo::SymbolKind::Variable ||
+             Symbol.getSymbolKind() ==
+                 SymbolInfo::SymbolKind::EnumConstantDecl ||
+             Symbol.getSymbolKind() == SymbolInfo::SymbolKind::Macro))
+          continue;
+
+        MatchedSymbols.push_back(std::move(SymAndSig));
+      }
+    }
+    Names.pop_back();
+    TookPrefix = true;
+  } while (MatchedSymbols.empty() && !Names.empty() && IsNestedSearch);
+
+  rank(MatchedSymbols, FileName);
+  // Strip signals, they are no longer needed.
+  std::vector<SymbolInfo> Res;
+  for (auto &SymAndSig : MatchedSymbols)
+    Res.push_back(std::move(SymAndSig.Symbol));
+  return Res;
+}
+
+} // namespace include_fixer
+} // namespace clang
diff --git a/include-fixer/SymbolIndexManager.h b/include-fixer/SymbolIndexManager.h
new file mode 100644 (file)
index 0000000..12963dd
--- /dev/null
@@ -0,0 +1,66 @@
+//===-- SymbolIndexManager.h - Managing multiple SymbolIndices --*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_SYMBOLINDEXMANAGER_H
+#define LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_SYMBOLINDEXMANAGER_H
+
+#include "SymbolIndex.h"
+#include "find-all-symbols/SymbolInfo.h"
+#include "llvm/ADT/StringRef.h"
+
+#ifdef _MSC_VER
+// Disable warnings from ppltasks.h transitively included by <future>.
+#pragma warning(push)
+#pragma warning(disable:4530)
+#endif
+
+#include <future>
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+namespace clang {
+namespace include_fixer {
+
+/// This class provides an interface for finding the header files corresponding
+/// to an identifier in the source code from multiple symbol databases.
+class SymbolIndexManager {
+public:
+  void addSymbolIndex(std::function<std::unique_ptr<SymbolIndex>()> F) {
+#if LLVM_ENABLE_THREADS
+    auto Strategy = std::launch::async;
+#else
+    auto Strategy = std::launch::deferred;
+#endif
+    SymbolIndices.push_back(std::async(Strategy, F));
+  }
+
+  /// Search for header files to be included for an identifier.
+  /// \param Identifier The identifier being searched for. May or may not be
+  ///                   fully qualified.
+  /// \param IsNestedSearch Whether searching nested classes. If true, the
+  ///        method tries to strip identifier name parts from the end until it
+  ///        finds the corresponding candidates in database (e.g for identifier
+  ///        "b::foo", the method will try to find "b" if it fails to find
+  ///        "b::foo").
+  ///
+  /// \returns A list of symbol candidates.
+  std::vector<find_all_symbols::SymbolInfo>
+  search(llvm::StringRef Identifier, bool IsNestedSearch = true,
+         llvm::StringRef FileName = "") const;
+
+private:
+  std::vector<std::shared_future<std::unique_ptr<SymbolIndex>>> SymbolIndices;
+};
+
+} // namespace include_fixer
+} // namespace clang
+
+#endif
diff --git a/include-fixer/YamlSymbolIndex.cpp b/include-fixer/YamlSymbolIndex.cpp
new file mode 100644 (file)
index 0000000..f3f2d5a
--- /dev/null
@@ -0,0 +1,61 @@
+//===-- YamlSymbolIndex.cpp -----------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "YamlSymbolIndex.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include <string>
+#include <vector>
+
+using clang::find_all_symbols::SymbolInfo;
+using clang::find_all_symbols::SymbolAndSignals;
+
+namespace clang {
+namespace include_fixer {
+
+llvm::ErrorOr<std::unique_ptr<YamlSymbolIndex>>
+YamlSymbolIndex::createFromFile(llvm::StringRef FilePath) {
+  auto Buffer = llvm::MemoryBuffer::getFile(FilePath);
+  if (!Buffer)
+    return Buffer.getError();
+
+  return std::unique_ptr<YamlSymbolIndex>(new YamlSymbolIndex(
+      find_all_symbols::ReadSymbolInfosFromYAML(Buffer.get()->getBuffer())));
+}
+
+llvm::ErrorOr<std::unique_ptr<YamlSymbolIndex>>
+YamlSymbolIndex::createFromDirectory(llvm::StringRef Directory,
+                                     llvm::StringRef Name) {
+  // Walk upwards from Directory, looking for files.
+  for (llvm::SmallString<128> PathStorage = Directory; !Directory.empty();
+       Directory = llvm::sys::path::parent_path(Directory)) {
+    assert(Directory.size() <= PathStorage.size());
+    PathStorage.resize(Directory.size()); // Shrink to parent.
+    llvm::sys::path::append(PathStorage, Name);
+    if (auto DB = createFromFile(PathStorage))
+      return DB;
+  }
+  return llvm::make_error_code(llvm::errc::no_such_file_or_directory);
+}
+
+std::vector<SymbolAndSignals>
+YamlSymbolIndex::search(llvm::StringRef Identifier) {
+  std::vector<SymbolAndSignals> Results;
+  for (const auto &Symbol : Symbols) {
+    if (Symbol.Symbol.getName() == Identifier)
+      Results.push_back(Symbol);
+  }
+  return Results;
+}
+
+} // namespace include_fixer
+} // namespace clang
diff --git a/include-fixer/YamlSymbolIndex.h b/include-fixer/YamlSymbolIndex.h
new file mode 100644 (file)
index 0000000..d5d699a
--- /dev/null
@@ -0,0 +1,46 @@
+//===-- YamlSymbolIndex.h ---------------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_YAMLSYMBOLINDEX_H
+#define LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_YAMLSYMBOLINDEX_H
+
+#include "SymbolIndex.h"
+#include "find-all-symbols/SymbolInfo.h"
+#include "llvm/Support/ErrorOr.h"
+#include <map>
+#include <vector>
+
+namespace clang {
+namespace include_fixer {
+
+/// Yaml format database.
+class YamlSymbolIndex : public SymbolIndex {
+public:
+  /// Create a new Yaml db from a file.
+  static llvm::ErrorOr<std::unique_ptr<YamlSymbolIndex>>
+  createFromFile(llvm::StringRef FilePath);
+  /// Look for a file called \c Name in \c Directory and all parent directories.
+  static llvm::ErrorOr<std::unique_ptr<YamlSymbolIndex>>
+  createFromDirectory(llvm::StringRef Directory, llvm::StringRef Name);
+
+  std::vector<find_all_symbols::SymbolAndSignals>
+  search(llvm::StringRef Identifier) override;
+
+private:
+  explicit YamlSymbolIndex(
+      std::vector<find_all_symbols::SymbolAndSignals> Symbols)
+      : Symbols(std::move(Symbols)) {}
+
+  std::vector<find_all_symbols::SymbolAndSignals> Symbols;
+};
+
+} // namespace include_fixer
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_YAMLSYMBOLINDEX_H
diff --git a/include-fixer/find-all-symbols/CMakeLists.txt b/include-fixer/find-all-symbols/CMakeLists.txt
new file mode 100644 (file)
index 0000000..c5fe19b
--- /dev/null
@@ -0,0 +1,24 @@
+set(LLVM_LINK_COMPONENTS
+  Support
+  )
+
+add_clang_library(findAllSymbols
+  FindAllSymbols.cpp
+  FindAllSymbolsAction.cpp
+  FindAllMacros.cpp
+  HeaderMapCollector.cpp
+  PathConfig.cpp
+  PragmaCommentHandler.cpp
+  STLPostfixHeaderMap.cpp
+  SymbolInfo.cpp
+
+  LINK_LIBS
+  clangAST
+  clangASTMatchers
+  clangBasic
+  clangFrontend
+  clangLex
+  clangTooling
+  )
+
+add_subdirectory(tool)
diff --git a/include-fixer/find-all-symbols/FindAllMacros.cpp b/include-fixer/find-all-symbols/FindAllMacros.cpp
new file mode 100644 (file)
index 0000000..3dc2b96
--- /dev/null
@@ -0,0 +1,70 @@
+//===-- FindAllMacros.cpp - find all macros ---------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "FindAllMacros.h"
+#include "HeaderMapCollector.h"
+#include "PathConfig.h"
+#include "SymbolInfo.h"
+#include "clang/Basic/IdentifierTable.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Lex/MacroInfo.h"
+#include "clang/Lex/Token.h"
+#include "llvm/Support/Path.h"
+
+namespace clang {
+namespace find_all_symbols {
+
+llvm::Optional<SymbolInfo>
+FindAllMacros::CreateMacroSymbol(const Token &MacroNameTok,
+                                 const MacroInfo *info) {
+  std::string FilePath =
+      getIncludePath(*SM, info->getDefinitionLoc(), Collector);
+  if (FilePath.empty())
+    return llvm::None;
+  return SymbolInfo(MacroNameTok.getIdentifierInfo()->getName(),
+                    SymbolInfo::SymbolKind::Macro, FilePath, {});
+}
+
+void FindAllMacros::MacroDefined(const Token &MacroNameTok,
+                                 const MacroDirective *MD) {
+  if (auto Symbol = CreateMacroSymbol(MacroNameTok, MD->getMacroInfo()))
+    ++FileSymbols[*Symbol].Seen;
+}
+
+void FindAllMacros::MacroUsed(const Token &Name, const MacroDefinition &MD) {
+  if (!MD || !SM->isInMainFile(SM->getExpansionLoc(Name.getLocation())))
+    return;
+  if (auto Symbol = CreateMacroSymbol(Name, MD.getMacroInfo()))
+    ++FileSymbols[*Symbol].Used;
+}
+
+void FindAllMacros::MacroExpands(const Token &MacroNameTok,
+                                 const MacroDefinition &MD, SourceRange Range,
+                                 const MacroArgs *Args) {
+  MacroUsed(MacroNameTok, MD);
+}
+
+void FindAllMacros::Ifdef(SourceLocation Loc, const Token &MacroNameTok,
+                          const MacroDefinition &MD) {
+  MacroUsed(MacroNameTok, MD);
+}
+
+void FindAllMacros::Ifndef(SourceLocation Loc, const Token &MacroNameTok,
+                           const MacroDefinition &MD) {
+  MacroUsed(MacroNameTok, MD);
+}
+
+void FindAllMacros::EndOfMainFile() {
+  Reporter->reportSymbols(SM->getFileEntryForID(SM->getMainFileID())->getName(),
+                          FileSymbols);
+  FileSymbols.clear();
+}
+
+} // namespace find_all_symbols
+} // namespace clang
diff --git a/include-fixer/find-all-symbols/FindAllMacros.h b/include-fixer/find-all-symbols/FindAllMacros.h
new file mode 100644 (file)
index 0000000..10b4a69
--- /dev/null
@@ -0,0 +1,65 @@
+//===-- FindAllMacros.h - find all macros -----------------------*- C++ -*-===//
+//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_FIND_ALL_MACROS_H
+#define LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_FIND_ALL_MACROS_H
+
+#include "SymbolInfo.h"
+#include "SymbolReporter.h"
+#include "clang/Lex/PPCallbacks.h"
+
+namespace clang {
+class MacroInfo;
+namespace find_all_symbols {
+
+class HeaderMapCollector;
+
+/// \brief A preprocessor that collects all macro symbols.
+/// The contexts of a macro will be ignored since they are not available during
+/// preprocessing period.
+class FindAllMacros : public clang::PPCallbacks {
+public:
+  explicit FindAllMacros(SymbolReporter *Reporter, SourceManager *SM,
+                         HeaderMapCollector *Collector = nullptr)
+      : Reporter(Reporter), SM(SM), Collector(Collector) {}
+
+  void MacroDefined(const Token &MacroNameTok,
+                    const MacroDirective *MD) override;
+
+  void MacroExpands(const Token &MacroNameTok, const MacroDefinition &MD,
+                    SourceRange Range, const MacroArgs *Args) override;
+
+  void Ifdef(SourceLocation Loc, const Token &MacroNameTok,
+             const MacroDefinition &MD) override;
+
+  void Ifndef(SourceLocation Loc, const Token &MacroNameTok,
+              const MacroDefinition &MD) override;
+
+  void EndOfMainFile() override;
+
+private:
+  llvm::Optional<SymbolInfo> CreateMacroSymbol(const Token &MacroNameTok,
+                                               const MacroInfo *MD);
+  // Not a callback, just a common path for all usage types.
+  void MacroUsed(const Token &Name, const MacroDefinition &MD);
+
+  SymbolInfo::SignalMap FileSymbols;
+  // Reporter for SymbolInfo.
+  SymbolReporter *const Reporter;
+  SourceManager *const SM;
+  // A remapping header file collector allowing clients to include a different
+  // header.
+  HeaderMapCollector *const Collector;
+};
+
+} // namespace find_all_symbols
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_FIND_ALL_MACROS_H
diff --git a/include-fixer/find-all-symbols/FindAllSymbols.cpp b/include-fixer/find-all-symbols/FindAllSymbols.cpp
new file mode 100644 (file)
index 0000000..cf8ce4e
--- /dev/null
@@ -0,0 +1,268 @@
+//===-- FindAllSymbols.cpp - find all symbols--------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "FindAllSymbols.h"
+#include "HeaderMapCollector.h"
+#include "PathConfig.h"
+#include "SymbolInfo.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/Type.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/Support/FileSystem.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace find_all_symbols {
+namespace {
+
+AST_MATCHER(EnumConstantDecl, isInScopedEnum) {
+  if (const auto *ED = dyn_cast<EnumDecl>(Node.getDeclContext()))
+    return ED->isScoped();
+  return false;
+}
+
+AST_POLYMORPHIC_MATCHER(isFullySpecialized,
+                        AST_POLYMORPHIC_SUPPORTED_TYPES(FunctionDecl, VarDecl,
+                                                        CXXRecordDecl)) {
+  if (Node.getTemplateSpecializationKind() == TSK_ExplicitSpecialization) {
+    bool IsPartialSpecialization =
+        llvm::isa<VarTemplatePartialSpecializationDecl>(Node) ||
+        llvm::isa<ClassTemplatePartialSpecializationDecl>(Node);
+    return !IsPartialSpecialization;
+  }
+  return false;
+}
+
+std::vector<SymbolInfo::Context> GetContexts(const NamedDecl *ND) {
+  std::vector<SymbolInfo::Context> Contexts;
+  for (const auto *Context = ND->getDeclContext(); Context;
+       Context = Context->getParent()) {
+    if (llvm::isa<TranslationUnitDecl>(Context) ||
+        llvm::isa<LinkageSpecDecl>(Context))
+      break;
+
+    assert(llvm::isa<NamedDecl>(Context) &&
+           "Expect Context to be a NamedDecl");
+    if (const auto *NSD = dyn_cast<NamespaceDecl>(Context)) {
+      if (!NSD->isInlineNamespace())
+        Contexts.emplace_back(SymbolInfo::ContextType::Namespace,
+                              NSD->getName().str());
+    } else if (const auto *ED = dyn_cast<EnumDecl>(Context)) {
+      Contexts.emplace_back(SymbolInfo::ContextType::EnumDecl,
+                            ED->getName().str());
+    } else {
+      const auto *RD = cast<RecordDecl>(Context);
+      Contexts.emplace_back(SymbolInfo::ContextType::Record,
+                            RD->getName().str());
+    }
+  }
+  return Contexts;
+}
+
+llvm::Optional<SymbolInfo>
+CreateSymbolInfo(const NamedDecl *ND, const SourceManager &SM,
+                 const HeaderMapCollector *Collector) {
+  SymbolInfo::SymbolKind Type;
+  if (llvm::isa<VarDecl>(ND)) {
+    Type = SymbolInfo::SymbolKind::Variable;
+  } else if (llvm::isa<FunctionDecl>(ND)) {
+    Type = SymbolInfo::SymbolKind::Function;
+  } else if (llvm::isa<TypedefNameDecl>(ND)) {
+    Type = SymbolInfo::SymbolKind::TypedefName;
+  } else if (llvm::isa<EnumConstantDecl>(ND)) {
+    Type = SymbolInfo::SymbolKind::EnumConstantDecl;
+  } else if (llvm::isa<EnumDecl>(ND)) {
+    Type = SymbolInfo::SymbolKind::EnumDecl;
+    // Ignore anonymous enum declarations.
+    if (ND->getName().empty())
+      return llvm::None;
+  } else {
+    assert(llvm::isa<RecordDecl>(ND) &&
+           "Matched decl must be one of VarDecl, "
+           "FunctionDecl, TypedefNameDecl, EnumConstantDecl, "
+           "EnumDecl and RecordDecl!");
+    // C-style record decl can have empty name, e.g "struct { ... } var;".
+    if (ND->getName().empty())
+      return llvm::None;
+    Type = SymbolInfo::SymbolKind::Class;
+  }
+
+  SourceLocation Loc = SM.getExpansionLoc(ND->getLocation());
+  if (!Loc.isValid()) {
+    llvm::errs() << "Declaration " << ND->getNameAsString() << "("
+                 << ND->getDeclKindName()
+                 << ") has invalid declaration location.";
+    return llvm::None;
+  }
+
+  std::string FilePath = getIncludePath(SM, Loc, Collector);
+  if (FilePath.empty()) return llvm::None;
+
+  return SymbolInfo(ND->getNameAsString(), Type, FilePath, GetContexts(ND));
+}
+
+} // namespace
+
+void FindAllSymbols::registerMatchers(MatchFinder *MatchFinder) {
+  // FIXME: Handle specialization.
+  auto IsInSpecialization = hasAncestor(
+      decl(anyOf(cxxRecordDecl(isExplicitTemplateSpecialization()),
+                 functionDecl(isExplicitTemplateSpecialization()))));
+
+  // Matchers for both C and C++.
+  // We only match symbols from header files, i.e. not from main files (see
+  // function's comment for detailed explanation).
+  auto CommonFilter =
+      allOf(unless(isImplicit()), unless(isExpansionInMainFile()));
+
+  auto HasNSOrTUCtxMatcher =
+      hasDeclContext(anyOf(namespaceDecl(), translationUnitDecl()));
+
+  // We need seperate rules for C record types and C++ record types since some
+  // template related matchers are inapplicable on C record declarations.
+  //
+  // Matchers specific to C++ code.
+  // All declarations should be in namespace or translation unit.
+  auto CCMatcher =
+      allOf(HasNSOrTUCtxMatcher, unless(IsInSpecialization),
+            unless(ast_matchers::isTemplateInstantiation()),
+            unless(isInstantiated()), unless(isFullySpecialized()));
+
+  // Matchers specific to code in extern "C" {...}.
+  auto ExternCMatcher = hasDeclContext(linkageSpecDecl());
+
+  // Matchers for variable declarations.
+  //
+  // In most cases, `ParmVarDecl` is filtered out by hasDeclContext(...)
+  // matcher since the declaration context is usually `MethodDecl`. However,
+  // this assumption does not hold for parameters of a function pointer
+  // parameter.
+  // For example, consider a function declaration:
+  //        void Func(void (*)(float), int);
+  // The float parameter of the function pointer has an empty name, and its
+  // declaration context is an anonymous namespace; therefore, it won't be
+  // filtered out by our matchers above.
+  auto Vars = varDecl(CommonFilter, anyOf(ExternCMatcher, CCMatcher),
+                      unless(parmVarDecl()));
+
+  // Matchers for C-style record declarations in extern "C" {...}.
+  auto CRecords = recordDecl(CommonFilter, ExternCMatcher, isDefinition());
+  // Matchers for C++ record declarations.
+  auto CXXRecords = cxxRecordDecl(CommonFilter, CCMatcher, isDefinition());
+
+  // Matchers for function declarations.
+  // We want to exclude friend declaration, but the `DeclContext` of a friend
+  // function declaration is not the class in which it is declared, so we need
+  // to explicitly check if the parent is a `friendDecl`.
+  auto Functions = functionDecl(CommonFilter, unless(hasParent(friendDecl())),
+                                anyOf(ExternCMatcher, CCMatcher));
+
+  // Matcher for typedef and type alias declarations.
+  //
+  // typedef and type alias can come from C-style headers and C++ headers.
+  // For C-style headers, `DeclContxet` can be either `TranslationUnitDecl`
+  // or `LinkageSpecDecl`.
+  // For C++ headers, `DeclContext ` can be either `TranslationUnitDecl`
+  // or `NamespaceDecl`.
+  // With the following context matcher, we can match `typedefNameDecl` from
+  // both C-style headers and C++ headers (except for those in classes).
+  // "cc_matchers" are not included since template-related matchers are not
+  // applicable on `TypedefNameDecl`.
+  auto Typedefs =
+      typedefNameDecl(CommonFilter, anyOf(HasNSOrTUCtxMatcher,
+                                          hasDeclContext(linkageSpecDecl())));
+
+  // Matchers for enum declarations.
+  auto Enums = enumDecl(CommonFilter, isDefinition(),
+                        anyOf(HasNSOrTUCtxMatcher, ExternCMatcher));
+
+  // Matchers for enum constant declarations.
+  // We only match the enum constants in non-scoped enum declarations which are
+  // inside toplevel translation unit or a namespace.
+  auto EnumConstants = enumConstantDecl(
+      CommonFilter, unless(isInScopedEnum()),
+      anyOf(hasDeclContext(enumDecl(HasNSOrTUCtxMatcher)), ExternCMatcher));
+
+  // Most of the time we care about all matchable decls, or all types.
+  auto Types = namedDecl(anyOf(CRecords, CXXRecords, Enums));
+  auto Decls = namedDecl(anyOf(CRecords, CXXRecords, Enums, Typedefs, Vars,
+                               EnumConstants, Functions));
+
+  // We want eligible decls bound to "decl"...
+  MatchFinder->addMatcher(Decls.bind("decl"), this);
+
+  // ... and all uses of them bound to "use". These have many cases:
+  // Uses of values/functions: these generate a declRefExpr.
+  MatchFinder->addMatcher(
+      declRefExpr(isExpansionInMainFile(), to(Decls.bind("use"))), this);
+  // Uses of function templates:
+  MatchFinder->addMatcher(
+      declRefExpr(isExpansionInMainFile(),
+                  to(functionDecl(hasParent(
+                      functionTemplateDecl(has(Functions.bind("use"))))))),
+      this);
+
+  // Uses of most types: just look at what the typeLoc refers to.
+  MatchFinder->addMatcher(
+      typeLoc(isExpansionInMainFile(),
+              loc(qualType(hasDeclaration(Types.bind("use"))))),
+      this);
+  // Uses of typedefs: these are often transparent to hasDeclaration, so we need
+  // to handle them explicitly.
+  MatchFinder->addMatcher(
+      typeLoc(isExpansionInMainFile(),
+              loc(typedefType(hasDeclaration(Typedefs.bind("use"))))),
+      this);
+  // Uses of class templates:
+  // The typeLoc names the templateSpecializationType. Its declaration is the
+  // ClassTemplateDecl, which contains the CXXRecordDecl we want.
+  MatchFinder->addMatcher(
+      typeLoc(isExpansionInMainFile(),
+              loc(templateSpecializationType(hasDeclaration(
+                  classTemplateDecl(has(CXXRecords.bind("use"))))))),
+      this);
+}
+
+void FindAllSymbols::run(const MatchFinder::MatchResult &Result) {
+  // Ignore Results in failing TUs.
+  if (Result.Context->getDiagnostics().hasErrorOccurred()) {
+    return;
+  }
+
+  SymbolInfo::Signals Signals;
+  const NamedDecl *ND;
+  if ((ND = Result.Nodes.getNodeAs<NamedDecl>("use")))
+    Signals.Used = 1;
+  else if ((ND = Result.Nodes.getNodeAs<NamedDecl>("decl")))
+    Signals.Seen = 1;
+  else
+    assert(false && "Must match a NamedDecl!");
+
+  const SourceManager *SM = Result.SourceManager;
+  if (auto Symbol = CreateSymbolInfo(ND, *SM, Collector)) {
+    Filename = SM->getFileEntryForID(SM->getMainFileID())->getName();
+    FileSymbols[*Symbol] += Signals;
+  }
+}
+
+void FindAllSymbols::onEndOfTranslationUnit() {
+  if (Filename != "") {
+    Reporter->reportSymbols(Filename, FileSymbols);
+    FileSymbols.clear();
+    Filename = "";
+  }
+}
+
+} // namespace find_all_symbols
+} // namespace clang
diff --git a/include-fixer/find-all-symbols/FindAllSymbols.h b/include-fixer/find-all-symbols/FindAllSymbols.h
new file mode 100644 (file)
index 0000000..fca849f
--- /dev/null
@@ -0,0 +1,63 @@
+//===-- FindAllSymbols.h - find all symbols----------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_SYMBOL_MATCHER_H
+#define LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_SYMBOL_MATCHER_H
+
+#include "SymbolInfo.h"
+#include "SymbolReporter.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include <string>
+
+namespace clang {
+namespace find_all_symbols {
+
+class HeaderMapCollector;
+
+/// \brief FindAllSymbols collects all classes, free standing functions and
+/// global variables with some extra information such as the path of the header
+/// file, the namespaces they are contained in, the type of variables and the
+/// parameter types of functions.
+///
+/// NOTE:
+///   - Symbols declared in main files are not collected since they can not be
+///   included.
+///   - Member functions are not collected because accessing them must go
+///   through the class. #include fixer only needs the class name to find
+///   headers.
+///
+class FindAllSymbols : public ast_matchers::MatchFinder::MatchCallback {
+public:
+  explicit FindAllSymbols(SymbolReporter *Reporter,
+                          HeaderMapCollector *Collector = nullptr)
+      : Reporter(Reporter), Collector(Collector) {}
+
+  void registerMatchers(ast_matchers::MatchFinder *MatchFinder);
+
+  void run(const ast_matchers::MatchFinder::MatchResult &result) override;
+
+protected:
+  void onEndOfTranslationUnit() override;
+
+private:
+  // Current source file being processed, filled by first symbol found.
+  std::string Filename;
+  // Findings for the current source file, flushed on onEndOfTranslationUnit.
+  SymbolInfo::SignalMap FileSymbols;
+  // Reporter for SymbolInfo.
+  SymbolReporter *const Reporter;
+  // A remapping header file collector allowing clients include a different
+  // header.
+  HeaderMapCollector *const Collector;
+};
+
+} // namespace find_all_symbols
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_SYMBOL_MATCHER_H
diff --git a/include-fixer/find-all-symbols/FindAllSymbolsAction.cpp b/include-fixer/find-all-symbols/FindAllSymbolsAction.cpp
new file mode 100644 (file)
index 0000000..bc00547
--- /dev/null
@@ -0,0 +1,37 @@
+//===-- FindAllSymbolsAction.cpp - find all symbols action --------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "FindAllSymbolsAction.h"
+#include "FindAllMacros.h"
+#include "clang/Lex/PPCallbacks.h"
+#include "clang/Lex/Preprocessor.h"
+#include "llvm/ADT/STLExtras.h"
+
+namespace clang {
+namespace find_all_symbols {
+
+FindAllSymbolsAction::FindAllSymbolsAction(
+    SymbolReporter *Reporter,
+    const HeaderMapCollector::RegexHeaderMap *RegexHeaderMap)
+    : Reporter(Reporter), Collector(RegexHeaderMap), Handler(&Collector),
+      Matcher(Reporter, &Collector) {
+  Matcher.registerMatchers(&MatchFinder);
+}
+
+std::unique_ptr<ASTConsumer>
+FindAllSymbolsAction::CreateASTConsumer(CompilerInstance &Compiler,
+                                        StringRef InFile) {
+  Compiler.getPreprocessor().addCommentHandler(&Handler);
+  Compiler.getPreprocessor().addPPCallbacks(llvm::make_unique<FindAllMacros>(
+      Reporter, &Compiler.getSourceManager(), &Collector));
+  return MatchFinder.newASTConsumer();
+}
+
+} // namespace find_all_symbols
+} // namespace clang
diff --git a/include-fixer/find-all-symbols/FindAllSymbolsAction.h b/include-fixer/find-all-symbols/FindAllSymbolsAction.h
new file mode 100644 (file)
index 0000000..7be9fe2
--- /dev/null
@@ -0,0 +1,63 @@
+//===-- FindAllSymbolsAction.h - find all symbols action --------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_FIND_ALL_SYMBOLS_ACTION_H
+#define LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_FIND_ALL_SYMBOLS_ACTION_H
+
+#include "FindAllSymbols.h"
+#include "HeaderMapCollector.h"
+#include "PragmaCommentHandler.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendAction.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/StringRef.h"
+#include <memory>
+
+namespace clang {
+namespace find_all_symbols {
+
+class FindAllSymbolsAction : public clang::ASTFrontendAction {
+public:
+  explicit FindAllSymbolsAction(
+      SymbolReporter *Reporter,
+      const HeaderMapCollector::RegexHeaderMap *RegexHeaderMap = nullptr);
+
+  std::unique_ptr<clang::ASTConsumer>
+  CreateASTConsumer(clang::CompilerInstance &Compiler,
+                    StringRef InFile) override;
+
+private:
+  SymbolReporter *const Reporter;
+  clang::ast_matchers::MatchFinder MatchFinder;
+  HeaderMapCollector Collector;
+  PragmaCommentHandler Handler;
+  FindAllSymbols Matcher;
+};
+
+class FindAllSymbolsActionFactory : public tooling::FrontendActionFactory {
+public:
+  FindAllSymbolsActionFactory(
+      SymbolReporter *Reporter,
+      const HeaderMapCollector::RegexHeaderMap *RegexHeaderMap = nullptr)
+      : Reporter(Reporter), RegexHeaderMap(RegexHeaderMap) {}
+
+  clang::FrontendAction *create() override {
+    return new FindAllSymbolsAction(Reporter, RegexHeaderMap);
+  }
+
+private:
+  SymbolReporter *const Reporter;
+  const HeaderMapCollector::RegexHeaderMap *const RegexHeaderMap;
+};
+
+} // namespace find_all_symbols
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_FIND_ALL_SYMBOLS_ACTION_H
diff --git a/include-fixer/find-all-symbols/HeaderMapCollector.cpp b/include-fixer/find-all-symbols/HeaderMapCollector.cpp
new file mode 100644 (file)
index 0000000..38cc430
--- /dev/null
@@ -0,0 +1,33 @@
+//===-- HeaderMapCoolector.h - find all symbols------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "HeaderMapCollector.h"
+#include "llvm/Support/Regex.h"
+
+namespace clang {
+namespace find_all_symbols {
+
+llvm::StringRef
+HeaderMapCollector::getMappedHeader(llvm::StringRef Header) const {
+  auto Iter = HeaderMappingTable.find(Header);
+  if (Iter != HeaderMappingTable.end())
+    return Iter->second;
+  // If there is no complete header name mapping for this header, check the
+  // regex header mapping.
+  if (RegexHeaderMappingTable) {
+    for (const auto &Entry : *RegexHeaderMappingTable) {
+      if (llvm::Regex(Entry.first).match(Header))
+        return Entry.second;
+    }
+  }
+  return Header;
+}
+
+} // namespace find_all_symbols
+} // namespace clang
diff --git a/include-fixer/find-all-symbols/HeaderMapCollector.h b/include-fixer/find-all-symbols/HeaderMapCollector.h
new file mode 100644 (file)
index 0000000..c8ba2a8
--- /dev/null
@@ -0,0 +1,57 @@
+//===-- HeaderMapCoolector.h - find all symbols------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_HEADER_MAP_COLLECTOR_H
+#define LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_HEADER_MAP_COLLECTOR_H
+
+#include "llvm/ADT/StringMap.h"
+#include "llvm/Support/Regex.h"
+#include <string>
+#include <vector>
+
+namespace clang {
+namespace find_all_symbols {
+
+/// \brief HeaderMappCollector collects all remapping header files. This maps
+/// complete header names or header name regex patterns to header names.
+class HeaderMapCollector {
+public:
+  typedef llvm::StringMap<std::string> HeaderMap;
+  typedef std::vector<std::pair<const char *, const char *>> RegexHeaderMap;
+
+  HeaderMapCollector() : RegexHeaderMappingTable(nullptr) {}
+
+  explicit HeaderMapCollector(const RegexHeaderMap *RegexHeaderMappingTable)
+      : RegexHeaderMappingTable(RegexHeaderMappingTable) {}
+
+  void addHeaderMapping(llvm::StringRef OrignalHeaderPath,
+                        llvm::StringRef MappingHeaderPath) {
+    HeaderMappingTable[OrignalHeaderPath] = MappingHeaderPath;
+  };
+
+  /// Check if there is a mapping from \p Header or a regex pattern that matches
+  /// it to another header name.
+  /// \param Header A header name.
+  /// \return \p Header itself if there is no mapping for it; otherwise, return
+  /// a mapped header name.
+  llvm::StringRef getMappedHeader(llvm::StringRef Header) const;
+
+private:
+  /// A string-to-string map saving the mapping relationship.
+  HeaderMap HeaderMappingTable;
+
+  // A map from header patterns to header names.
+  // This is a reference to a hard-coded map.
+  const RegexHeaderMap *const RegexHeaderMappingTable;
+};
+
+} // namespace find_all_symbols
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_HEADER_MAP_COLLECTOR_H
diff --git a/include-fixer/find-all-symbols/PathConfig.cpp b/include-fixer/find-all-symbols/PathConfig.cpp
new file mode 100644 (file)
index 0000000..de799b9
--- /dev/null
@@ -0,0 +1,42 @@
+//===-- PathConfig.cpp - Process paths of symbols ---------------*- C++ -*-===//
+//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "PathConfig.h"
+#include "llvm/Support/Path.h"
+
+namespace clang {
+namespace find_all_symbols {
+
+std::string getIncludePath(const SourceManager &SM, SourceLocation Loc,
+                           const HeaderMapCollector *Collector) {
+  llvm::StringRef FilePath;
+  // Walk up the include stack to skip .inc files.
+  while (true) {
+    if (!Loc.isValid() || SM.isInMainFile(Loc))
+      return "";
+    FilePath = SM.getFilename(Loc);
+    if (FilePath.empty())
+      return "";
+    if (!FilePath.endswith(".inc"))
+      break;
+    FileID ID = SM.getFileID(Loc);
+    Loc = SM.getIncludeLoc(ID);
+  }
+
+  if (Collector)
+    FilePath = Collector->getMappedHeader(FilePath);
+  SmallString<256> CleanedFilePath = FilePath;
+  llvm::sys::path::remove_dots(CleanedFilePath, /*remove_dot_dot=*/false);
+
+  return CleanedFilePath.str();
+}
+
+} // namespace find_all_symbols
+} // namespace clang
diff --git a/include-fixer/find-all-symbols/PathConfig.h b/include-fixer/find-all-symbols/PathConfig.h
new file mode 100644 (file)
index 0000000..50de548
--- /dev/null
@@ -0,0 +1,37 @@
+//===-- PathConfig.h - Process paths of symbols -----------------*- C++ -*-===//
+//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_PATH_CONFIG_H
+#define LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_PATH_CONFIG_H
+
+#include "HeaderMapCollector.h"
+#include "clang/Basic/SourceManager.h"
+#include <string>
+
+namespace clang {
+namespace find_all_symbols {
+
+/// \brief This calculates the include path for \p Loc.
+///
+/// \param SM SourceManager.
+/// \param Loc A SourceLocation.
+/// \param Collector An optional header mapping collector.
+///
+/// \return The file path (or mapped file path if Collector is provided) of the
+/// header that includes \p Loc. If \p Loc comes from .inc header file, \p Loc
+/// is set to the location from which the .inc header file is included. If \p
+/// Loc is invalid or comes from a main file, this returns an empty string.
+std::string getIncludePath(const SourceManager &SM, SourceLocation Loc,
+                           const HeaderMapCollector *Collector = nullptr);
+
+} // namespace find_all_symbols
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_PATH_CONFIG_H
diff --git a/include-fixer/find-all-symbols/PragmaCommentHandler.cpp b/include-fixer/find-all-symbols/PragmaCommentHandler.cpp
new file mode 100644 (file)
index 0000000..0242d38
--- /dev/null
@@ -0,0 +1,37 @@
+//===-- PragmaCommentHandler.cpp - find all symbols -----------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "PragmaCommentHandler.h"
+#include "FindAllSymbols.h"
+#include "HeaderMapCollector.h"
+#include "clang/Lex/Preprocessor.h"
+#include "llvm/Support/Regex.h"
+
+namespace clang {
+namespace find_all_symbols {
+namespace {
+const char IWYUPragma[] = "// IWYU pragma: private, include ";
+} // namespace
+
+bool PragmaCommentHandler::HandleComment(Preprocessor &PP, SourceRange Range) {
+  StringRef Text =
+      Lexer::getSourceText(CharSourceRange::getCharRange(Range),
+                           PP.getSourceManager(), PP.getLangOpts());
+  size_t Pos = Text.find(IWYUPragma);
+  if (Pos == StringRef::npos)
+    return false;
+  StringRef RemappingFilePath = Text.substr(Pos + std::strlen(IWYUPragma));
+  Collector->addHeaderMapping(
+      PP.getSourceManager().getFilename(Range.getBegin()),
+      RemappingFilePath.trim("\"<>"));
+  return false;
+}
+
+} // namespace find_all_symbols
+} // namespace clang
diff --git a/include-fixer/find-all-symbols/PragmaCommentHandler.h b/include-fixer/find-all-symbols/PragmaCommentHandler.h
new file mode 100644 (file)
index 0000000..9eb4972
--- /dev/null
@@ -0,0 +1,41 @@
+//===-- PragmaCommentHandler.h - find all symbols----------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_PRAGMA_COMMENT_HANDLER_H
+#define LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_PRAGMA_COMMENT_HANDLER_H
+
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Lex/Preprocessor.h"
+#include <map>
+
+namespace clang {
+namespace find_all_symbols {
+
+class HeaderMapCollector;
+
+/// \brief PragmaCommentHandler parses pragma comment on include files to
+/// determine when we should include a different header from the header that
+/// directly defines a symbol.
+///
+/// Currently it only supports IWYU private pragma:
+/// https://github.com/include-what-you-use/include-what-you-use/blob/master/docs/IWYUPragmas.md#iwyu-pragma-private
+class PragmaCommentHandler : public clang::CommentHandler {
+public:
+  PragmaCommentHandler(HeaderMapCollector *Collector) : Collector(Collector) {}
+
+  bool HandleComment(Preprocessor &PP, SourceRange Range) override;
+
+private:
+  HeaderMapCollector *const Collector;
+};
+
+} // namespace find_all_symbols
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_PRAGMA_COMMENT_HANDLER_H
diff --git a/include-fixer/find-all-symbols/STLPostfixHeaderMap.cpp b/include-fixer/find-all-symbols/STLPostfixHeaderMap.cpp
new file mode 100644 (file)
index 0000000..5b82c3e
--- /dev/null
@@ -0,0 +1,650 @@
+//===-- STLPostfixHeaderMap.h - hardcoded STL header map --------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "STLPostfixHeaderMap.h"
+
+namespace clang {
+namespace find_all_symbols {
+
+const HeaderMapCollector::RegexHeaderMap *getSTLPostfixHeaderMap() {
+  static const HeaderMapCollector::RegexHeaderMap STLPostfixHeaderMap = {
+      {"include/__stddef_max_align_t.h$", "<cstddef>"},
+      {"include/__wmmintrin_aes.h$", "<wmmintrin.h>"},
+      {"include/__wmmintrin_pclmul.h$", "<wmmintrin.h>"},
+      {"include/adxintrin.h$", "<immintrin.h>"},
+      {"include/ammintrin.h$", "<ammintrin.h>"},
+      {"include/avx2intrin.h$", "<immintrin.h>"},
+      {"include/avx512bwintrin.h$", "<immintrin.h>"},
+      {"include/avx512cdintrin.h$", "<immintrin.h>"},
+      {"include/avx512dqintrin.h$", "<immintrin.h>"},
+      {"include/avx512erintrin.h$", "<immintrin.h>"},
+      {"include/avx512fintrin.h$", "<immintrin.h>"},
+      {"include/avx512ifmaintrin.h$", "<immintrin.h>"},
+      {"include/avx512ifmavlintrin.h$", "<immintrin.h>"},
+      {"include/avx512pfintrin.h$", "<immintrin.h>"},
+      {"include/avx512vbmiintrin.h$", "<immintrin.h>"},
+      {"include/avx512vbmivlintrin.h$", "<immintrin.h>"},
+      {"include/avx512vlbwintrin.h$", "<immintrin.h>"},
+      {"include/avx512vlcdintrin.h$", "<immintrin.h>"},
+      {"include/avx512vldqintrin.h$", "<immintrin.h>"},
+      {"include/avx512vlintrin.h$", "<immintrin.h>"},
+      {"include/avxintrin.h$", "<immintrin.h>"},
+      {"include/bmi2intrin.h$", "<x86intrin.h>"},
+      {"include/bmiintrin.h$", "<x86intrin.h>"},
+      {"include/emmintrin.h$", "<emmintrin.h>"},
+      {"include/f16cintrin.h$", "<emmintrin.h>"},
+      {"include/float.h$", "<cfloat>"},
+      {"include/fma4intrin.h$", "<x86intrin.h>"},
+      {"include/fmaintrin.h$", "<immintrin.h>"},
+      {"include/fxsrintrin.h$", "<immintrin.h>"},
+      {"include/ia32intrin.h$", "<x86intrin.h>"},
+      {"include/immintrin.h$", "<immintrin.h>"},
+      {"include/inttypes.h$", "<cinttypes>"},
+      {"include/limits.h$", "<climits>"},
+      {"include/lzcntintrin.h$", "<x86intrin.h>"},
+      {"include/mm3dnow.h$", "<mm3dnow.h>"},
+      {"include/mm_malloc.h$", "<mm_malloc.h>"},
+      {"include/mmintrin.h$", "<mmintrin>"},
+      {"include/mwaitxintrin.h$", "<x86intrin.h>"},
+      {"include/pkuintrin.h$", "<immintrin.h>"},
+      {"include/pmmintrin.h$", "<pmmintrin.h>"},
+      {"include/popcntintrin.h$", "<popcntintrin.h>"},
+      {"include/prfchwintrin.h$", "<x86intrin.h>"},
+      {"include/rdseedintrin.h$", "<x86intrin.h>"},
+      {"include/rtmintrin.h$", "<immintrin.h>"},
+      {"include/shaintrin.h$", "<immintrin.h>"},
+      {"include/smmintrin.h$", "<smmintrin.h>"},
+      {"include/stdalign.h$", "<cstdalign>"},
+      {"include/stdarg.h$", "<cstdarg>"},
+      {"include/stdbool.h$", "<cstdbool>"},
+      {"include/stddef.h$", "<cstddef>"},
+      {"include/stdint.h$", "<cstdint>"},
+      {"include/tbmintrin.h$", "<x86intrin.h>"},
+      {"include/tmmintrin.h$", "<tmmintrin.h>"},
+      {"include/wmmintrin.h$", "<wmmintrin.h>"},
+      {"include/x86intrin.h$", "<x86intrin.h>"},
+      {"include/xmmintrin.h$", "<xmmintrin.h>"},
+      {"include/xopintrin.h$", "<x86intrin.h>"},
+      {"include/xsavecintrin.h$", "<immintrin.h>"},
+      {"include/xsaveintrin.h$", "<immintrin.h>"},
+      {"include/xsaveoptintrin.h$", "<immintrin.h>"},
+      {"include/xsavesintrin.h$", "<immintrin.h>"},
+      {"include/xtestintrin.h$", "<immintrin.h>"},
+      {"include/_G_config.h$", "<cstdio>"},
+      {"include/assert.h$", "<cassert>"},
+      {"algorithm$", "<algorithm>"},
+      {"array$", "<array>"},
+      {"atomic$", "<atomic>"},
+      {"backward/auto_ptr.h$", "<memory>"},
+      {"backward/binders.h$", "<string>"},
+      {"bits/algorithmfwd.h$", "<algorithm>"},
+      {"bits/alloc_traits.h$", "<unordered_set>"},
+      {"bits/allocator.h$", "<string>"},
+      {"bits/atomic_base.h$", "<atomic>"},
+      {"bits/atomic_lockfree_defines.h$", "<exception>"},
+      {"bits/basic_ios.h$", "<ios>"},
+      {"bits/basic_ios.tcc$", "<ios>"},
+      {"bits/basic_string.h$", "<string>"},
+      {"bits/basic_string.tcc$", "<string>"},
+      {"bits/char_traits.h$", "<string>"},
+      {"bits/codecvt.h$", "<locale>"},
+      {"bits/concept_check.h$", "<numeric>"},
+      {"bits/cpp_type_traits.h$", "<cmath>"},
+      {"bits/cxxabi_forced.h$", "<cxxabi.h>"},
+      {"bits/deque.tcc$", "<deque>"},
+      {"bits/exception_defines.h$", "<exception>"},
+      {"bits/exception_ptr.h$", "<exception>"},
+      {"bits/forward_list.h$", "<forward_list>"},
+      {"bits/forward_list.tcc$", "<forward_list>"},
+      {"bits/fstream.tcc$", "<fstream>"},
+      {"bits/functexcept.h$", "<list>"},
+      {"bits/functional_hash.h$", "<string>"},
+      {"bits/gslice.h$", "<valarray>"},
+      {"bits/gslice_array.h$", "<valarray>"},
+      {"bits/hash_bytes.h$", "<typeinfo>"},
+      {"bits/hashtable.h$", "<unordered_set>"},
+      {"bits/hashtable_policy.h$", "<unordered_set>"},
+      {"bits/indirect_array.h$", "<valarray>"},
+      {"bits/ios_base.h$", "<ios>"},
+      {"bits/istream.tcc$", "<istream>"},
+      {"bits/list.tcc$", "<list>"},
+      {"bits/locale_classes.h$", "<locale>"},
+      {"bits/locale_classes.tcc$", "<locale>"},
+      {"bits/locale_facets.h$", "<locale>"},
+      {"bits/locale_facets.tcc$", "<locale>"},
+      {"bits/locale_facets_nonio.h$", "<locale>"},
+      {"bits/locale_facets_nonio.tcc$", "<locale>"},
+      {"bits/localefwd.h$", "<locale>"},
+      {"bits/mask_array.h$", "<valarray>"},
+      {"bits/memoryfwd.h$", "<memory>"},
+      {"bits/move.h$", "<utility>"},
+      {"bits/nested_exception.h$", "<exception>"},
+      {"bits/ostream.tcc$", "<ostream>"},
+      {"bits/ostream_insert.h$", "<ostream>"},
+      {"bits/postypes.h$", "<iosfwd>"},
+      {"bits/ptr_traits.h$", "<memory>"},
+      {"bits/random.h$", "<random>"},
+      {"bits/random.tcc$", "<random>"},
+      {"bits/range_access.h$", "<iterator>"},
+      {"bits/regex.h$", "<regex>"},
+      {"bits/regex_compiler.h$", "<regex>"},
+      {"bits/regex_constants.h$", "<regex>"},
+      {"bits/regex_cursor.h$", "<regex>"},
+      {"bits/regex_error.h$", "<regex>"},
+      {"bits/regex_grep_matcher.h$", "<regex>"},
+      {"bits/regex_grep_matcher.tcc$", "<regex>"},
+      {"bits/regex_nfa.h$", "<regex>"},
+      {"bits/shared_ptr.h$", "<memory>"},
+      {"bits/shared_ptr_base.h$", "<memory>"},
+      {"bits/slice_array.h$", "<valarray>"},
+      {"bits/sstream.tcc$", "<sstream>"},
+      {"bits/stl_algo.h$", "<algorithm>"},
+      {"bits/stl_algobase.h$", "<list>"},
+      {"bits/stl_bvector.h$", "<vector>"},
+      {"bits/stl_construct.h$", "<deque>"},
+      {"bits/stl_deque.h$", "<deque>"},
+      {"bits/stl_function.h$", "<string>"},
+      {"bits/stl_heap.h$", "<queue>"},
+      {"bits/stl_iterator.h$", "<iterator>"},
+      {"bits/stl_iterator_base_funcs.h$", "<iterator>"},
+      {"bits/stl_iterator_base_types.h$", "<numeric>"},
+      {"bits/stl_list.h$", "<list>"},
+      {"bits/stl_map.h$", "<map>"},
+      {"bits/stl_multimap.h$", "<map>"},
+      {"bits/stl_multiset.h$", "<set>"},
+      {"bits/stl_numeric.h$", "<numeric>"},
+      {"bits/stl_pair.h$", "<utility>"},
+      {"bits/stl_queue.h$", "<queue>"},
+      {"bits/stl_raw_storage_iter.h$", "<memory>"},
+      {"bits/stl_relops.h$", "<utility>"},
+      {"bits/stl_set.h$", "<set>"},
+      {"bits/stl_stack.h$", "<stack>"},
+      {"bits/stl_tempbuf.h$", "<memory>"},
+      {"bits/stl_tree.h$", "<map>"},
+      {"bits/stl_uninitialized.h$", "<deque>"},
+      {"bits/stl_vector.h$", "<vector>"},
+      {"bits/stream_iterator.h$", "<iterator>"},
+      {"bits/streambuf.tcc$", "<streambuf>"},
+      {"bits/streambuf_iterator.h$", "<iterator>"},
+      {"bits/stringfwd.h$", "<string>"},
+      {"bits/unique_ptr.h$", "<memory>"},
+      {"bits/unordered_map.h$", "<unordered_map>"},
+      {"bits/unordered_set.h$", "<unordered_set>"},
+      {"bits/uses_allocator.h$", "<tuple>"},
+      {"bits/valarray_after.h$", "<valarray>"},
+      {"bits/valarray_array.h$", "<valarray>"},
+      {"bits/valarray_array.tcc$", "<valarray>"},
+      {"bits/valarray_before.h$", "<valarray>"},
+      {"bits/vector.tcc$", "<vector>"},
+      {"bitset$", "<bitset>"},
+      {"ccomplex$", "<ccomplex>"},
+      {"cctype$", "<cctype>"},
+      {"cerrno$", "<cerrno>"},
+      {"cfenv$", "<cfenv>"},
+      {"cfloat$", "<cfloat>"},
+      {"chrono$", "<chrono>"},
+      {"cinttypes$", "<cinttypes>"},
+      {"climits$", "<climits>"},
+      {"clocale$", "<clocale>"},
+      {"cmath$", "<cmath>"},
+      {"complex$", "<complex>"},
+      {"complex.h$", "<complex.h>"},
+      {"condition_variable$", "<condition_variable>"},
+      {"csetjmp$", "<csetjmp>"},
+      {"csignal$", "<csignal>"},
+      {"cstdalign$", "<cstdalign>"},
+      {"cstdarg$", "<cstdarg>"},
+      {"cstdbool$", "<cstdbool>"},
+      {"cstdint$", "<cstdint>"},
+      {"cstdio$", "<cstdio>"},
+      {"cstdlib$", "<cstdlib>"},
+      {"cstring$", "<cstring>"},
+      {"ctgmath$", "<ctgmath>"},
+      {"ctime$", "<ctime>"},
+      {"cwchar$", "<cwchar>"},
+      {"cwctype$", "<cwctype>"},
+      {"cxxabi.h$", "<cxxabi.h>"},
+      {"debug/debug.h$", "<numeric>"},
+      {"deque$", "<deque>"},
+      {"exception$", "<exception>"},
+      {"ext/alloc_traits.h$", "<deque>"},
+      {"ext/atomicity.h$", "<memory>"},
+      {"ext/concurrence.h$", "<memory>"},
+      {"ext/new_allocator.h$", "<string>"},
+      {"ext/numeric_traits.h$", "<list>"},
+      {"ext/string_conversions.h$", "<string>"},
+      {"ext/type_traits.h$", "<cmath>"},
+      {"fenv.h$", "<fenv.h>"},
+      {"forward_list$", "<forward_list>"},
+      {"fstream$", "<fstream>"},
+      {"functional$", "<functional>"},
+      {"future$", "<future>"},
+      {"initializer_list$", "<initializer_list>"},
+      {"iomanip$", "<iomanip>"},
+      {"ios$", "<ios>"},
+      {"iosfwd$", "<iosfwd>"},
+      {"iostream$", "<iostream>"},
+      {"istream$", "<istream>"},
+      {"iterator$", "<iterator>"},
+      {"limits$", "<limits>"},
+      {"list$", "<list>"},
+      {"locale$", "<locale>"},
+      {"map$", "<map>"},
+      {"memory$", "<memory>"},
+      {"mutex$", "<mutex>"},
+      {"new$", "<new>"},
+      {"numeric$", "<numeric>"},
+      {"ostream$", "<ostream>"},
+      {"queue$", "<queue>"},
+      {"random$", "<random>"},
+      {"ratio$", "<ratio>"},
+      {"regex$", "<regex>"},
+      {"scoped_allocator$", "<scoped_allocator>"},
+      {"set$", "<set>"},
+      {"sstream$", "<sstream>"},
+      {"stack$", "<stack>"},
+      {"stdexcept$", "<stdexcept>"},
+      {"streambuf$", "<streambuf>"},
+      {"string$", "<string>"},
+      {"system_error$", "<system_error>"},
+      {"tgmath.h$", "<tgmath.h>"},
+      {"thread$", "<thread>"},
+      {"tuple$", "<tuple>"},
+      {"type_traits$", "<type_traits>"},
+      {"typeindex$", "<typeindex>"},
+      {"typeinfo$", "<typeinfo>"},
+      {"unordered_map$", "<unordered_map>"},
+      {"unordered_set$", "<unordered_set>"},
+      {"utility$", "<utility>"},
+      {"valarray$", "<valarray>"},
+      {"vector$", "<vector>"},
+      {"include/complex.h$", "<complex.h>"},
+      {"include/ctype.h$", "<cctype>"},
+      {"include/errno.h$", "<cerrno>"},
+      {"include/fenv.h$", "<fenv.h>"},
+      {"include/inttypes.h$", "<cinttypes>"},
+      {"include/libio.h$", "<cstdio>"},
+      {"include/limits.h$", "<climits>"},
+      {"include/locale.h$", "<clocale>"},
+      {"include/math.h$", "<cmath>"},
+      {"include/setjmp.h$", "<csetjmp>"},
+      {"include/signal.h$", "<csignal>"},
+      {"include/stdint.h$", "<cstdint>"},
+      {"include/stdio.h$", "<cstdio>"},
+      {"include/stdlib.h$", "<cstdlib>"},
+      {"include/string.h$", "<cstring>"},
+      {"include/time.h$", "<ctime>"},
+      {"include/wchar.h$", "<cwchar>"},
+      {"include/wctype.h$", "<cwctype>"},
+      {"bits/cmathcalls.h$", "<complex.h>"},
+      {"bits/errno.h$", "<cerrno>"},
+      {"bits/fenv.h$", "<fenv.h>"},
+      {"bits/huge_val.h$", "<cmath>"},
+      {"bits/huge_valf.h$", "<cmath>"},
+      {"bits/huge_vall.h$", "<cmath>"},
+      {"bits/inf.h$", "<cmath>"},
+      {"bits/local_lim.h$", "<climits>"},
+      {"bits/locale.h$", "<clocale>"},
+      {"bits/mathcalls.h$", "<math.h>"},
+      {"bits/mathdef.h$", "<cmath>"},
+      {"bits/nan.h$", "<cmath>"},
+      {"bits/posix1_lim.h$", "<climits>"},
+      {"bits/posix2_lim.h$", "<climits>"},
+      {"bits/setjmp.h$", "<csetjmp>"},
+      {"bits/sigaction.h$", "<csignal>"},
+      {"bits/sigcontext.h$", "<csignal>"},
+      {"bits/siginfo.h$", "<csignal>"},
+      {"bits/signum.h$", "<csignal>"},
+      {"bits/sigset.h$", "<csignal>"},
+      {"bits/sigstack.h$", "<csignal>"},
+      {"bits/stdio_lim.h$", "<cstdio>"},
+      {"bits/sys_errlist.h$", "<cstdio>"},
+      {"bits/time.h$", "<ctime>"},
+      {"bits/timex.h$", "<ctime>"},
+      {"bits/typesizes.h$", "<cstdio>"},
+      {"bits/wchar.h$", "<cwchar>"},
+      {"bits/wordsize.h$", "<csetjmp>"},
+      {"bits/xopen_lim.h$", "<climits>"},
+      {"include/xlocale.h$", "<cstring>"},
+      {"bits/atomic_word.h$", "<memory>"},
+      {"bits/basic_file.h$", "<fstream>"},
+      {"bits/c++allocator.h$", "<string>"},
+      {"bits/c++config.h$", "<iosfwd>"},
+      {"bits/c++io.h$", "<ios>"},
+      {"bits/c++locale.h$", "<locale>"},
+      {"bits/cpu_defines.h$", "<iosfwd>"},
+      {"bits/ctype_base.h$", "<locale>"},
+      {"bits/cxxabi_tweaks.h$", "<cxxabi.h>"},
+      {"bits/error_constants.h$", "<system_error>"},
+      {"bits/gthr-default.h$", "<memory>"},
+      {"bits/gthr.h$", "<memory>"},
+      {"bits/opt_random.h$", "<random>"},
+      {"bits/os_defines.h$", "<iosfwd>"},
+      // GNU C headers
+      {"include/aio.h$", "<aio.h>"},
+      {"include/aliases.h$", "<aliases.h>"},
+      {"include/alloca.h$", "<alloca.h>"},
+      {"include/ar.h$", "<ar.h>"},
+      {"include/argp.h$", "<argp.h>"},
+      {"include/argz.h$", "<argz.h>"},
+      {"include/arpa/nameser.h$", "<resolv.h>"},
+      {"include/arpa/nameser_compat.h$", "<resolv.h>"},
+      {"include/byteswap.h$", "<byteswap.h>"},
+      {"include/cpio.h$", "<cpio.h>"},
+      {"include/crypt.h$", "<crypt.h>"},
+      {"include/dirent.h$", "<dirent.h>"},
+      {"include/dlfcn.h$", "<dlfcn.h>"},
+      {"include/elf.h$", "<elf.h>"},
+      {"include/endian.h$", "<endian.h>"},
+      {"include/envz.h$", "<envz.h>"},
+      {"include/err.h$", "<err.h>"},
+      {"include/error.h$", "<error.h>"},
+      {"include/execinfo.h$", "<execinfo.h>"},
+      {"include/fcntl.h$", "<fcntl.h>"},
+      {"include/features.h$", "<features.h>"},
+      {"include/fenv.h$", "<fenv.h>"},
+      {"include/fmtmsg.h$", "<fmtmsg.h>"},
+      {"include/fnmatch.h$", "<fnmatch.h>"},
+      {"include/fstab.h$", "<fstab.h>"},
+      {"include/fts.h$", "<fts.h>"},
+      {"include/ftw.h$", "<ftw.h>"},
+      {"include/gconv.h$", "<gconv.h>"},
+      {"include/getopt.h$", "<getopt.h>"},
+      {"include/glob.h$", "<glob.h>"},
+      {"include/grp.h$", "<grp.h>"},
+      {"include/gshadow.h$", "<gshadow.h>"},
+      {"include/iconv.h$", "<iconv.h>"},
+      {"include/ifaddrs.h$", "<ifaddrs.h>"},
+      {"include/kdb.h$", "<kdb.h>"},
+      {"include/langinfo.h$", "<langinfo.h>"},
+      {"include/libgen.h$", "<libgen.h>"},
+      {"include/libintl.h$", "<libintl.h>"},
+      {"include/link.h$", "<link.h>"},
+      {"include/malloc.h$", "<malloc.h>"},
+      {"include/mcheck.h$", "<mcheck.h>"},
+      {"include/memory.h$", "<memory.h>"},
+      {"include/mntent.h$", "<mntent.h>"},
+      {"include/monetary.h$", "<monetary.h>"},
+      {"include/mqueue.h$", "<mqueue.h>"},
+      {"include/netdb.h$", "<netdb.h>"},
+      {"include/netinet/in.h$", "<netinet/in.h>"},
+      {"include/nl_types.h$", "<nl_types.h>"},
+      {"include/nss.h$", "<nss.h>"},
+      {"include/obstack.h$", "<obstack.h>"},
+      {"include/panel.h$", "<panel.h>"},
+      {"include/paths.h$", "<paths.h>"},
+      {"include/printf.h$", "<printf.h>"},
+      {"include/profile.h$", "<profile.h>"},
+      {"include/pthread.h$", "<pthread.h>"},
+      {"include/pty.h$", "<pty.h>"},
+      {"include/pwd.h$", "<pwd.h>"},
+      {"include/re_comp.h$", "<re_comp.h>"},
+      {"include/regex.h$", "<regex.h>"},
+      {"include/regexp.h$", "<regexp.h>"},
+      {"include/resolv.h$", "<resolv.h>"},
+      {"include/rpc/netdb.h$", "<netdb.h>"},
+      {"include/sched.h$", "<sched.h>"},
+      {"include/search.h$", "<search.h>"},
+      {"include/semaphore.h$", "<semaphore.h>"},
+      {"include/sgtty.h$", "<sgtty.h>"},
+      {"include/shadow.h$", "<shadow.h>"},
+      {"include/spawn.h$", "<spawn.h>"},
+      {"include/stab.h$", "<stab.h>"},
+      {"include/stdc-predef.h$", "<stdc-predef.h>"},
+      {"include/stdio_ext.h$", "<stdio_ext.h>"},
+      {"include/strings.h$", "<strings.h>"},
+      {"include/stropts.h$", "<stropts.h>"},
+      {"include/sudo_plugin.h$", "<sudo_plugin.h>"},
+      {"include/sysexits.h$", "<sysexits.h>"},
+      {"include/tar.h$", "<tar.h>"},
+      {"include/tcpd.h$", "<tcpd.h>"},
+      {"include/term.h$", "<term.h>"},
+      {"include/term_entry.h$", "<term_entry.h>"},
+      {"include/termcap.h$", "<termcap.h>"},
+      {"include/termios.h$", "<termios.h>"},
+      {"include/thread_db.h$", "<thread_db.h>"},
+      {"include/tic.h$", "<tic.h>"},
+      {"include/ttyent.h$", "<ttyent.h>"},
+      {"include/uchar.h$", "<uchar.h>"},
+      {"include/ucontext.h$", "<ucontext.h>"},
+      {"include/ulimit.h$", "<ulimit.h>"},
+      {"include/unctrl.h$", "<unctrl.h>"},
+      {"include/unistd.h$", "<unistd.h>"},
+      {"include/utime.h$", "<utime.h>"},
+      {"include/utmp.h$", "<utmp.h>"},
+      {"include/utmpx.h$", "<utmpx.h>"},
+      {"include/values.h$", "<values.h>"},
+      {"include/wordexp.h$", "<wordexp.h>"},
+      {"fpu_control.h$", "<fpu_control.h>"},
+      {"ieee754.h$", "<ieee754.h>"},
+      {"include/xlocale.h$", "<xlocale.h>"},
+      {"gnu/lib-names.h$", "<gnu/lib-names.h>"},
+      {"gnu/libc-version.h$", "<gnu/libc-version.h>"},
+      {"gnu/option-groups.h$", "<gnu/option-groups.h>"},
+      {"gnu/stubs-32.h$", "<gnu/stubs-32.h>"},
+      {"gnu/stubs-64.h$", "<gnu/stubs-64.h>"},
+      {"gnu/stubs-x32.h$", "<gnu/stubs-x32.h>"},
+      {"include/rpc/auth_des.h$", "<rpc/auth_des.h>"},
+      {"include/rpc/rpc_msg.h$", "<rpc/rpc_msg.h>"},
+      {"include/rpc/pmap_clnt.h$", "<rpc/pmap_clnt.h>"},
+      {"include/rpc/rpc.h$", "<rpc/rpc.h>"},
+      {"include/rpc/types.h$", "<rpc/types.h>"},
+      {"include/rpc/auth_unix.h$", "<rpc/auth_unix.h>"},
+      {"include/rpc/key_prot.h$", "<rpc/key_prot.h>"},
+      {"include/rpc/pmap_prot.h$", "<rpc/pmap_prot.h>"},
+      {"include/rpc/auth.h$", "<rpc/auth.h>"},
+      {"include/rpc/svc_auth.h$", "<rpc/svc_auth.h>"},
+      {"include/rpc/xdr.h$", "<rpc/xdr.h>"},
+      {"include/rpc/pmap_rmt.h$", "<rpc/pmap_rmt.h>"},
+      {"include/rpc/des_crypt.h$", "<rpc/des_crypt.h>"},
+      {"include/rpc/svc.h$", "<rpc/svc.h>"},
+      {"include/rpc/rpc_des.h$", "<rpc/rpc_des.h>"},
+      {"include/rpc/clnt.h$", "<rpc/clnt.h>"},
+      {"include/scsi/scsi.h$", "<scsi/scsi.h>"},
+      {"include/scsi/sg.h$", "<scsi/sg.h>"},
+      {"include/scsi/scsi_ioctl.h$", "<scsi/scsi_ioctl>"},
+      {"include/netrose/rose.h$", "<netrose/rose.h>"},
+      {"include/nfs/nfs.h$", "<nfs/nfs.h>"},
+      {"include/netatalk/at.h$", "<netatalk/at.h>"},
+      {"include/netinet/ether.h$", "<netinet/ether.h>"},
+      {"include/netinet/icmp6.h$", "<netinet/icmp6.h>"},
+      {"include/netinet/if_ether.h$", "<netinet/if_ether.h>"},
+      {"include/netinet/if_fddi.h$", "<netinet/if_fddi.h>"},
+      {"include/netinet/if_tr.h$", "<netinet/if_tr.h>"},
+      {"include/netinet/igmp.h$", "<netinet/igmp.h>"},
+      {"include/netinet/in.h$", "<netinet/in.h>"},
+      {"include/netinet/in_systm.h$", "<netinet/in_systm.h>"},
+      {"include/netinet/ip.h$", "<netinet/ip.h>"},
+      {"include/netinet/ip6.h$", "<netinet/ip6.h>"},
+      {"include/netinet/ip_icmp.h$", "<netinet/ip_icmp.h>"},
+      {"include/netinet/tcp.h$", "<netinet/tcp.h>"},
+      {"include/netinet/udp.h$", "<netinet/udp.h>"},
+      {"include/netrom/netrom.h$", "<netrom/netrom.h>"},
+      {"include/protocols/routed.h$", "<protocols/routed.h>"},
+      {"include/protocols/rwhod.h$", "<protocols/rwhod.h>"},
+      {"include/protocols/talkd.h$", "<protocols/talkd.h>"},
+      {"include/protocols/timed.h$", "<protocols/timed.h>"},
+      {"include/rpcsvc/klm_prot.x$", "<rpcsvc/klm_prot.x>"},
+      {"include/rpcsvc/rstat.h$", "<rpcsvc/rstat.h>"},
+      {"include/rpcsvc/spray.x$", "<rpcsvc/spray.x>"},
+      {"include/rpcsvc/nlm_prot.x$", "<rpcsvc/nlm_prot.x>"},
+      {"include/rpcsvc/nis_callback.x$", "<rpcsvc/nis_callback.x>"},
+      {"include/rpcsvc/yp.h$", "<rpcsvc/yp.h>"},
+      {"include/rpcsvc/yp.x$", "<rpcsvc/yp.x>"},
+      {"include/rpcsvc/nfs_prot.h$", "<rpcsvc/nfs_prot.h>"},
+      {"include/rpcsvc/rex.h$", "<rpcsvc/rex.h>"},
+      {"include/rpcsvc/yppasswd.h$", "<rpcsvc/yppasswd.h>"},
+      {"include/rpcsvc/rex.x$", "<rpcsvc/rex.x>"},
+      {"include/rpcsvc/nis_tags.h$", "<rpcsvc/nis_tags.h>"},
+      {"include/rpcsvc/nis_callback.h$", "<rpcsvc/nis_callback.h>"},
+      {"include/rpcsvc/nfs_prot.x$", "<rpcsvc/nfs_prot.x>"},
+      {"include/rpcsvc/bootparam_prot.x$", "<rpcsvc/bootparam_prot.x>"},
+      {"include/rpcsvc/rusers.x$", "<rpcsvc/rusers.x>"},
+      {"include/rpcsvc/rquota.x$", "<rpcsvc/rquota.x>"},
+      {"include/rpcsvc/nis.h$", "<rpcsvc/nis.h>"},
+      {"include/rpcsvc/nislib.h$", "<rpcsvc/nislib.h>"},
+      {"include/rpcsvc/ypupd.h$", "<rpcsvc/ypupd.h>"},
+      {"include/rpcsvc/bootparam.h$", "<rpcsvc/bootparam.h>"},
+      {"include/rpcsvc/spray.h$", "<rpcsvc/spray.h>"},
+      {"include/rpcsvc/key_prot.h$", "<rpcsvc/key_prot.h>"},
+      {"include/rpcsvc/klm_prot.h$", "<rpcsvc/klm_prot.h>"},
+      {"include/rpcsvc/sm_inter.h$", "<rpcsvc/sm_inter.h>"},
+      {"include/rpcsvc/nlm_prot.h$", "<rpcsvc/nlm_prot.h>"},
+      {"include/rpcsvc/yp_prot.h$", "<rpcsvc/yp_prot.h>"},
+      {"include/rpcsvc/ypclnt.h$", "<rpcsvc/ypclnt.h>"},
+      {"include/rpcsvc/rstat.x$", "<rpcsvc/rstat.x>"},
+      {"include/rpcsvc/rusers.h$", "<rpcsvc/rusers.h>"},
+      {"include/rpcsvc/key_prot.x$", "<rpcsvc/key_prot.x>"},
+      {"include/rpcsvc/sm_inter.x$", "<rpcsvc/sm_inter.x>"},
+      {"include/rpcsvc/rquota.h$", "<rpcsvc/rquota.h>"},
+      {"include/rpcsvc/nis.x$", "<rpcsvc/nis.x>"},
+      {"include/rpcsvc/bootparam_prot.h$", "<rpcsvc/bootparam_prot.h>"},
+      {"include/rpcsvc/mount.h$", "<rpcsvc/mount.h>"},
+      {"include/rpcsvc/mount.x$", "<rpcsvc/mount.x>"},
+      {"include/rpcsvc/nis_object.x$", "<rpcsvc/nis_object.x>"},
+      {"include/rpcsvc/yppasswd.x$", "<rpcsvc/yppasswd.x>"},
+      {"sys/acct.h$", "<sys/acct.h>"},
+      {"sys/auxv.h$", "<sys/auxv.h>"},
+      {"sys/cdefs.h$", "<sys/cdefs.h>"},
+      {"sys/debugreg.h$", "<sys/debugreg.h>"},
+      {"sys/dir.h$", "<sys/dir.h>"},
+      {"sys/elf.h$", "<sys/elf.h>"},
+      {"sys/epoll.h$", "<sys/epoll.h>"},
+      {"sys/eventfd.h$", "<sys/eventfd.h>"},
+      {"sys/fanotify.h$", "<sys/fanotify.h>"},
+      {"sys/file.h$", "<sys/file.h>"},
+      {"sys/fsuid.h$", "<sys/fsuid.h>"},
+      {"sys/gmon.h$", "<sys/gmon.h>"},
+      {"sys/gmon_out.h$", "<sys/gmon_out.h>"},
+      {"sys/inotify.h$", "<sys/inotify.h>"},
+      {"sys/io.h$", "<sys/io.h>"},
+      {"sys/ioctl.h$", "<sys/ioctl.h>"},
+      {"sys/ipc.h$", "<sys/ipc.h>"},
+      {"sys/kd.h$", "<sys/kd.h>"},
+      {"sys/kdaemon.h$", "<sys/kdaemon.h>"},
+      {"sys/klog.h$", "<sys/klog.h>"},
+      {"sys/mman.h$", "<sys/mman.h>"},
+      {"sys/mount.h$", "<sys/mount.h>"},
+      {"sys/msg.h$", "<sys/msg.h>"},
+      {"sys/mtio.h$", "<sys/mtio.h>"},
+      {"sys/param.h$", "<sys/param.h>"},
+      {"sys/pci.h$", "<sys/pci.h>"},
+      {"sys/perm.h$", "<sys/perm.h>"},
+      {"sys/personality.h$", "<sys/personality.h>"},
+      {"sys/poll.h$", "<sys/poll.h>"},
+      {"sys/prctl.h$", "<sys/prctl.h>"},
+      {"sys/procfs.h$", "<sys/procfs.h>"},
+      {"sys/profil.h$", "<sys/profil.h>"},
+      {"sys/ptrace.h$", "<sys/ptrace.h>"},
+      {"sys/queue.h$", "<sys/queue.h>"},
+      {"sys/quota.h$", "<sys/quota.h>"},
+      {"sys/raw.h$", "<sys/raw.h>"},
+      {"sys/reboot.h$", "<sys/reboot.h>"},
+      {"sys/reg.h$", "<sys/reg.h>"},
+      {"sys/resource.h$", "<sys/resource.h>"},
+      {"sys/select.h$", "<sys/select.h>"},
+      {"sys/sem.h$", "<sys/sem.h>"},
+      {"sys/sendfile.h$", "<sys/sendfile.h>"},
+      {"sys/shm.h$", "<sys/shm.h>"},
+      {"sys/signalfd.h$", "<sys/signalfd.h>"},
+      {"sys/socket.h$", "<sys/socket.h>"},
+      {"sys/stat.h$", "<sys/stat.h>"},
+      {"sys/statfs.h$", "<sys/statfs.h>"},
+      {"sys/statvfs.h$", "<sys/statvfs.h>"},
+      {"sys/swap.h$", "<sys/swap.h>"},
+      {"sys/syscall.h$", "<sys/syscall.h>"},
+      {"sys/sysctl.h$", "<sys/sysctl.h>"},
+      {"sys/sysinfo.h$", "<sys/sysinfo.h>"},
+      {"sys/syslog.h$", "<sys/syslog.h>"},
+      {"sys/sysmacros.h$", "<sys/sysmacros.h>"},
+      {"sys/termios.h$", "<sys/termios.h>"},
+      {"sys/time.h$", "<sys/select.h>"},
+      {"sys/timeb.h$", "<sys/timeb.h>"},
+      {"sys/timerfd.h$", "<sys/timerfd.h>"},
+      {"sys/times.h$", "<sys/times.h>"},
+      {"sys/timex.h$", "<sys/timex.h>"},
+      {"sys/ttychars.h$", "<sys/ttychars.h>"},
+      {"sys/ttydefaults.h$", "<sys/ttydefaults.h>"},
+      {"sys/types.h$", "<sys/types.h>"},
+      {"sys/ucontext.h$", "<sys/ucontext.h>"},
+      {"sys/uio.h$", "<sys/uio.h>"},
+      {"sys/un.h$", "<sys/un.h>"},
+      {"sys/user.h$", "<sys/user.h>"},
+      {"sys/ustat.h$", "<sys/ustat.h>"},
+      {"sys/utsname.h$", "<sys/utsname.h>"},
+      {"sys/vlimit.h$", "<sys/vlimit.h>"},
+      {"sys/vm86.h$", "<sys/vm86.h>"},
+      {"sys/vtimes.h$", "<sys/vtimes.h>"},
+      {"sys/wait.h$", "<sys/wait.h>"},
+      {"sys/xattr.h$", "<sys/xattr.h>"},
+      {"bits/epoll.h$", "<sys/epoll.h>"},
+      {"bits/eventfd.h$", "<sys/eventfd.h>"},
+      {"bits/inotify.h$", "<sys/inotify.h>"},
+      {"bits/ipc.h$", "<sys/ipc.h>"},
+      {"bits/ipctypes.h$", "<sys/ipc.h>"},
+      {"bits/mman-linux.h$", "<sys/mman.h>"},
+      {"bits/mman.h$", "<sys/mman.h>"},
+      {"bits/msq.h$", "<sys/msg.h>"},
+      {"bits/resource.h$", "<sys/resource.h>"},
+      {"bits/sem.h$", "<sys/sem.h>"},
+      {"bits/shm.h$", "<sys/shm.h>"},
+      {"bits/signalfd.h$", "<sys/signalfd.h>"},
+      {"bits/statfs.h$", "<sys/statfs.h>"},
+      {"bits/statvfs.h$", "<sys/statvfs.h>"},
+      {"bits/timerfd.h$", "<sys/timerfd.h>"},
+      {"bits/utsname.h$", "<sys/utsname.h>"},
+      {"bits/auxv.h$", "<sys/auxv.h>"},
+      {"bits/byteswap-16.h$", "<byteswap.h>"},
+      {"bits/byteswap.h$", "<byteswap.h>"},
+      {"bits/confname.h$", "<unistd.h>"},
+      {"bits/dirent.h$", "<dirent.h>"},
+      {"bits/dlfcn.h$", "<dlfcn.h>"},
+      {"bits/elfclass.h$", "<link.h>"},
+      {"bits/endian.h$", "<endian.h>"},
+      {"bits/environments.h$", "<unistd.h>"},
+      {"bits/fcntl-linux.h$", "<fcntl.h>"},
+      {"bits/fcntl.h$", "<fcntl.h>"},
+      {"bits/in.h$", "<netinet/in.h>"},
+      {"bits/ioctl-types.h$", "<sys/ioctl.h>"},
+      {"bits/ioctls.h$", "<sys/ioctl.h>"},
+      {"bits/link.h$", "<link.h>"},
+      {"bits/mqueue.h$", "<mqueue.h>"},
+      {"bits/netdb.h$", "<netdb.h>"},
+      {"bits/param.h$", "<sys/param.h>"},
+      {"bits/poll.h$", "<sys/poll.h>"},
+      {"bits/posix_opt.h$", "<bits/posix_opt.h>"},
+      {"bits/pthreadtypes.h$", "<pthread.h>"},
+      {"bits/sched.h$", "<sched.h>"},
+      {"bits/select.h$", "<sys/select.h>"},
+      {"bits/semaphore.h$", "<semaphore.h>"},
+      {"bits/sigthread.h$", "<pthread.h>"},
+      {"bits/sockaddr.h$", "<sys/socket.h>"},
+      {"bits/socket.h$", "<sys/socket.h>"},
+      {"bits/socket_type.h$", "<sys/socket.h>"},
+      {"bits/stab.def$", "<stab.h>"},
+      {"bits/stat.h$", "<sys/stat.h>"},
+      {"bits/stropts.h$", "<stropts.h>"},
+      {"bits/syscall.h$", "<sys/syscall.h>"},
+      {"bits/syslog-path.h$", "<sys/syslog.h>"},
+      {"bits/termios.h$", "<termios.h>"},
+      {"bits/types.h$", "<sys/types.h>"},
+      {"bits/typesizes.h$", "<sys/types.h>"},
+      {"bits/uio.h$", "<sys/uio.h>"},
+      {"bits/ustat.h$", "<sys/ustat.h>"},
+      {"bits/utmp.h$", "<utmp.h>"},
+      {"bits/utmpx.h$", "<utmpx.h>"},
+      {"bits/waitflags.h$", "<sys/wait.h>"},
+      {"bits/waitstatus.h$", "<sys/wait.h>"},
+      {"bits/xtitypes.h$", "<stropts.h>"},
+  };
+  return &STLPostfixHeaderMap;
+}
+
+} // namespace find_all_symbols
+} // namespace clang
diff --git a/include-fixer/find-all-symbols/STLPostfixHeaderMap.h b/include-fixer/find-all-symbols/STLPostfixHeaderMap.h
new file mode 100644 (file)
index 0000000..162580d
--- /dev/null
@@ -0,0 +1,23 @@
+//===-- STLPostfixHeaderMap.h - hardcoded header map for STL ----*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_TOOL_STL_POSTFIX_HEADER_MAP_H
+#define LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_TOOL_STL_POSTFIX_HEADER_MAP_H
+
+#include "HeaderMapCollector.h"
+
+namespace clang {
+namespace find_all_symbols {
+
+const HeaderMapCollector::RegexHeaderMap *getSTLPostfixHeaderMap();
+
+} // namespace find_all_symbols
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_TOOL_STL_POSTFIX_HEADER_MAP_H
diff --git a/include-fixer/find-all-symbols/SymbolInfo.cpp b/include-fixer/find-all-symbols/SymbolInfo.cpp
new file mode 100644 (file)
index 0000000..00bfbe5
--- /dev/null
@@ -0,0 +1,137 @@
+//===-- SymbolInfo.cpp - Symbol Info ----------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "SymbolInfo.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/YAMLTraits.h"
+#include "llvm/Support/raw_ostream.h"
+
+using llvm::yaml::MappingTraits;
+using llvm::yaml::IO;
+using llvm::yaml::Input;
+using ContextType = clang::find_all_symbols::SymbolInfo::ContextType;
+using clang::find_all_symbols::SymbolInfo;
+using clang::find_all_symbols::SymbolAndSignals;
+using SymbolKind = clang::find_all_symbols::SymbolInfo::SymbolKind;
+
+LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(SymbolAndSignals)
+LLVM_YAML_IS_SEQUENCE_VECTOR(SymbolInfo::Context)
+
+namespace llvm {
+namespace yaml {
+template <> struct MappingTraits<SymbolAndSignals> {
+  static void mapping(IO &io, SymbolAndSignals &Symbol) {
+    io.mapRequired("Name", Symbol.Symbol.Name);
+    io.mapRequired("Contexts", Symbol.Symbol.Contexts);
+    io.mapRequired("FilePath", Symbol.Symbol.FilePath);
+    io.mapRequired("Type", Symbol.Symbol.Type);
+    io.mapRequired("Seen", Symbol.Signals.Seen);
+    io.mapRequired("Used", Symbol.Signals.Used);
+  }
+};
+
+template <> struct ScalarEnumerationTraits<ContextType> {
+  static void enumeration(IO &io, ContextType &value) {
+    io.enumCase(value, "Record", ContextType::Record);
+    io.enumCase(value, "Namespace", ContextType::Namespace);
+    io.enumCase(value, "EnumDecl", ContextType::EnumDecl);
+  }
+};
+
+template <> struct ScalarEnumerationTraits<SymbolKind> {
+  static void enumeration(IO &io, SymbolKind &value) {
+    io.enumCase(value, "Variable", SymbolKind::Variable);
+    io.enumCase(value, "Function", SymbolKind::Function);
+    io.enumCase(value, "Class", SymbolKind::Class);
+    io.enumCase(value, "TypedefName", SymbolKind::TypedefName);
+    io.enumCase(value, "EnumDecl", SymbolKind::EnumDecl);
+    io.enumCase(value, "EnumConstantDecl", SymbolKind::EnumConstantDecl);
+    io.enumCase(value, "Macro", SymbolKind::Macro);
+    io.enumCase(value, "Unknown", SymbolKind::Unknown);
+  }
+};
+
+template <> struct MappingTraits<SymbolInfo::Context> {
+  static void mapping(IO &io, SymbolInfo::Context &Context) {
+    io.mapRequired("ContextType", Context.first);
+    io.mapRequired("ContextName", Context.second);
+  }
+};
+
+} // namespace yaml
+} // namespace llvm
+
+namespace clang {
+namespace find_all_symbols {
+
+SymbolInfo::SymbolInfo(llvm::StringRef Name, SymbolKind Type,
+                       llvm::StringRef FilePath,
+                       const std::vector<Context> &Contexts)
+    : Name(Name), Type(Type), FilePath(FilePath), Contexts(Contexts) {}
+
+bool SymbolInfo::operator==(const SymbolInfo &Symbol) const {
+  return std::tie(Name, Type, FilePath, Contexts) ==
+         std::tie(Symbol.Name, Symbol.Type, Symbol.FilePath, Symbol.Contexts);
+}
+
+bool SymbolInfo::operator<(const SymbolInfo &Symbol) const {
+  return std::tie(Name, Type, FilePath, Contexts) <
+         std::tie(Symbol.Name, Symbol.Type, Symbol.FilePath, Symbol.Contexts);
+}
+
+std::string SymbolInfo::getQualifiedName() const {
+  std::string QualifiedName = Name;
+  for (const auto &Context : Contexts) {
+    if (Context.first == ContextType::EnumDecl)
+      continue;
+    QualifiedName = Context.second + "::" + QualifiedName;
+  }
+  return QualifiedName;
+}
+
+SymbolInfo::Signals &SymbolInfo::Signals::operator+=(const Signals &RHS) {
+  Seen += RHS.Seen;
+  Used += RHS.Used;
+  return *this;
+}
+
+SymbolInfo::Signals SymbolInfo::Signals::operator+(const Signals &RHS) const {
+  Signals Result = *this;
+  Result += RHS;
+  return Result;
+}
+
+bool SymbolInfo::Signals::operator==(const Signals &RHS) const {
+  return std::tie(Seen, Used) == std::tie(RHS.Seen, RHS.Used);
+}
+
+bool SymbolAndSignals::operator==(const SymbolAndSignals& RHS) const {
+  return std::tie(Symbol, Signals) == std::tie(RHS.Symbol, RHS.Signals);
+}
+
+bool WriteSymbolInfosToStream(llvm::raw_ostream &OS,
+                              const SymbolInfo::SignalMap &Symbols) {
+  llvm::yaml::Output yout(OS);
+  for (const auto &Symbol : Symbols) {
+    SymbolAndSignals S{Symbol.first, Symbol.second};
+    yout << S;
+  }
+  return true;
+}
+
+std::vector<SymbolAndSignals> ReadSymbolInfosFromYAML(llvm::StringRef Yaml) {
+  std::vector<SymbolAndSignals> Symbols;
+  llvm::yaml::Input yin(Yaml);
+  yin >> Symbols;
+  return Symbols;
+}
+
+} // namespace find_all_symbols
+} // namespace clang
diff --git a/include-fixer/find-all-symbols/SymbolInfo.h b/include-fixer/find-all-symbols/SymbolInfo.h
new file mode 100644 (file)
index 0000000..92d360d
--- /dev/null
@@ -0,0 +1,143 @@
+//===-- SymbolInfo.h - Symbol Info ------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_FIND_ALL_SYMBOLS_SYMBOLINFO_H
+#define LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_FIND_ALL_SYMBOLS_SYMBOLINFO_H
+
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/YAMLTraits.h"
+#include "llvm/Support/raw_ostream.h"
+#include <set>
+#include <string>
+#include <vector>
+
+namespace clang {
+namespace find_all_symbols {
+/// \brief Describes a named symbol from a header.
+/// Symbols with the same qualified name and type (e.g. function overloads)
+/// that appear in the same header are represented by a single SymbolInfo.
+///
+/// TODO: keep track of instances, e.g. overload locations and signatures.
+class SymbolInfo {
+public:
+  /// \brief The SymbolInfo Type.
+  enum class SymbolKind {
+    Function,
+    Class,
+    Variable,
+    TypedefName,
+    EnumDecl,
+    EnumConstantDecl,
+    Macro,
+    Unknown,
+  };
+
+  /// \brief The Context Type.
+  enum class ContextType {
+    Namespace, // Symbols declared in a namespace.
+    Record,    // Symbols declared in a class.
+    EnumDecl,  // Enum constants declared in a enum declaration.
+  };
+
+  /// \brief A pair of <ContextType, ContextName>.
+  typedef std::pair<ContextType, std::string> Context;
+
+  // \brief Signals are signals gathered by observing how a symbol is used.
+  // These are used to rank results.
+  struct Signals {
+    Signals() {}
+    Signals(unsigned Seen, unsigned Used) : Seen(Seen), Used(Used) {}
+
+    // Number of times this symbol was visible to a TU.
+    unsigned Seen = 0;
+
+    // Number of times this symbol was referenced a TU's main file.
+    unsigned Used = 0;
+
+    Signals &operator+=(const Signals &RHS);
+    Signals operator+(const Signals &RHS) const;
+    bool operator==(const Signals &RHS) const;
+  };
+
+  using SignalMap = std::map<SymbolInfo, Signals>;
+
+  // The default constructor is required by YAML traits in
+  // LLVM_YAML_IS_DOCUMENT_LIST_VECTOR.
+  SymbolInfo() : Type(SymbolKind::Unknown) {}
+
+  SymbolInfo(llvm::StringRef Name, SymbolKind Type, llvm::StringRef FilePath,
+             const std::vector<Context> &Contexts);
+
+  void SetFilePath(llvm::StringRef Path) { FilePath = Path; }
+
+  /// \brief Get symbol name.
+  llvm::StringRef getName() const { return Name; }
+
+  /// \brief Get the fully-qualified symbol name.
+  std::string getQualifiedName() const;
+
+  /// \brief Get symbol type.
+  SymbolKind getSymbolKind() const { return Type; }
+
+  /// \brief Get a relative file path where symbol comes from.
+  llvm::StringRef getFilePath() const { return FilePath; }
+
+  /// \brief Get symbol contexts.
+  const std::vector<SymbolInfo::Context> &getContexts() const {
+    return Contexts;
+  }
+
+  bool operator<(const SymbolInfo &Symbol) const;
+
+  bool operator==(const SymbolInfo &Symbol) const;
+
+private:
+  friend struct llvm::yaml::MappingTraits<struct SymbolAndSignals>;
+
+  /// \brief Identifier name.
+  std::string Name;
+
+  /// \brief Symbol type.
+  SymbolKind Type;
+
+  /// \brief The file path where the symbol comes from. It's a relative file
+  /// path based on the build directory.
+  std::string FilePath;
+
+  /// \brief Contains information about symbol contexts. Context information is
+  /// stored from the inner-most level to outer-most level.
+  ///
+  /// For example, if a symbol 'x' is declared as:
+  ///     namespace na { namespace nb { class A { int x; } } }
+  /// The contexts would be { {RECORD, "A"}, {NAMESPACE, "nb"}, {NAMESPACE,
+  /// "na"} }.
+  /// The name of an anonymous namespace is "".
+  ///
+  /// If the symbol is declared in `TranslationUnitDecl`, it has no context.
+  std::vector<Context> Contexts;
+};
+
+struct SymbolAndSignals {
+  SymbolInfo Symbol;
+  SymbolInfo::Signals Signals;
+  bool operator==(const SymbolAndSignals& RHS) const;
+};
+
+/// \brief Write SymbolInfos to a stream (YAML format).
+bool WriteSymbolInfosToStream(llvm::raw_ostream &OS,
+                              const SymbolInfo::SignalMap &Symbols);
+
+/// \brief Read SymbolInfos from a YAML document.
+std::vector<SymbolAndSignals> ReadSymbolInfosFromYAML(llvm::StringRef Yaml);
+
+} // namespace find_all_symbols
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_INCLUDE_FIXER_FIND_ALL_SYMBOLS_SYMBOLINFO_H
diff --git a/include-fixer/find-all-symbols/SymbolReporter.h b/include-fixer/find-all-symbols/SymbolReporter.h
new file mode 100644 (file)
index 0000000..2398234
--- /dev/null
@@ -0,0 +1,30 @@
+//===--- SymbolReporter.h - Symbol Reporter ---------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_SYMBOL_REPORTER_H
+#define LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_SYMBOL_REPORTER_H
+
+#include "SymbolInfo.h"
+
+namespace clang {
+namespace find_all_symbols {
+
+/// \brief An interface for classes that collect symbols.
+class SymbolReporter {
+public:
+  virtual ~SymbolReporter() = default;
+
+  virtual void reportSymbols(llvm::StringRef FileName,
+                             const SymbolInfo::SignalMap &Symbols) = 0;
+};
+
+} // namespace find_all_symbols
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_FIND_ALL_SYMBOLS_SYMBOL_REPORTER_H
diff --git a/include-fixer/find-all-symbols/tool/CMakeLists.txt b/include-fixer/find-all-symbols/tool/CMakeLists.txt
new file mode 100644 (file)
index 0000000..0bd14f7
--- /dev/null
@@ -0,0 +1,22 @@
+include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..)
+
+add_clang_executable(find-all-symbols
+  FindAllSymbolsMain.cpp
+  )
+
+target_link_libraries(find-all-symbols
+  clangAST
+  clangASTMatchers
+  clangBasic
+  clangFrontend
+  clangLex
+  clangTooling
+  findAllSymbols
+  )
+
+install(TARGETS find-all-symbols
+  RUNTIME DESTINATION bin)
+
+install(PROGRAMS run-find-all-symbols.py
+  DESTINATION share/clang
+  COMPONENT find-all-symbols)
diff --git a/include-fixer/find-all-symbols/tool/FindAllSymbolsMain.cpp b/include-fixer/find-all-symbols/tool/FindAllSymbolsMain.cpp
new file mode 100644 (file)
index 0000000..e09a5aa
--- /dev/null
@@ -0,0 +1,152 @@
+//===-- FindAllSymbolsMain.cpp - find all symbols tool ----------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "FindAllSymbolsAction.h"
+#include "STLPostfixHeaderMap.h"
+#include "SymbolInfo.h"
+#include "SymbolReporter.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendActions.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Tooling/CommonOptionsParser.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/ThreadPool.h"
+#include "llvm/Support/raw_ostream.h"
+#include <map>
+#include <mutex>
+#include <set>
+#include <string>
+#include <system_error>
+#include <vector>
+
+using namespace clang::tooling;
+using namespace llvm;
+using SymbolInfo = clang::find_all_symbols::SymbolInfo;
+
+// Apply a custom category to all command-line options so that they are the
+// only ones displayed.
+static cl::OptionCategory FindAllSymbolsCategory("find_all_symbols options");
+
+// CommonOptionsParser declares HelpMessage with a description of the common
+// command-line options related to the compilation database and input files.
+// It's nice to have this help message in all tools.
+static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage);
+
+// A help message for this specific tool can be added afterwards.
+static cl::extrahelp MoreHelp("\nMore help text...");
+
+static cl::opt<std::string> OutputDir("output-dir", cl::desc(R"(
+The output directory for saving the results.)"),
+                                      cl::init("."),
+                                      cl::cat(FindAllSymbolsCategory));
+
+static cl::opt<std::string> MergeDir("merge-dir", cl::desc(R"(
+The directory for merging symbols.)"),
+                                     cl::init(""),
+                                     cl::cat(FindAllSymbolsCategory));
+namespace clang {
+namespace find_all_symbols {
+
+class YamlReporter : public SymbolReporter {
+public:
+  void reportSymbols(StringRef FileName,
+                     const SymbolInfo::SignalMap &Symbols) override {
+    int FD;
+    SmallString<128> ResultPath;
+    llvm::sys::fs::createUniqueFile(
+        OutputDir + "/" + llvm::sys::path::filename(FileName) + "-%%%%%%.yaml",
+        FD, ResultPath);
+    llvm::raw_fd_ostream OS(FD, /*shouldClose=*/true);
+    WriteSymbolInfosToStream(OS, Symbols);
+  }
+};
+
+bool Merge(llvm::StringRef MergeDir, llvm::StringRef OutputFile) {
+  std::error_code EC;
+  SymbolInfo::SignalMap Symbols;
+  std::mutex SymbolMutex;
+  auto AddSymbols = [&](ArrayRef<SymbolAndSignals> NewSymbols) {
+    // Synchronize set accesses.
+    std::unique_lock<std::mutex> LockGuard(SymbolMutex);
+    for (const auto &Symbol : NewSymbols) {
+      Symbols[Symbol.Symbol] += Symbol.Signals;
+    }
+  };
+
+  // Load all symbol files in MergeDir.
+  {
+    llvm::ThreadPool Pool;
+    for (llvm::sys::fs::directory_iterator Dir(MergeDir, EC), DirEnd;
+         Dir != DirEnd && !EC; Dir.increment(EC)) {
+      // Parse YAML files in parallel.
+      Pool.async(
+          [&AddSymbols](std::string Path) {
+            auto Buffer = llvm::MemoryBuffer::getFile(Path);
+            if (!Buffer) {
+              llvm::errs() << "Can't open " << Path << "\n";
+              return;
+            }
+            std::vector<SymbolAndSignals> Symbols =
+                ReadSymbolInfosFromYAML(Buffer.get()->getBuffer());
+            for (auto &Symbol : Symbols) {
+              // Only count one occurrence per file, to avoid spam.
+              Symbol.Signals.Seen = std::min(Symbol.Signals.Seen, 1u);
+              Symbol.Signals.Used = std::min(Symbol.Signals.Used, 1u);
+            }
+            // FIXME: Merge without creating such a heavy contention point.
+            AddSymbols(Symbols);
+          },
+          Dir->path());
+    }
+  }
+
+  llvm::raw_fd_ostream OS(OutputFile, EC, llvm::sys::fs::F_None);
+  if (EC) {
+    llvm::errs() << "Can't open '" << OutputFile << "': " << EC.message()
+                 << '\n';
+    return false;
+  }
+  WriteSymbolInfosToStream(OS, Symbols);
+  return true;
+}
+
+} // namespace clang
+} // namespace find_all_symbols
+
+int main(int argc, const char **argv) {
+  CommonOptionsParser OptionsParser(argc, argv, FindAllSymbolsCategory);
+  ClangTool Tool(OptionsParser.getCompilations(),
+                 OptionsParser.getSourcePathList());
+
+  std::vector<std::string> sources = OptionsParser.getSourcePathList();
+  if (sources.empty()) {
+    llvm::errs() << "Must specify at least one one source file.\n";
+    return 1;
+  }
+  if (!MergeDir.empty()) {
+    clang::find_all_symbols::Merge(MergeDir, sources[0]);
+    return 0;
+  }
+
+  clang::find_all_symbols::YamlReporter Reporter;
+
+  auto Factory =
+      llvm::make_unique<clang::find_all_symbols::FindAllSymbolsActionFactory>(
+          &Reporter, clang::find_all_symbols::getSTLPostfixHeaderMap());
+  return Tool.run(Factory.get());
+}
diff --git a/include-fixer/find-all-symbols/tool/run-find-all-symbols.py b/include-fixer/find-all-symbols/tool/run-find-all-symbols.py
new file mode 100755 (executable)
index 0000000..461d959
--- /dev/null
@@ -0,0 +1,124 @@
+#!/usr/bin/env python
+#
+#=- run-find-all-symbols.py - Parallel find-all-symbols runner -*- python  -*-=#
+#
+#                     The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+#
+#===------------------------------------------------------------------------===#
+
+"""
+Parallel find-all-symbols runner
+================================
+
+Runs find-all-symbols over all files in a compilation database.
+
+Example invocations.
+- Run find-all-symbols on all files in the current working directory.
+    run-find-all-symbols.py <source-file>
+
+Compilation database setup:
+http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html
+"""
+
+import argparse
+import json
+import multiprocessing
+import os
+import Queue
+import shutil
+import subprocess
+import sys
+import tempfile
+import threading
+
+
+def find_compilation_database(path):
+  """Adjusts the directory until a compilation database is found."""
+  result = './'
+  while not os.path.isfile(os.path.join(result, path)):
+    if os.path.realpath(result) == '/':
+      print 'Error: could not find compilation database.'
+      sys.exit(1)
+    result += '../'
+  return os.path.realpath(result)
+
+
+def MergeSymbols(directory, args):
+  """Merge all symbol files (yaml) in a given directaory into a single file."""
+  invocation = [args.binary, '-merge-dir='+directory, args.saving_path]
+  subprocess.call(invocation)
+  print 'Merge is finished. Saving results in ' + args.saving_path
+
+
+def run_find_all_symbols(args, tmpdir, build_path, queue):
+  """Takes filenames out of queue and runs find-all-symbols on them."""
+  while True:
+    name = queue.get()
+    invocation = [args.binary, name, '-output-dir='+tmpdir, '-p='+build_path]
+    sys.stdout.write(' '.join(invocation) + '\n')
+    subprocess.call(invocation)
+    queue.task_done()
+
+
+def main():
+  parser = argparse.ArgumentParser(description='Runs find-all-symbols over all'
+                                   'files in a compilation database.')
+  parser.add_argument('-binary', metavar='PATH',
+                      default='./bin/find-all-symbols',
+                      help='path to find-all-symbols binary')
+  parser.add_argument('-j', type=int, default=0,
+                      help='number of instances to be run in parallel.')
+  parser.add_argument('-p', dest='build_path',
+                      help='path used to read a compilation database.')
+  parser.add_argument('-saving-path', default='./find_all_symbols_db.yaml',
+                      help='result saving path')
+  args = parser.parse_args()
+
+  db_path = 'compile_commands.json'
+
+  if args.build_path is not None:
+    build_path = args.build_path
+  else:
+    build_path = find_compilation_database(db_path)
+
+  tmpdir = tempfile.mkdtemp()
+
+  # Load the database and extract all files.
+  database = json.load(open(os.path.join(build_path, db_path)))
+  files = [entry['file'] for entry in database]
+
+  max_task = args.j
+  if max_task == 0:
+    max_task = multiprocessing.cpu_count()
+
+  try:
+    # Spin up a bunch of tidy-launching threads.
+    queue = Queue.Queue(max_task)
+    for _ in range(max_task):
+      t = threading.Thread(target=run_find_all_symbols,
+                           args=(args, tmpdir, build_path, queue))
+      t.daemon = True
+      t.start()
+
+    # Fill the queue with files.
+    for name in files:
+      queue.put(name)
+
+    # Wait for all threads to be done.
+    queue.join()
+
+    MergeSymbols(tmpdir, args)
+
+
+  except KeyboardInterrupt:
+    # This is a sad hack. Unfortunately subprocess goes
+    # bonkers with ctrl-c and we start forking merrily.
+    print '\nCtrl-C detected, goodbye.'
+    os.kill(0, 9)
+
+
+if __name__ == '__main__':
+  main()
diff --git a/include-fixer/plugin/CMakeLists.txt b/include-fixer/plugin/CMakeLists.txt
new file mode 100644 (file)
index 0000000..df792ea
--- /dev/null
@@ -0,0 +1,13 @@
+add_clang_library(clangIncludeFixerPlugin
+  IncludeFixerPlugin.cpp
+
+  LINK_LIBS
+  clangAST
+  clangBasic
+  clangFrontend
+  clangIncludeFixer
+  clangParse
+  clangSema
+  clangTooling
+  ${LLVM_PTHREAD_LIB}
+  )
diff --git a/include-fixer/plugin/IncludeFixerPlugin.cpp b/include-fixer/plugin/IncludeFixerPlugin.cpp
new file mode 100644 (file)
index 0000000..0d6bdb7
--- /dev/null
@@ -0,0 +1,100 @@
+//===- IncludeFixerPlugin.cpp - clang-include-fixer as a clang plugin -----===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "../IncludeFixer.h"
+#include "../YamlSymbolIndex.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendPluginRegistry.h"
+#include "clang/Parse/ParseAST.h"
+#include "clang/Sema/Sema.h"
+#include "llvm/Support/Path.h"
+
+namespace clang {
+namespace include_fixer {
+
+/// The core include fixer plugin action. This just provides the AST consumer
+/// and command line flag parsing for using include fixer as a clang plugin.
+class ClangIncludeFixerPluginAction : public PluginASTAction {
+  /// ASTConsumer to keep the symbol index alive. We don't really need an
+  /// ASTConsumer for this plugin (everything is funneled on the side through
+  /// Sema) but we have to keep the symbol index alive until sema is done.
+  struct ASTConsumerManagerWrapper : public ASTConsumer {
+    ASTConsumerManagerWrapper(std::shared_ptr<SymbolIndexManager> SIM)
+        : SymbolIndexMgr(std::move(SIM)) {}
+    std::shared_ptr<SymbolIndexManager> SymbolIndexMgr;
+  };
+
+public:
+  explicit ClangIncludeFixerPluginAction()
+      : SymbolIndexMgr(std::make_shared<SymbolIndexManager>()),
+        SemaSource(new IncludeFixerSemaSource(*SymbolIndexMgr,
+                                              /*MinimizeIncludePaths=*/true,
+                                              /*GenerateDiagnostics=*/true)) {}
+
+  std::unique_ptr<clang::ASTConsumer>
+  CreateASTConsumer(clang::CompilerInstance &CI, StringRef InFile) override {
+    CI.setExternalSemaSource(SemaSource);
+    SemaSource->setFilePath(InFile);
+    SemaSource->setCompilerInstance(&CI);
+    return llvm::make_unique<ASTConsumerManagerWrapper>(SymbolIndexMgr);
+  }
+
+  void ExecuteAction() override {} // Do nothing.
+
+  bool ParseArgs(const CompilerInstance &CI,
+                 const std::vector<std::string> &Args) override {
+    StringRef DB = "yaml";
+    StringRef Input;
+
+    // Parse the extra command line args.
+    // FIXME: This is very limited at the moment.
+    for (StringRef Arg : Args) {
+      if (Arg.startswith("-db="))
+        DB = Arg.substr(strlen("-db="));
+      else if (Arg.startswith("-input="))
+        Input = Arg.substr(strlen("-input="));
+    }
+
+    std::string InputFile = CI.getFrontendOpts().Inputs[0].getFile();
+    auto CreateYamlIdx = [=]() -> std::unique_ptr<include_fixer::SymbolIndex> {
+      llvm::ErrorOr<std::unique_ptr<include_fixer::YamlSymbolIndex>> SymbolIdx(
+          nullptr);
+      if (DB == "yaml") {
+        if (!Input.empty()) {
+          SymbolIdx = include_fixer::YamlSymbolIndex::createFromFile(Input);
+        } else {
+          // If we don't have any input file, look in the directory of the first
+          // file and its parents.
+          SmallString<128> AbsolutePath(tooling::getAbsolutePath(InputFile));
+          StringRef Directory = llvm::sys::path::parent_path(AbsolutePath);
+          SymbolIdx = include_fixer::YamlSymbolIndex::createFromDirectory(
+              Directory, "find_all_symbols_db.yaml");
+        }
+      }
+      return std::move(*SymbolIdx);
+    };
+
+    SymbolIndexMgr->addSymbolIndex(std::move(CreateYamlIdx));
+    return true;
+  }
+
+private:
+  std::shared_ptr<SymbolIndexManager> SymbolIndexMgr;
+  IntrusiveRefCntPtr<IncludeFixerSemaSource> SemaSource;
+};
+} // namespace include_fixer
+} // namespace clang
+
+// This anchor is used to force the linker to link in the generated object file
+// and thus register the include fixer plugin.
+volatile int ClangIncludeFixerPluginAnchorSource = 0;
+
+static clang::FrontendPluginRegistry::Add<
+    clang::include_fixer::ClangIncludeFixerPluginAction>
+    X("clang-include-fixer", "clang-include-fixer");
diff --git a/include-fixer/tool/CMakeLists.txt b/include-fixer/tool/CMakeLists.txt
new file mode 100644 (file)
index 0000000..a07a684
--- /dev/null
@@ -0,0 +1,26 @@
+include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..)
+
+add_clang_executable(clang-include-fixer
+  ClangIncludeFixer.cpp
+  )
+
+target_link_libraries(clang-include-fixer
+  clangBasic
+  clangFormat
+  clangFrontend
+  clangIncludeFixer
+  clangRewrite
+  clangTooling
+  clangToolingCore
+  findAllSymbols
+  )
+
+install(TARGETS clang-include-fixer
+  RUNTIME DESTINATION bin)
+
+install(PROGRAMS clang-include-fixer.el
+  DESTINATION share/clang
+  COMPONENT clang-include-fixer)
+install(PROGRAMS clang-include-fixer.py
+  DESTINATION share/clang
+  COMPONENT clang-include-fixer)
diff --git a/include-fixer/tool/ClangIncludeFixer.cpp b/include-fixer/tool/ClangIncludeFixer.cpp
new file mode 100644 (file)
index 0000000..2f2e45e
--- /dev/null
@@ -0,0 +1,471 @@
+//===-- ClangIncludeFixer.cpp - Standalone include fixer ------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "FuzzySymbolIndex.h"
+#include "InMemorySymbolIndex.h"
+#include "IncludeFixer.h"
+#include "IncludeFixerContext.h"
+#include "SymbolIndexManager.h"
+#include "YamlSymbolIndex.h"
+#include "clang/Format/Format.h"
+#include "clang/Frontend/TextDiagnosticPrinter.h"
+#include "clang/Rewrite/Core/Rewriter.h"
+#include "clang/Tooling/CommonOptionsParser.h"
+#include "clang/Tooling/Core/Replacement.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/YAMLTraits.h"
+
+using namespace clang;
+using namespace llvm;
+using clang::include_fixer::IncludeFixerContext;
+
+LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(IncludeFixerContext)
+LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(IncludeFixerContext::HeaderInfo)
+LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(IncludeFixerContext::QuerySymbolInfo)
+
+namespace llvm {
+namespace yaml {
+
+template <> struct MappingTraits<tooling::Range> {
+  struct NormalizedRange {
+    NormalizedRange(const IO &) : Offset(0), Length(0) {}
+
+    NormalizedRange(const IO &, const tooling::Range &R)
+        : Offset(R.getOffset()), Length(R.getLength()) {}
+
+    tooling::Range denormalize(const IO &) {
+      return tooling::Range(Offset, Length);
+    }
+
+    unsigned Offset;
+    unsigned Length;
+  };
+  static void mapping(IO &IO, tooling::Range &Info) {
+    MappingNormalization<NormalizedRange, tooling::Range> Keys(IO, Info);
+    IO.mapRequired("Offset", Keys->Offset);
+    IO.mapRequired("Length", Keys->Length);
+  }
+};
+
+template <> struct MappingTraits<IncludeFixerContext::HeaderInfo> {
+  static void mapping(IO &io, IncludeFixerContext::HeaderInfo &Info) {
+    io.mapRequired("Header", Info.Header);
+    io.mapRequired("QualifiedName", Info.QualifiedName);
+  }
+};
+
+template <> struct MappingTraits<IncludeFixerContext::QuerySymbolInfo> {
+  static void mapping(IO &io, IncludeFixerContext::QuerySymbolInfo &Info) {
+    io.mapRequired("RawIdentifier", Info.RawIdentifier);
+    io.mapRequired("Range", Info.Range);
+  }
+};
+
+template <> struct MappingTraits<IncludeFixerContext> {
+  static void mapping(IO &IO, IncludeFixerContext &Context) {
+    IO.mapRequired("QuerySymbolInfos", Context.QuerySymbolInfos);
+    IO.mapRequired("HeaderInfos", Context.HeaderInfos);
+    IO.mapRequired("FilePath", Context.FilePath);
+  }
+};
+} // namespace yaml
+} // namespace llvm
+
+namespace {
+cl::OptionCategory IncludeFixerCategory("Tool options");
+
+enum DatabaseFormatTy {
+  fixed,     ///< Hard-coded mapping.
+  yaml,      ///< Yaml database created by find-all-symbols.
+  fuzzyYaml, ///< Yaml database with fuzzy-matched identifiers.
+};
+
+cl::opt<DatabaseFormatTy> DatabaseFormat(
+    "db", cl::desc("Specify input format"),
+    cl::values(clEnumVal(fixed, "Hard-coded mapping"),
+               clEnumVal(yaml, "Yaml database created by find-all-symbols"),
+               clEnumVal(fuzzyYaml, "Yaml database, with fuzzy-matched names")),
+    cl::init(yaml), cl::cat(IncludeFixerCategory));
+
+cl::opt<std::string> Input("input",
+                           cl::desc("String to initialize the database"),
+                           cl::cat(IncludeFixerCategory));
+
+cl::opt<std::string>
+    QuerySymbol("query-symbol",
+                 cl::desc("Query a given symbol (e.g. \"a::b::foo\") in\n"
+                          "database directly without parsing the file."),
+                 cl::cat(IncludeFixerCategory));
+
+cl::opt<bool>
+    MinimizeIncludePaths("minimize-paths",
+                         cl::desc("Whether to minimize added include paths"),
+                         cl::init(true), cl::cat(IncludeFixerCategory));
+
+cl::opt<bool> Quiet("q", cl::desc("Reduce terminal output"), cl::init(false),
+                    cl::cat(IncludeFixerCategory));
+
+cl::opt<bool>
+    STDINMode("stdin",
+              cl::desc("Override source file's content (in the overlaying\n"
+                       "virtual file system) with input from <stdin> and run\n"
+                       "the tool on the new content with the compilation\n"
+                       "options of the source file. This mode is currently\n"
+                       "used for editor integration."),
+              cl::init(false), cl::cat(IncludeFixerCategory));
+
+cl::opt<bool> OutputHeaders(
+    "output-headers",
+    cl::desc("Print the symbol being queried and all its relevant headers in\n"
+             "JSON format to stdout:\n"
+             "  {\n"
+             "    \"FilePath\": \"/path/to/foo.cc\",\n"
+             "    \"QuerySymbolInfos\": [\n"
+             "       {\"RawIdentifier\": \"foo\",\n"
+             "        \"Range\": {\"Offset\": 0, \"Length\": 3}}\n"
+             "    ],\n"
+             "    \"HeaderInfos\": [ {\"Header\": \"\\\"foo_a.h\\\"\",\n"
+             "                      \"QualifiedName\": \"a::foo\"} ]\n"
+             "  }"),
+    cl::init(false), cl::cat(IncludeFixerCategory));
+
+cl::opt<std::string> InsertHeader(
+    "insert-header",
+    cl::desc("Insert a specific header. This should run with STDIN mode.\n"
+             "The result is written to stdout. It is currently used for\n"
+             "editor integration. Support YAML/JSON format:\n"
+             "  -insert-header=\"{\n"
+             "     FilePath: \"/path/to/foo.cc\",\n"
+             "     QuerySymbolInfos: [\n"
+             "       {RawIdentifier: foo,\n"
+             "        Range: {Offset: 0, Length: 3}}\n"
+             "     ],\n"
+             "     HeaderInfos: [ {Headers: \"\\\"foo_a.h\\\"\",\n"
+             "                     QualifiedName: \"a::foo\"} ]}\""),
+    cl::init(""), cl::cat(IncludeFixerCategory));
+
+cl::opt<std::string>
+    Style("style",
+          cl::desc("Fallback style for reformatting after inserting new\n"
+                   "headers if there is no clang-format config file found."),
+          cl::init("llvm"), cl::cat(IncludeFixerCategory));
+
+std::unique_ptr<include_fixer::SymbolIndexManager>
+createSymbolIndexManager(StringRef FilePath) {
+  using find_all_symbols::SymbolInfo;
+
+  auto SymbolIndexMgr = llvm::make_unique<include_fixer::SymbolIndexManager>();
+  switch (DatabaseFormat) {
+  case fixed: {
+    // Parse input and fill the database with it.
+    // <symbol>=<header><, header...>
+    // Multiple symbols can be given, separated by semicolons.
+    std::map<std::string, std::vector<std::string>> SymbolsMap;
+    SmallVector<StringRef, 4> SemicolonSplits;
+    StringRef(Input).split(SemicolonSplits, ";");
+    std::vector<find_all_symbols::SymbolAndSignals> Symbols;
+    for (StringRef Pair : SemicolonSplits) {
+      auto Split = Pair.split('=');
+      std::vector<std::string> Headers;
+      SmallVector<StringRef, 4> CommaSplits;
+      Split.second.split(CommaSplits, ",");
+      for (size_t I = 0, E = CommaSplits.size(); I != E; ++I)
+        Symbols.push_back(
+            {SymbolInfo(Split.first.trim(), SymbolInfo::SymbolKind::Unknown,
+                        CommaSplits[I].trim(), {}),
+             // Use fake "seen" signal for tests, so first header wins.
+             SymbolInfo::Signals(/*Seen=*/static_cast<unsigned>(E - I),
+                                 /*Used=*/0)});
+    }
+    SymbolIndexMgr->addSymbolIndex([=]() {
+      return llvm::make_unique<include_fixer::InMemorySymbolIndex>(Symbols);
+    });
+    break;
+  }
+  case yaml: {
+    auto CreateYamlIdx = [=]() -> std::unique_ptr<include_fixer::SymbolIndex> {
+      llvm::ErrorOr<std::unique_ptr<include_fixer::YamlSymbolIndex>> DB(
+          nullptr);
+      if (!Input.empty()) {
+        DB = include_fixer::YamlSymbolIndex::createFromFile(Input);
+      } else {
+        // If we don't have any input file, look in the directory of the
+        // first
+        // file and its parents.
+        SmallString<128> AbsolutePath(tooling::getAbsolutePath(FilePath));
+        StringRef Directory = llvm::sys::path::parent_path(AbsolutePath);
+        DB = include_fixer::YamlSymbolIndex::createFromDirectory(
+            Directory, "find_all_symbols_db.yaml");
+      }
+
+      if (!DB) {
+        llvm::errs() << "Couldn't find YAML db: " << DB.getError().message()
+                     << '\n';
+        return nullptr;
+      }
+      return std::move(*DB);
+    };
+
+    SymbolIndexMgr->addSymbolIndex(std::move(CreateYamlIdx));
+    break;
+  }
+  case fuzzyYaml: {
+    // This mode is not very useful, because we don't correct the identifier.
+    // It's main purpose is to expose FuzzySymbolIndex to tests.
+    SymbolIndexMgr->addSymbolIndex(
+        []() -> std::unique_ptr<include_fixer::SymbolIndex> {
+          auto DB = include_fixer::FuzzySymbolIndex::createFromYAML(Input);
+          if (!DB) {
+            llvm::errs() << "Couldn't load fuzzy YAML db: "
+                         << llvm::toString(DB.takeError()) << '\n';
+            return nullptr;
+          }
+          return std::move(*DB);
+        });
+    break;
+  }
+  }
+  return SymbolIndexMgr;
+}
+
+void writeToJson(llvm::raw_ostream &OS, const IncludeFixerContext& Context) {
+  OS << "{\n"
+     << "  \"FilePath\": \""
+     << llvm::yaml::escape(Context.getFilePath()) << "\",\n"
+     << "  \"QuerySymbolInfos\": [\n";
+  for (const auto &Info : Context.getQuerySymbolInfos()) {
+    OS << "     {\"RawIdentifier\": \"" << Info.RawIdentifier << "\",\n";
+    OS << "      \"Range\":{";
+    OS << "\"Offset\":" << Info.Range.getOffset() << ",";
+    OS << "\"Length\":" << Info.Range.getLength() << "}}";
+    if (&Info != &Context.getQuerySymbolInfos().back())
+      OS << ",\n";
+  }
+  OS << "\n  ],\n";
+  OS << "  \"HeaderInfos\": [\n";
+  const auto &HeaderInfos = Context.getHeaderInfos();
+  for (const auto &Info : HeaderInfos) {
+    OS << "     {\"Header\": \"" << llvm::yaml::escape(Info.Header) << "\",\n"
+       << "      \"QualifiedName\": \"" << Info.QualifiedName << "\"}";
+    if (&Info != &HeaderInfos.back())
+      OS << ",\n";
+  }
+  OS << "\n";
+  OS << "  ]\n";
+  OS << "}\n";
+}
+
+int includeFixerMain(int argc, const char **argv) {
+  tooling::CommonOptionsParser options(argc, argv, IncludeFixerCategory);
+  tooling::ClangTool tool(options.getCompilations(),
+                          options.getSourcePathList());
+
+  llvm::StringRef SourceFilePath = options.getSourcePathList().front();
+  // In STDINMode, we override the file content with the <stdin> input.
+  // Since `tool.mapVirtualFile` takes `StringRef`, we define `Code` outside of
+  // the if-block so that `Code` is not released after the if-block.
+  std::unique_ptr<llvm::MemoryBuffer> Code;
+  if (STDINMode) {
+    assert(options.getSourcePathList().size() == 1 &&
+           "Expect exactly one file path in STDINMode.");
+    llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> CodeOrErr =
+        MemoryBuffer::getSTDIN();
+    if (std::error_code EC = CodeOrErr.getError()) {
+      errs() << EC.message() << "\n";
+      return 1;
+    }
+    Code = std::move(CodeOrErr.get());
+    if (Code->getBufferSize() == 0)
+      return 0;  // Skip empty files.
+
+    tool.mapVirtualFile(SourceFilePath, Code->getBuffer());
+  }
+
+  if (!InsertHeader.empty()) {
+    if (!STDINMode) {
+      errs() << "Should be running in STDIN mode\n";
+      return 1;
+    }
+
+    llvm::yaml::Input yin(InsertHeader);
+    IncludeFixerContext Context;
+    yin >> Context;
+
+    const auto &HeaderInfos = Context.getHeaderInfos();
+    assert(!HeaderInfos.empty());
+    // We only accept one unique header.
+    // Check all elements in HeaderInfos have the same header.
+    bool IsUniqueHeader = std::equal(
+        HeaderInfos.begin()+1, HeaderInfos.end(), HeaderInfos.begin(),
+        [](const IncludeFixerContext::HeaderInfo &LHS,
+           const IncludeFixerContext::HeaderInfo &RHS) {
+          return LHS.Header == RHS.Header;
+        });
+    if (!IsUniqueHeader) {
+      errs() << "Expect exactly one unique header.\n";
+      return 1;
+    }
+
+    // If a header has multiple symbols, we won't add the missing namespace
+    // qualifiers because we don't know which one is exactly used.
+    //
+    // Check whether all elements in HeaderInfos have the same qualified name.
+    bool IsUniqueQualifiedName = std::equal(
+        HeaderInfos.begin() + 1, HeaderInfos.end(), HeaderInfos.begin(),
+        [](const IncludeFixerContext::HeaderInfo &LHS,
+           const IncludeFixerContext::HeaderInfo &RHS) {
+          return LHS.QualifiedName == RHS.QualifiedName;
+        });
+    auto InsertStyle = format::getStyle("file", Context.getFilePath(), Style);
+    if (!InsertStyle) {
+      llvm::errs() << llvm::toString(InsertStyle.takeError()) << "\n";
+      return 1;
+    }
+    auto Replacements = clang::include_fixer::createIncludeFixerReplacements(
+        Code->getBuffer(), Context, *InsertStyle,
+        /*AddQualifiers=*/IsUniqueQualifiedName);
+    if (!Replacements) {
+      errs() << "Failed to create replacements: "
+             << llvm::toString(Replacements.takeError()) << "\n";
+      return 1;
+    }
+
+    auto ChangedCode =
+        tooling::applyAllReplacements(Code->getBuffer(), *Replacements);
+    if (!ChangedCode) {
+      llvm::errs() << llvm::toString(ChangedCode.takeError()) << "\n";
+      return 1;
+    }
+    llvm::outs() << *ChangedCode;
+    return 0;
+  }
+
+  // Set up data source.
+  std::unique_ptr<include_fixer::SymbolIndexManager> SymbolIndexMgr =
+      createSymbolIndexManager(SourceFilePath);
+  if (!SymbolIndexMgr)
+    return 1;
+
+  // Query symbol mode.
+  if (!QuerySymbol.empty()) {
+    auto MatchedSymbols = SymbolIndexMgr->search(
+        QuerySymbol, /*IsNestedSearch=*/true, SourceFilePath);
+    for (auto &Symbol : MatchedSymbols) {
+      std::string HeaderPath = Symbol.getFilePath().str();
+      Symbol.SetFilePath(((HeaderPath[0] == '"' || HeaderPath[0] == '<')
+                              ? HeaderPath
+                              : "\"" + HeaderPath + "\""));
+    }
+
+    // We leave an empty symbol range as we don't know the range of the symbol
+    // being queried in this mode. include-fixer won't add namespace qualifiers
+    // if the symbol range is empty, which also fits this case.
+    IncludeFixerContext::QuerySymbolInfo Symbol;
+    Symbol.RawIdentifier = QuerySymbol;
+    auto Context =
+        IncludeFixerContext(SourceFilePath, {Symbol}, MatchedSymbols);
+    writeToJson(llvm::outs(), Context);
+    return 0;
+  }
+
+  // Now run our tool.
+  std::vector<include_fixer::IncludeFixerContext> Contexts;
+  include_fixer::IncludeFixerActionFactory Factory(*SymbolIndexMgr, Contexts,
+                                                   Style, MinimizeIncludePaths);
+
+  if (tool.run(&Factory) != 0) {
+    // We suppress all Clang diagnostics (because they would be wrong,
+    // include-fixer does custom recovery) but still want to give some feedback
+    // in case there was a compiler error we couldn't recover from. The most
+    // common case for this is a #include in the file that couldn't be found.
+    llvm::errs() << "Fatal compiler error occurred while parsing file!"
+                    " (incorrect include paths?)\n";
+    return 1;
+  }
+
+  assert(!Contexts.empty());
+
+  if (OutputHeaders) {
+    // FIXME: Print contexts of all processing files instead of the first one.
+    writeToJson(llvm::outs(), Contexts.front());
+    return 0;
+  }
+
+  std::vector<tooling::Replacements> FixerReplacements;
+  for (const auto &Context : Contexts) {
+    StringRef FilePath = Context.getFilePath();
+    auto InsertStyle = format::getStyle("file", FilePath, Style);
+    if (!InsertStyle) {
+      llvm::errs() << llvm::toString(InsertStyle.takeError()) << "\n";
+      return 1;
+    }
+    auto Buffer = llvm::MemoryBuffer::getFile(FilePath);
+    if (!Buffer) {
+      errs() << "Couldn't open file: " + FilePath.str() + ": "
+             << Buffer.getError().message() + "\n";
+      return 1;
+    }
+
+    auto Replacements = clang::include_fixer::createIncludeFixerReplacements(
+        Buffer.get()->getBuffer(), Context, *InsertStyle);
+    if (!Replacements) {
+      errs() << "Failed to create replacement: "
+             << llvm::toString(Replacements.takeError()) << "\n";
+      return 1;
+    }
+    FixerReplacements.push_back(*Replacements);
+  }
+
+  if (!Quiet) {
+    for (const auto &Context : Contexts) {
+      if (!Context.getHeaderInfos().empty()) {
+        llvm::errs() << "Added #include "
+                     << Context.getHeaderInfos().front().Header << " for "
+                     << Context.getFilePath() << "\n";
+      }
+    }
+  }
+
+  if (STDINMode) {
+    assert(FixerReplacements.size() == 1);
+    auto ChangedCode = tooling::applyAllReplacements(Code->getBuffer(),
+                                                     FixerReplacements.front());
+    if (!ChangedCode) {
+      llvm::errs() << llvm::toString(ChangedCode.takeError()) << "\n";
+      return 1;
+    }
+    llvm::outs() << *ChangedCode;
+    return 0;
+  }
+
+  // Set up a new source manager for applying the resulting replacements.
+  IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(new DiagnosticOptions);
+  DiagnosticsEngine Diagnostics(new DiagnosticIDs, &*DiagOpts);
+  TextDiagnosticPrinter DiagnosticPrinter(outs(), &*DiagOpts);
+  SourceManager SM(Diagnostics, tool.getFiles());
+  Diagnostics.setClient(&DiagnosticPrinter, false);
+
+  // Write replacements to disk.
+  Rewriter Rewrites(SM, LangOptions());
+  for (const auto &Replacement : FixerReplacements) {
+    if (!tooling::applyAllReplacements(Replacement, Rewrites)) {
+      llvm::errs() << "Failed to apply replacements.\n";
+      return 1;
+    }
+  }
+  return Rewrites.overwriteChangedFiles();
+}
+
+} // namespace
+
+int main(int argc, const char **argv) {
+  return includeFixerMain(argc, argv);
+}
diff --git a/include-fixer/tool/clang-include-fixer-test.el b/include-fixer/tool/clang-include-fixer-test.el
new file mode 100644 (file)
index 0000000..d66d530
--- /dev/null
@@ -0,0 +1,65 @@
+;;; clang-include-fixer-test.el --- unit tests for clang-include-fixer.el  -*- lexical-binding: t; -*-
+
+;;; Commentary:
+
+;; Unit tests for clang-include-fixer.el.
+
+;;; Code:
+
+(require 'clang-include-fixer)
+
+(require 'cc-mode)
+(require 'ert)
+
+(ert-deftest clang-include-fixer--insert-line ()
+  "Unit test for `clang-include-fixer--insert-line'."
+  (with-temp-buffer
+    (insert "aa\nab\nac\nad\n")
+    (let ((from (current-buffer)))
+      (with-temp-buffer
+        (insert "aa\nac\nad\n")
+        (let ((to (current-buffer)))
+          (should (clang-include-fixer--insert-line from to))
+          (should (equal (buffer-string) "aa\nab\nac\nad\n")))))
+    (should (equal (buffer-string) "aa\nab\nac\nad\n"))))
+
+(ert-deftest clang-include-fixer--insert-line-diff-on-empty-line ()
+  "Unit test for `clang-include-fixer--insert-line'."
+  (with-temp-buffer
+    (insert "aa\nab\n\nac\nad\n")
+    (let ((from (current-buffer)))
+      (with-temp-buffer
+        (insert "aa\n\nac\nad\n")
+        (let ((to (current-buffer)))
+          (should (clang-include-fixer--insert-line from to))
+          (should (equal (buffer-string) "aa\nab\n\nac\nad\n")))))
+    (should (equal (buffer-string) "aa\nab\n\nac\nad\n"))))
+
+(ert-deftest clang-include-fixer--symbol-at-point ()
+  "Unit test for `clang-include-fixer--symbol-at-point'."
+  (with-temp-buffer
+    (insert "a+bbb::cc")
+    (c++-mode)
+    (goto-char (point-min))
+    (should (equal (clang-include-fixer--symbol-at-point) "a"))
+    (forward-char)
+    ;; Emacs treats the character immediately following a symbol as part of the
+    ;; symbol.
+    (should (equal (clang-include-fixer--symbol-at-point) "a"))
+    (forward-char)
+    (should (equal (clang-include-fixer--symbol-at-point) "bbb::cc"))
+    (goto-char (point-max))
+    (should (equal (clang-include-fixer--symbol-at-point) "bbb::cc"))))
+
+(ert-deftest clang-include-fixer--highlight ()
+  (with-temp-buffer
+    (insert "util::Status foo;\n")
+    (setq buffer-file-coding-system 'utf-8-unix)
+    (should (equal nil (clang-include-fixer--highlight
+                        '((Range . ((Offset . 0) (Length . 0)))))))
+    (let ((overlay (clang-include-fixer--highlight
+                    '((Range . ((Offset . 1) (Length . 12)))))))
+      (should (equal 2 (overlay-start overlay)))
+      (should (equal 14 (overlay-end overlay))))))
+
+;;; clang-include-fixer-test.el ends here
diff --git a/include-fixer/tool/clang-include-fixer.el b/include-fixer/tool/clang-include-fixer.el
new file mode 100644 (file)
index 0000000..ab10002
--- /dev/null
@@ -0,0 +1,446 @@
+;;; clang-include-fixer.el --- Emacs integration of the clang include fixer  -*- lexical-binding: t; -*-
+
+;; Keywords: tools, c
+;; Package-Requires: ((cl-lib "0.5") (json "1.2") (let-alist "1.0.4"))
+
+;;; Commentary:
+
+;; This package allows Emacs users to invoke the 'clang-include-fixer' within
+;; Emacs.  'clang-include-fixer' provides an automated way of adding #include
+;; directives for missing symbols in one translation unit, see
+;; <http://clang.llvm.org/extra/include-fixer.html>.
+
+;;; Code:
+
+(require 'cl-lib)
+(require 'json)
+(require 'let-alist)
+
+(defgroup clang-include-fixer nil
+  "Clang-based include fixer."
+  :group 'tools)
+
+(defvar clang-include-fixer-add-include-hook nil
+  "A hook that will be called for every added include.
+The first argument is the filename of the include, the second argument is
+non-nil if the include is a system-header.")
+
+(defcustom clang-include-fixer-executable
+  "clang-include-fixer"
+  "Location of the clang-include-fixer executable.
+
+A string containing the name or the full path of the executable."
+  :group 'clang-include-fixer
+  :type '(file :must-match t)
+  :risky t)
+
+(defcustom clang-include-fixer-input-format
+  'yaml
+  "Input format for clang-include-fixer.
+This string is passed as -db argument to
+`clang-include-fixer-executable'."
+  :group 'clang-include-fixer
+  :type '(radio
+          (const :tag "Hard-coded mapping" :fixed)
+          (const :tag "YAML" yaml)
+          (symbol :tag "Other"))
+  :risky t)
+
+(defcustom clang-include-fixer-init-string
+  ""
+  "Database initialization string for clang-include-fixer.
+This string is passed as -input argument to
+`clang-include-fixer-executable'."
+  :group 'clang-include-fixer
+  :type 'string
+  :risky t)
+
+(defface clang-include-fixer-highlight '((t :background "green"))
+  "Used for highlighting the symbol for which a header file is being added.")
+
+;;;###autoload
+(defun clang-include-fixer ()
+  "Invoke the Include Fixer to insert missing C++ headers."
+  (interactive)
+  (message (concat "Calling the include fixer. "
+                   "This might take some seconds. Please wait."))
+  (clang-include-fixer--start #'clang-include-fixer--add-header
+                              "-output-headers"))
+
+;;;###autoload
+(defun clang-include-fixer-at-point ()
+  "Invoke the Clang include fixer for the symbol at point."
+  (interactive)
+  (let ((symbol (clang-include-fixer--symbol-at-point)))
+    (unless symbol
+      (user-error "No symbol at current location"))
+    (clang-include-fixer-from-symbol symbol)))
+
+;;;###autoload
+(defun clang-include-fixer-from-symbol (symbol)
+  "Invoke the Clang include fixer for the SYMBOL.
+When called interactively, prompts the user for a symbol."
+  (interactive
+   (list (read-string "Symbol: " (clang-include-fixer--symbol-at-point))))
+  (clang-include-fixer--start #'clang-include-fixer--add-header
+                              (format "-query-symbol=%s" symbol)))
+
+(defun clang-include-fixer--start (callback &rest args)
+  "Asynchronously start clang-include-fixer with parameters ARGS.
+The current file name is passed after ARGS as last argument.  If
+the call was successful the returned result is stored in a
+temporary buffer, and CALLBACK is called with the temporary
+buffer as only argument."
+  (unless buffer-file-name
+    (user-error "clang-include-fixer works only in buffers that visit a file"))
+  (let ((process (if (fboundp 'make-process)
+                     ;; Prefer using â€˜make-process’ if available, because
+                     ;; â€˜start-process’ doesn’t allow us to separate the
+                     ;; standard error from the output.
+                     (clang-include-fixer--make-process callback args)
+                   (clang-include-fixer--start-process callback args))))
+    (save-restriction
+      (widen)
+      (process-send-region process (point-min) (point-max)))
+    (process-send-eof process))
+  nil)
+
+(defun clang-include-fixer--make-process (callback args)
+  "Start a new clang-incude-fixer process using `make-process'.
+CALLBACK is called after the process finishes successfully; it is
+called with a single argument, the buffer where standard output
+has been inserted.  ARGS is a list of additional command line
+arguments.  Return the new process object."
+  (let ((stdin (current-buffer))
+        (stdout (generate-new-buffer "*clang-include-fixer output*"))
+        (stderr (generate-new-buffer "*clang-include-fixer errors*")))
+    (make-process :name "clang-include-fixer"
+                  :buffer stdout
+                  :command (clang-include-fixer--command args)
+                  :coding 'utf-8-unix
+                  :noquery t
+                  :connection-type 'pipe
+                  :sentinel (clang-include-fixer--sentinel stdin stdout stderr
+                                                           callback)
+                  :stderr stderr)))
+
+(defun clang-include-fixer--start-process (callback args)
+  "Start a new clang-incude-fixer process using `start-process'.
+CALLBACK is called after the process finishes successfully; it is
+called with a single argument, the buffer where standard output
+has been inserted.  ARGS is a list of additional command line
+arguments.  Return the new process object."
+  (let* ((stdin (current-buffer))
+         (stdout (generate-new-buffer "*clang-include-fixer output*"))
+         (process-connection-type nil)
+         (process (apply #'start-process "clang-include-fixer" stdout
+                         (clang-include-fixer--command args))))
+    (set-process-coding-system process 'utf-8-unix 'utf-8-unix)
+    (set-process-query-on-exit-flag process nil)
+    (set-process-sentinel process
+                          (clang-include-fixer--sentinel stdin stdout nil
+                                                         callback))
+    process))
+
+(defun clang-include-fixer--command (args)
+  "Return the clang-include-fixer command line.
+Returns a list; the first element is the binary to
+execute (`clang-include-fixer-executable'), and the remaining
+elements are the command line arguments.  Adds proper arguments
+for `clang-include-fixer-input-format' and
+`clang-include-fixer-init-string'.  Appends the current buffer's
+file name; prepends ARGS directly in front of it."
+  (cl-check-type args list)
+  `(,clang-include-fixer-executable
+    ,(format "-db=%s" clang-include-fixer-input-format)
+    ,(format "-input=%s" clang-include-fixer-init-string)
+    "-stdin"
+    ,@args
+    ,(buffer-file-name)))
+
+(defun clang-include-fixer--sentinel (stdin stdout stderr callback)
+  "Return a process sentinel for clang-include-fixer processes.
+STDIN, STDOUT, and STDERR are buffers for the standard streams;
+only STDERR may be nil.  CALLBACK is called in the case of
+success; it is called with a single argument, STDOUT.  On
+failure, a buffer containing the error output is displayed."
+  (cl-check-type stdin buffer-live)
+  (cl-check-type stdout buffer-live)
+  (cl-check-type stderr (or null buffer-live))
+  (cl-check-type callback function)
+  (lambda (process event)
+    (cl-check-type process process)
+    (cl-check-type event string)
+    (unwind-protect
+        (if (string-equal event "finished\n")
+            (progn
+              (when stderr (kill-buffer stderr))
+              (with-current-buffer stdin
+                (funcall callback stdout))
+              (kill-buffer stdout))
+          (when stderr (kill-buffer stdout))
+          (message "clang-include-fixer failed")
+          (with-current-buffer (or stderr stdout)
+            (insert "\nProcess " (process-name process)
+                    ?\s event))
+          (display-buffer (or stderr stdout))))
+    nil))
+
+(defun clang-include-fixer--replace-buffer (stdout)
+  "Replace current buffer by content of STDOUT."
+  (cl-check-type stdout buffer-live)
+  (barf-if-buffer-read-only)
+  (cond ((fboundp 'replace-buffer-contents) (replace-buffer-contents stdout))
+        ((clang-include-fixer--insert-line stdout (current-buffer)))
+        (t (erase-buffer) (insert-buffer-substring stdout)))
+  (message "Fix applied")
+  nil)
+
+(defun clang-include-fixer--insert-line (from to)
+  "Insert a single missing line from the buffer FROM into TO.
+FROM and TO must be buffers.  If the contents of FROM and TO are
+equal, do nothing and return non-nil.  If FROM contains a single
+line missing from TO, insert that line into TO so that the buffer
+contents are equal and return non-nil.  Otherwise, do nothing and
+return nil.  Buffer restrictions are ignored."
+  (cl-check-type from buffer-live)
+  (cl-check-type to buffer-live)
+  (with-current-buffer from
+    (save-excursion
+      (save-restriction
+        (widen)
+        (with-current-buffer to
+          (save-excursion
+            (save-restriction
+              (widen)
+              ;; Search for the first buffer difference.
+              (let ((chars (abs (compare-buffer-substrings to nil nil from nil nil))))
+                (if (zerop chars)
+                    ;; Buffer contents are equal, nothing to do.
+                    t
+                  (goto-char chars)
+                  ;; We might have ended up in the middle of a line if the
+                  ;; current line partially matches.  In this case we would
+                  ;; have to insert more than a line.  Move to the beginning of
+                  ;; the line to avoid this situation.
+                  (beginning-of-line)
+                  (with-current-buffer from
+                    (goto-char chars)
+                    (beginning-of-line)
+                    (let ((from-begin (point))
+                          (from-end (progn (forward-line) (point)))
+                          (to-point (with-current-buffer to (point))))
+                      ;; Search for another buffer difference after the line in
+                      ;; question.  If there is none, we can proceed.
+                      (when (zerop (compare-buffer-substrings from from-end nil
+                                                              to to-point nil))
+                        (with-current-buffer to
+                          (insert-buffer-substring from from-begin from-end))
+                        t))))))))))))
+
+(defun clang-include-fixer--add-header (stdout)
+  "Analyse the result of include-fixer stored in STDOUT.
+Add a missing header if there is any.  If there are multiple
+possible headers the user can select one of them to be included.
+Temporarily highlight the affected symbols.  Asynchronously call
+clang-include-fixer to insert the selected header."
+  (cl-check-type stdout buffer-live)
+  (let ((context (clang-include-fixer--parse-json stdout)))
+    (let-alist context
+      (cond
+       ((null .QuerySymbolInfos)
+        (message "The file is fine, no need to add a header."))
+       ((null .HeaderInfos)
+        (message "Couldn't find header for '%s'"
+                 (let-alist (car .QuerySymbolInfos) .RawIdentifier)))
+       (t
+        ;; Users may C-g in prompts, make sure the process sentinel
+        ;; behaves correctly.
+        (with-local-quit
+          ;; Replace the HeaderInfos list by a single header selected by
+          ;; the user.
+          (clang-include-fixer--select-header context)
+          ;; Call clang-include-fixer again to insert the selected header.
+          (clang-include-fixer--start
+           (let ((old-tick (buffer-chars-modified-tick)))
+             (lambda (stdout)
+               (when (/= old-tick (buffer-chars-modified-tick))
+                 ;; Replacing the buffer now would undo the user’s changes.
+                 (user-error (concat "The buffer has been changed "
+                                     "before the header could be inserted")))
+               (clang-include-fixer--replace-buffer stdout)
+               (let-alist context
+                 (let-alist (car .HeaderInfos)
+                   (with-local-quit
+                     (run-hook-with-args 'clang-include-fixer-add-include-hook
+                                         (substring .Header 1 -1)
+                                         (string= (substring .Header 0 1) "<")))))))
+           (format "-insert-header=%s"
+                   (clang-include-fixer--encode-json context))))))))
+  nil)
+
+(defun clang-include-fixer--select-header (context)
+  "Prompt the user for a header if necessary.
+CONTEXT must be a clang-include-fixer context object in
+association list format.  If it contains more than one HeaderInfo
+element, prompt the user to select one of the headers.  CONTEXT
+is modified to include only the selected element."
+  (cl-check-type context cons)
+  (let-alist context
+    (if (cdr .HeaderInfos)
+        (clang-include-fixer--prompt-for-header context)
+      (message "Only one include is missing: %s"
+               (let-alist (car .HeaderInfos) .Header))))
+  nil)
+
+(defvar clang-include-fixer--history nil
+  "History for `clang-include-fixer--prompt-for-header'.")
+
+(defun clang-include-fixer--prompt-for-header (context)
+  "Prompt the user for a single header.
+The choices are taken from the HeaderInfo elements in CONTEXT.
+They are replaced by the single element selected by the user."
+  (let-alist context
+    (let ((symbol (clang-include-fixer--symbol-name .QuerySymbolInfos))
+          ;; Add temporary highlighting so that the user knows which
+          ;; symbols the current session is about.
+          (overlays (remove nil
+                            (mapcar #'clang-include-fixer--highlight .QuerySymbolInfos))))
+      (unwind-protect
+          (save-excursion
+            ;; While prompting, go to the closest overlay so that the user sees
+            ;; some context.
+            (when overlays
+              (goto-char (clang-include-fixer--closest-overlay overlays)))
+            (cl-flet ((header (info) (let-alist info .Header)))
+              ;; The header-infos is already sorted by include-fixer.
+              (let* ((header (completing-read
+                              (clang-include-fixer--format-message
+                               "Select include for '%s': " symbol)
+                              (mapcar #'header .HeaderInfos)
+                              nil :require-match nil
+                              'clang-include-fixer--history))
+                     (info (cl-find header .HeaderInfos :key #'header :test #'string=)))
+                (cl-assert info)
+                (setcar .HeaderInfos info)
+                (setcdr .HeaderInfos nil))))
+        (mapc #'delete-overlay overlays)))))
+
+(defun clang-include-fixer--symbol-name (symbol-infos)
+  "Return the unique symbol name in SYMBOL-INFOS.
+Raise a signal if the symbol name is not unique."
+  (let ((symbols (delete-dups (mapcar (lambda (info)
+                                        (let-alist info .RawIdentifier))
+                                      symbol-infos))))
+    (when (cdr symbols)
+      (error "Multiple symbols %s returned" symbols))
+    (car symbols)))
+
+(defun clang-include-fixer--highlight (symbol-info)
+  "Add an overlay to highlight SYMBOL-INFO, if it points to a non-empty range.
+Return the overlay object, or nil."
+  (let-alist symbol-info
+    (unless (zerop .Range.Length)
+      (let ((overlay (make-overlay
+                      (clang-include-fixer--filepos-to-bufferpos
+                       .Range.Offset 'approximate)
+                      (clang-include-fixer--filepos-to-bufferpos
+                       (+ .Range.Offset .Range.Length) 'approximate))))
+        (overlay-put overlay 'face 'clang-include-fixer-highlight)
+        overlay))))
+
+(defun clang-include-fixer--closest-overlay (overlays)
+  "Return the start of the overlay in OVERLAYS that is closest to point."
+  (cl-check-type overlays cons)
+  (let ((point (point))
+        acc)
+    (dolist (overlay overlays acc)
+      (let ((start (overlay-start overlay)))
+        (when (or (null acc) (< (abs (- point start)) (abs (- point acc))))
+          (setq acc start))))))
+
+(defun clang-include-fixer--parse-json (buffer)
+  "Parse a JSON response from clang-include-fixer in BUFFER.
+Return the JSON object as an association list."
+  (with-current-buffer buffer
+    (save-excursion
+      (goto-char (point-min))
+      (let ((json-object-type 'alist)
+            (json-array-type 'list)
+            (json-key-type 'symbol)
+            (json-false :json-false)
+            (json-null nil)
+            (json-pre-element-read-function nil)
+            (json-post-element-read-function nil))
+        (json-read)))))
+
+(defun clang-include-fixer--encode-json (object)
+  "Return the JSON representation of OBJECT as a string."
+  (let ((json-encoding-separator ",")
+        (json-encoding-default-indentation "  ")
+        (json-encoding-pretty-print nil)
+        (json-encoding-lisp-style-closings nil)
+        (json-encoding-object-sort-predicate nil))
+    (json-encode object)))
+
+(defun clang-include-fixer--symbol-at-point ()
+  "Return the qualified symbol at point.
+If there is no symbol at point, return nil."
+  ;; Let â€˜bounds-of-thing-at-point’ to do the hard work and deal with edge
+  ;; cases.
+  (let ((bounds (bounds-of-thing-at-point 'symbol)))
+    (when bounds
+      (let ((beg (car bounds))
+            (end (cdr bounds)))
+        (save-excursion
+          ;; Extend the symbol range to the left.  Skip over namespace
+          ;; delimiters and parent namespace names.
+          (goto-char beg)
+          (while (and (clang-include-fixer--skip-double-colon-backward)
+                      (skip-syntax-backward "w_")))
+          ;; Skip over one more namespace delimiter, for absolute names.
+          (clang-include-fixer--skip-double-colon-backward)
+          (setq beg (point))
+          ;; Extend the symbol range to the right.  Skip over namespace
+          ;; delimiters and child namespace names.
+          (goto-char end)
+          (while (and (clang-include-fixer--skip-double-colon-forward)
+                      (skip-syntax-forward "w_")))
+          (setq end (point)))
+        (buffer-substring-no-properties beg end)))))
+
+(defun clang-include-fixer--skip-double-colon-forward ()
+  "Skip a double colon.
+When the next two characters are '::', skip them and return
+non-nil.  Otherwise return nil."
+  (let ((end (+ (point) 2)))
+    (when (and (<= end (point-max))
+               (string-equal (buffer-substring-no-properties (point) end) "::"))
+      (goto-char end)
+      t)))
+
+(defun clang-include-fixer--skip-double-colon-backward ()
+  "Skip a double colon.
+When the previous two characters are '::', skip them and return
+non-nil.  Otherwise return nil."
+  (let ((beg (- (point) 2)))
+    (when (and (>= beg (point-min))
+               (string-equal (buffer-substring-no-properties beg (point)) "::"))
+      (goto-char beg)
+      t)))
+
+;; â€˜filepos-to-bufferpos’ is new in Emacs 25.1.  Provide a fallback for older
+;; versions.
+(defalias 'clang-include-fixer--filepos-to-bufferpos
+  (if (fboundp 'filepos-to-bufferpos)
+      'filepos-to-bufferpos
+    (lambda (byte &optional _quality _coding-system)
+      (byte-to-position (1+ byte)))))
+
+;; â€˜format-message’ is new in Emacs 25.1.  Provide a fallback for older
+;; versions.
+(defalias 'clang-include-fixer--format-message
+  (if (fboundp 'format-message) 'format-message 'format))
+
+(provide 'clang-include-fixer)
+;;; clang-include-fixer.el ends here
diff --git a/include-fixer/tool/clang-include-fixer.py b/include-fixer/tool/clang-include-fixer.py
new file mode 100644 (file)
index 0000000..0e9306d
--- /dev/null
@@ -0,0 +1,208 @@
+# This file is a minimal clang-include-fixer vim-integration. To install:
+# - Change 'binary' if clang-include-fixer is not on the path (see below).
+# - Add to your .vimrc:
+#
+#   noremap <leader>cf :pyf path/to/llvm/source/tools/clang/tools/extra/include-fixer/tool/clang-include-fixer.py<cr>
+#
+# This enables clang-include-fixer for NORMAL and VISUAL mode. Change "<leader>cf"
+# to another binding if you need clang-include-fixer on a different key.
+#
+# To set up clang-include-fixer, see http://clang.llvm.org/extra/include-fixer.html
+#
+# With this integration you can press the bound key and clang-include-fixer will
+# be run on the current buffer.
+#
+# It operates on the current, potentially unsaved buffer and does not create
+# or save any files. To revert a fix, just undo.
+
+import argparse
+import difflib
+import json
+import re
+import subprocess
+import vim
+
+# set g:clang_include_fixer_path to the path to clang-include-fixer if it is not
+# on the path.
+# Change this to the full path if clang-include-fixer is not on the path.
+binary = 'clang-include-fixer'
+if vim.eval('exists("g:clang_include_fixer_path")') == "1":
+  binary = vim.eval('g:clang_include_fixer_path')
+
+maximum_suggested_headers = 3
+if vim.eval('exists("g:clang_include_fixer_maximum_suggested_headers")') == "1":
+  maximum_suggested_headers = max(
+      1,
+      vim.eval('g:clang_include_fixer_maximum_suggested_headers'))
+
+increment_num = 5
+if vim.eval('exists("g:clang_include_fixer_increment_num")') == "1":
+  increment_num = max(
+      1,
+      vim.eval('g:clang_include_fixer_increment_num'))
+
+jump_to_include = False
+if vim.eval('exists("g:clang_include_fixer_jump_to_include")') == "1":
+  jump_to_include = vim.eval('g:clang_include_fixer_jump_to_include') != "0"
+
+query_mode = False
+if vim.eval('exists("g:clang_include_fixer_query_mode")') == "1":
+  query_mode = vim.eval('g:clang_include_fixer_query_mode') != "0"
+
+
+def GetUserSelection(message, headers, maximum_suggested_headers):
+  eval_message = message + '\n'
+  for idx, header in enumerate(headers[0:maximum_suggested_headers]):
+    eval_message += "({0}). {1}\n".format(idx + 1, header)
+  eval_message += "Enter (q) to quit;"
+  if maximum_suggested_headers < len(headers):
+    eval_message += " (m) to show {0} more candidates.".format(
+        min(increment_num, len(headers) - maximum_suggested_headers))
+
+  eval_message += "\nSelect (default 1): "
+  res = vim.eval("input('{0}')".format(eval_message))
+  if res == '':
+    # choose the top ranked header by default
+    idx = 1
+  elif res == 'q':
+    raise Exception('   Insertion cancelled...')
+  elif res == 'm':
+    return GetUserSelection(message,
+                            headers, maximum_suggested_headers + increment_num)
+  else:
+    try:
+      idx = int(res)
+      if idx <= 0 or idx > len(headers):
+        raise Exception()
+    except Exception:
+      # Show a new prompt on invalid option instead of aborting so that users
+      # don't need to wait for another include-fixer run.
+      print >> sys.stderr, "Invalid option:", res
+      return GetUserSelection(message, headers, maximum_suggested_headers)
+  return headers[idx - 1]
+
+
+def execute(command, text):
+  p = subprocess.Popen(command,
+                       stdout=subprocess.PIPE, stderr=subprocess.PIPE,
+                       stdin=subprocess.PIPE)
+  return p.communicate(input=text)
+
+
+def InsertHeaderToVimBuffer(header, text):
+  command = [binary, "-stdin", "-insert-header=" + json.dumps(header),
+             vim.current.buffer.name]
+  stdout, stderr = execute(command, text)
+  if stderr:
+    raise Exception(stderr)
+  if stdout:
+    lines = stdout.splitlines()
+    sequence = difflib.SequenceMatcher(None, vim.current.buffer, lines)
+    line_num = None
+    for op in reversed(sequence.get_opcodes()):
+      if op[0] != 'equal':
+        vim.current.buffer[op[1]:op[2]] = lines[op[3]:op[4]]
+      if op[0] == 'insert':
+        # line_num in vim is 1-based.
+        line_num = op[1] + 1
+
+    if jump_to_include and line_num:
+      vim.current.window.cursor = (line_num, 0)
+
+
+# The vim internal implementation (expand("cword"/"cWORD")) doesn't support
+# our use case very well, we re-implement our own one.
+def get_symbol_under_cursor():
+  line = vim.eval("line(\".\")")
+  # column number in vim is 1-based.
+  col = int(vim.eval("col(\".\")")) - 1
+  line_text = vim.eval("getline({0})".format(line))
+  if len(line_text) == 0: return ""
+  symbol_pos_begin = col
+  p = re.compile('[a-zA-Z0-9:_]')
+  while symbol_pos_begin >= 0 and p.match(line_text[symbol_pos_begin]):
+    symbol_pos_begin -= 1
+
+  symbol_pos_end = col
+  while symbol_pos_end < len(line_text) and p.match(line_text[symbol_pos_end]):
+    symbol_pos_end += 1
+  return line_text[symbol_pos_begin+1:symbol_pos_end]
+
+
+def main():
+  parser = argparse.ArgumentParser(
+      description='Vim integration for clang-include-fixer')
+  parser.add_argument('-db', default='yaml',
+                      help='clang-include-fixer input format.')
+  parser.add_argument('-input', default='',
+                      help='String to initialize the database.')
+  # Don't throw exception when parsing unknown arguements to make the script
+  # work in neovim.
+  # Neovim (at least v0.2.1) somehow mangles the sys.argv in a weird way: it
+  # will pass additional arguments (e.g. "-c script_host.py") to sys.argv,
+  # which makes the script fail.
+  args, _ = parser.parse_known_args()
+
+  # Get the current text.
+  buf = vim.current.buffer
+  text = '\n'.join(buf)
+
+  if query_mode:
+    symbol = get_symbol_under_cursor()
+    if len(symbol) == 0:
+      print "Skip querying empty symbol."
+      return
+    command = [binary, "-stdin", "-query-symbol="+get_symbol_under_cursor(),
+               "-db=" + args.db, "-input=" + args.input,
+               vim.current.buffer.name]
+  else:
+    # Run command to get all headers.
+    command = [binary, "-stdin", "-output-headers", "-db=" + args.db,
+               "-input=" + args.input, vim.current.buffer.name]
+  stdout, stderr = execute(command, text)
+  if stderr:
+    print >> sys.stderr, "Error while running clang-include-fixer: " + stderr
+    return
+
+  include_fixer_context = json.loads(stdout)
+  query_symbol_infos = include_fixer_context["QuerySymbolInfos"]
+  if not query_symbol_infos:
+    print "The file is fine, no need to add a header."
+    return
+  symbol = query_symbol_infos[0]["RawIdentifier"]
+  # The header_infos is already sorted by include-fixer.
+  header_infos = include_fixer_context["HeaderInfos"]
+  # Deduplicate headers while keeping the order, so that the same header would
+  # not be suggested twice.
+  unique_headers = []
+  seen = set()
+  for header_info in header_infos:
+    header = header_info["Header"]
+    if header not in seen:
+      seen.add(header)
+      unique_headers.append(header)
+
+  if not unique_headers:
+    print "Couldn't find a header for {0}.".format(symbol)
+    return
+
+  try:
+    selected = unique_headers[0]
+    inserted_header_infos = header_infos
+    if len(unique_headers) > 1:
+      selected = GetUserSelection(
+          "choose a header file for {0}.".format(symbol),
+          unique_headers, maximum_suggested_headers)
+      inserted_header_infos = [
+        header for header in header_infos if header["Header"] == selected]
+    include_fixer_context["HeaderInfos"] = inserted_header_infos
+
+    InsertHeaderToVimBuffer(include_fixer_context, text)
+    print "Added #include {0} for {1}.".format(selected, symbol)
+  except Exception as error:
+    print >> sys.stderr, error.message
+  return
+
+
+if __name__ == '__main__':
+  main()
diff --git a/modularize/CMakeLists.txt b/modularize/CMakeLists.txt
new file mode 100644 (file)
index 0000000..187db94
--- /dev/null
@@ -0,0 +1,25 @@
+set(LLVM_LINK_COMPONENTS
+  Option
+  Support
+  )
+
+add_clang_tool(modularize
+  Modularize.cpp
+  ModuleAssistant.cpp
+  ModularizeUtilities.cpp
+  CoverageChecker.cpp
+  PreprocessorTracker.cpp
+  )
+
+target_link_libraries(modularize
+  clangAST
+  clangBasic
+  clangDriver
+  clangFrontend
+  clangLex
+  clangTooling
+  )
+
+install(TARGETS modularize
+        RUNTIME DESTINATION bin
+        COMPONENT clang-extras)
diff --git a/modularize/CoverageChecker.cpp b/modularize/CoverageChecker.cpp
new file mode 100644 (file)
index 0000000..510d062
--- /dev/null
@@ -0,0 +1,420 @@
+//===--- extra/module-map-checker/CoverageChecker.cpp -------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements a class that validates a module map by checking that
+// all headers in the corresponding directories are accounted for.
+//
+// This class uses a previously loaded module map object.
+// Starting at the module map file directory, or just the include
+// paths, if specified, it will collect the names of all the files it
+// considers headers (no extension, .h, or .inc--if you need more, modify the
+// ModularizeUtilities::isHeader function).
+//  It then compares the headers against those referenced
+// in the module map, either explicitly named, or implicitly named via an
+// umbrella directory or umbrella file, as parsed by the ModuleMap object.
+// If headers are found which are not referenced or covered by an umbrella
+// directory or file, warning messages will be produced, and the doChecks
+// function will return an error code of 1.  Other errors result in an error
+// code of 2. If no problems are found, an error code of 0 is returned.
+//
+// Note that in the case of umbrella headers, this tool invokes the compiler
+// to preprocess the file, and uses a callback to collect the header files
+// included by the umbrella header or any of its nested includes.  If any
+// front end options are needed for these compiler invocations, these are
+// to be passed in via the CommandLine parameter.
+//
+// Warning message have the form:
+//
+//  warning: module.modulemap does not account for file: Level3A.h
+//
+// Note that for the case of the module map referencing a file that does
+// not exist, the module map parser in Clang will (at the time of this
+// writing) display an error message.
+//
+// Potential problems with this program:
+//
+// 1. Might need a better header matching mechanism, or extensions to the
+//    canonical file format used.
+//
+// 2. It might need to support additional header file extensions.
+//
+// Future directions:
+//
+// 1. Add an option to fix the problems found, writing a new module map.
+//    Include an extra option to add unaccounted-for headers as excluded.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ModularizeUtilities.h"
+#include "clang/AST/ASTConsumer.h"
+#include "CoverageChecker.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Driver/Options.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendActions.h"
+#include "clang/Lex/PPCallbacks.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Tooling/CompilationDatabase.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/Option/Option.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace Modularize;
+using namespace clang;
+using namespace clang::driver;
+using namespace clang::driver::options;
+using namespace clang::tooling;
+namespace cl = llvm::cl;
+namespace sys = llvm::sys;
+
+// Preprocessor callbacks.
+// We basically just collect include files.
+class CoverageCheckerCallbacks : public PPCallbacks {
+public:
+  CoverageCheckerCallbacks(CoverageChecker &Checker) : Checker(Checker) {}
+  ~CoverageCheckerCallbacks() override {}
+
+  // Include directive callback.
+  void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
+                          StringRef FileName, bool IsAngled,
+                          CharSourceRange FilenameRange, const FileEntry *File,
+                          StringRef SearchPath, StringRef RelativePath,
+                          const Module *Imported) override {
+    Checker.collectUmbrellaHeaderHeader(File->getName());
+  }
+
+private:
+  CoverageChecker &Checker;
+};
+
+// Frontend action stuff:
+
+// Consumer is responsible for setting up the callbacks.
+class CoverageCheckerConsumer : public ASTConsumer {
+public:
+  CoverageCheckerConsumer(CoverageChecker &Checker, Preprocessor &PP) {
+    // PP takes ownership.
+    PP.addPPCallbacks(llvm::make_unique<CoverageCheckerCallbacks>(Checker));
+  }
+};
+
+class CoverageCheckerAction : public SyntaxOnlyAction {
+public:
+  CoverageCheckerAction(CoverageChecker &Checker) : Checker(Checker) {}
+
+protected:
+  std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
+    StringRef InFile) override {
+    return llvm::make_unique<CoverageCheckerConsumer>(Checker,
+      CI.getPreprocessor());
+  }
+
+private:
+  CoverageChecker &Checker;
+};
+
+class CoverageCheckerFrontendActionFactory : public FrontendActionFactory {
+public:
+  CoverageCheckerFrontendActionFactory(CoverageChecker &Checker)
+    : Checker(Checker) {}
+
+  CoverageCheckerAction *create() override {
+    return new CoverageCheckerAction(Checker);
+  }
+
+private:
+  CoverageChecker &Checker;
+};
+
+// CoverageChecker class implementation.
+
+// Constructor.
+CoverageChecker::CoverageChecker(StringRef ModuleMapPath,
+    std::vector<std::string> &IncludePaths,
+    ArrayRef<std::string> CommandLine,
+    clang::ModuleMap *ModuleMap)
+  : ModuleMapPath(ModuleMapPath), IncludePaths(IncludePaths),
+    CommandLine(CommandLine),
+    ModMap(ModuleMap) {}
+
+// Create instance of CoverageChecker, to simplify setting up
+// subordinate objects.
+std::unique_ptr<CoverageChecker> CoverageChecker::createCoverageChecker(
+    StringRef ModuleMapPath, std::vector<std::string> &IncludePaths,
+    ArrayRef<std::string> CommandLine, clang::ModuleMap *ModuleMap) {
+
+  return llvm::make_unique<CoverageChecker>(ModuleMapPath, IncludePaths,
+                                            CommandLine, ModuleMap);
+}
+
+// Do checks.
+// Starting from the directory of the module.modulemap file,
+// Find all header files, optionally looking only at files
+// covered by the include path options, and compare against
+// the headers referenced by the module.modulemap file.
+// Display warnings for unaccounted-for header files.
+// Returns error_code of 0 if there were no errors or warnings, 1 if there
+//   were warnings, 2 if any other problem, such as if a bad
+//   module map path argument was specified.
+std::error_code CoverageChecker::doChecks() {
+  std::error_code returnValue;
+
+  // Collect the headers referenced in the modules.
+  collectModuleHeaders();
+
+  // Collect the file system headers.
+  if (!collectFileSystemHeaders())
+    return std::error_code(2, std::generic_category());
+
+  // Do the checks.  These save the problematic file names.
+  findUnaccountedForHeaders();
+
+  // Check for warnings.
+  if (!UnaccountedForHeaders.empty())
+    returnValue = std::error_code(1, std::generic_category());
+
+  return returnValue;
+}
+
+// The following functions are called by doChecks.
+
+// Collect module headers.
+// Walks the modules and collects referenced headers into
+// ModuleMapHeadersSet.
+void CoverageChecker::collectModuleHeaders() {
+  for (ModuleMap::module_iterator I = ModMap->module_begin(),
+    E = ModMap->module_end();
+    I != E; ++I) {
+    collectModuleHeaders(*I->second);
+  }
+}
+
+// Collect referenced headers from one module.
+// Collects the headers referenced in the given module into
+// ModuleMapHeadersSet.
+// FIXME: Doesn't collect files from umbrella header.
+bool CoverageChecker::collectModuleHeaders(const Module &Mod) {
+
+  if (const FileEntry *UmbrellaHeader = Mod.getUmbrellaHeader().Entry) {
+    // Collect umbrella header.
+    ModuleMapHeadersSet.insert(ModularizeUtilities::getCanonicalPath(
+      UmbrellaHeader->getName()));
+    // Preprocess umbrella header and collect the headers it references.
+    if (!collectUmbrellaHeaderHeaders(UmbrellaHeader->getName()))
+      return false;
+  }
+  else if (const DirectoryEntry *UmbrellaDir = Mod.getUmbrellaDir().Entry) {
+    // Collect headers in umbrella directory.
+    if (!collectUmbrellaHeaders(UmbrellaDir->getName()))
+      return false;
+  }
+
+  for (auto &HeaderKind : Mod.Headers)
+    for (auto &Header : HeaderKind)
+      ModuleMapHeadersSet.insert(ModularizeUtilities::getCanonicalPath(
+        Header.Entry->getName()));
+
+  for (auto MI = Mod.submodule_begin(), MIEnd = Mod.submodule_end();
+       MI != MIEnd; ++MI)
+    collectModuleHeaders(**MI);
+
+  return true;
+}
+
+// Collect headers from an umbrella directory.
+bool CoverageChecker::collectUmbrellaHeaders(StringRef UmbrellaDirName) {
+  // Initialize directory name.
+  SmallString<256> Directory(ModuleMapDirectory);
+  if (UmbrellaDirName.size())
+    sys::path::append(Directory, UmbrellaDirName);
+  if (Directory.size() == 0)
+    Directory = ".";
+  // Walk the directory.
+  std::error_code EC;
+  sys::fs::file_status Status;
+  for (sys::fs::directory_iterator I(Directory.str(), EC), E; I != E;
+    I.increment(EC)) {
+    if (EC)
+      return false;
+    std::string File(I->path());
+    I->status(Status);
+    sys::fs::file_type Type = Status.type();
+    // If the file is a directory, ignore the name and recurse.
+    if (Type == sys::fs::file_type::directory_file) {
+      if (!collectUmbrellaHeaders(File))
+        return false;
+      continue;
+    }
+    // If the file does not have a common header extension, ignore it.
+    if (!ModularizeUtilities::isHeader(File))
+      continue;
+    // Save header name.
+    ModuleMapHeadersSet.insert(ModularizeUtilities::getCanonicalPath(File));
+  }
+  return true;
+}
+
+// Collect headers rferenced from an umbrella file.
+bool
+CoverageChecker::collectUmbrellaHeaderHeaders(StringRef UmbrellaHeaderName) {
+
+  SmallString<256> PathBuf(ModuleMapDirectory);
+
+  // If directory is empty, it's the current directory.
+  if (ModuleMapDirectory.length() == 0)
+    sys::fs::current_path(PathBuf);
+
+  // Create the compilation database.
+  std::unique_ptr<CompilationDatabase> Compilations;
+  Compilations.reset(new FixedCompilationDatabase(Twine(PathBuf), CommandLine));
+
+  std::vector<std::string> HeaderPath;
+  HeaderPath.push_back(UmbrellaHeaderName);
+
+  // Create the tool and run the compilation.
+  ClangTool Tool(*Compilations, HeaderPath);
+  int HadErrors = Tool.run(new CoverageCheckerFrontendActionFactory(*this));
+
+  // If we had errors, exit early.
+  return !HadErrors;
+}
+
+// Called from CoverageCheckerCallbacks to track a header included
+// from an umbrella header.
+void CoverageChecker::collectUmbrellaHeaderHeader(StringRef HeaderName) {
+
+  SmallString<256> PathBuf(ModuleMapDirectory);
+  // If directory is empty, it's the current directory.
+  if (ModuleMapDirectory.length() == 0)
+    sys::fs::current_path(PathBuf);
+  // HeaderName will have an absolute path, so if it's the module map
+  // directory, we remove it, also skipping trailing separator.
+  if (HeaderName.startswith(PathBuf))
+    HeaderName = HeaderName.substr(PathBuf.size() + 1);
+  // Save header name.
+  ModuleMapHeadersSet.insert(ModularizeUtilities::getCanonicalPath(HeaderName));
+}
+
+// Collect file system header files.
+// This function scans the file system for header files,
+// starting at the directory of the module.modulemap file,
+// optionally filtering out all but the files covered by
+// the include path options.
+// Returns true if no errors.
+bool CoverageChecker::collectFileSystemHeaders() {
+
+  // Get directory containing the module.modulemap file.
+  // Might be relative to current directory, absolute, or empty.
+  ModuleMapDirectory = ModularizeUtilities::getDirectoryFromPath(ModuleMapPath);
+
+  // If no include paths specified, we do the whole tree starting
+  // at the module.modulemap directory.
+  if (IncludePaths.size() == 0) {
+    if (!collectFileSystemHeaders(StringRef("")))
+      return false;
+  }
+  else {
+    // Otherwise we only look at the sub-trees specified by the
+    // include paths.
+    for (std::vector<std::string>::const_iterator I = IncludePaths.begin(),
+      E = IncludePaths.end();
+      I != E; ++I) {
+      if (!collectFileSystemHeaders(*I))
+        return false;
+    }
+  }
+
+  // Sort it, because different file systems might order the file differently.
+  std::sort(FileSystemHeaders.begin(), FileSystemHeaders.end());
+
+  return true;
+}
+
+// Collect file system header files from the given path.
+// This function scans the file system for header files,
+// starting at the given directory, which is assumed to be
+// relative to the directory of the module.modulemap file.
+// \returns True if no errors.
+bool CoverageChecker::collectFileSystemHeaders(StringRef IncludePath) {
+
+  // Initialize directory name.
+  SmallString<256> Directory(ModuleMapDirectory);
+  if (IncludePath.size())
+    sys::path::append(Directory, IncludePath);
+  if (Directory.size() == 0)
+    Directory = ".";
+  if (IncludePath.startswith("/") || IncludePath.startswith("\\") ||
+    ((IncludePath.size() >= 2) && (IncludePath[1] == ':'))) {
+    llvm::errs() << "error: Include path \"" << IncludePath
+      << "\" is not relative to the module map file.\n";
+    return false;
+  }
+
+  // Recursively walk the directory tree.
+  std::error_code EC;
+  sys::fs::file_status Status;
+  int Count = 0;
+  for (sys::fs::recursive_directory_iterator I(Directory.str(), EC), E; I != E;
+    I.increment(EC)) {
+    if (EC)
+      return false;
+    //std::string file(I->path());
+    StringRef file(I->path());
+    I->status(Status);
+    sys::fs::file_type type = Status.type();
+    // If the file is a directory, ignore the name (but still recurses).
+    if (type == sys::fs::file_type::directory_file)
+      continue;
+    // Assume directories or files starting with '.' are private and not to
+    // be considered.
+    if ((file.find("\\.") != StringRef::npos) ||
+        (file.find("/.") != StringRef::npos))
+      continue;
+    // If the file does not have a common header extension, ignore it.
+    if (!ModularizeUtilities::isHeader(file))
+      continue;
+    // Save header name.
+    FileSystemHeaders.push_back(ModularizeUtilities::getCanonicalPath(file));
+    Count++;
+  }
+  if (Count == 0) {
+    llvm::errs() << "warning: No headers found in include path: \""
+      << IncludePath << "\"\n";
+  }
+  return true;
+}
+
+// Find headers unaccounted-for in module map.
+// This function compares the list of collected header files
+// against those referenced in the module map.  Display
+// warnings for unaccounted-for header files.
+// Save unaccounted-for file list for possible.
+// fixing action.
+// FIXME: There probably needs to be some canonalization
+// of file names so that header path can be correctly
+// matched.  Also, a map could be used for the headers
+// referenced in the module, but
+void CoverageChecker::findUnaccountedForHeaders() {
+  // Walk over file system headers.
+  for (std::vector<std::string>::const_iterator I = FileSystemHeaders.begin(),
+    E = FileSystemHeaders.end();
+    I != E; ++I) {
+    // Look for header in module map.
+    if (ModuleMapHeadersSet.insert(*I).second) {
+      UnaccountedForHeaders.push_back(*I);
+      llvm::errs() << "warning: " << ModuleMapPath
+        << " does not account for file: " << *I << "\n";
+    }
+  }
+}
diff --git a/modularize/CoverageChecker.h b/modularize/CoverageChecker.h
new file mode 100644 (file)
index 0000000..f6c8367
--- /dev/null
@@ -0,0 +1,164 @@
+//===-- CoverageChecker.h - Module map coverage checker -*- C++ -*-------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===--------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Definitions for CoverageChecker.
+///
+//===--------------------------------------------------------------------===//
+
+#ifndef COVERAGECHECKER_H
+#define COVERAGECHECKER_H
+
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/TargetInfo.h"
+#include "clang/Basic/TargetOptions.h"
+#include "clang/Frontend/TextDiagnosticPrinter.h"
+#include "clang/Lex/HeaderSearch.h"
+#include "clang/Lex/HeaderSearchOptions.h"
+#include "clang/Lex/ModuleMap.h"
+#include "clang/Lex/Preprocessor.h"
+#include "llvm/ADT/StringSet.h"
+#include "llvm/Support/Host.h"
+#include <string>
+#include <vector>
+
+namespace Modularize {
+
+/// Module map checker class.
+/// This is the heart of the checker.
+/// The doChecks function does the main work.
+/// The data members store the options and internally collected data.
+class CoverageChecker {
+  // Checker arguments.
+
+  /// The module.modulemap file path. Can be relative or absolute.
+  llvm::StringRef ModuleMapPath;
+  /// The include paths to check for files.
+  /// (Note that other directories above these paths are ignored.
+  /// To expect all files to be accounted for from the module.modulemap
+  /// file directory on down, leave this empty.)
+  std::vector<std::string> IncludePaths;
+  /// The remaining arguments, to be passed to the front end.
+  llvm::ArrayRef<std::string> CommandLine;
+  /// The module map.
+  clang::ModuleMap *ModMap;
+
+  // Internal data.
+
+  /// Directory containing the module map.
+  /// Might be relative to the current directory, or absolute.
+  std::string ModuleMapDirectory;
+  /// Set of all the headers found in the module map.
+  llvm::StringSet<llvm::MallocAllocator> ModuleMapHeadersSet;
+  /// All the headers found in the file system starting at the
+  /// module map, or the union of those from the include paths.
+  std::vector<std::string> FileSystemHeaders;
+  /// Headers found in file system, but not in module map.
+  std::vector<std::string> UnaccountedForHeaders;
+
+public:
+  /// Constructor.
+  /// You can use the static createCoverageChecker to create an instance
+  /// of this object.
+  /// \param ModuleMapPath The module.modulemap file path.
+  ///   Can be relative or absolute.
+  /// \param IncludePaths The include paths to check for files.
+  ///   (Note that other directories above these paths are ignored.
+  ///   To expect all files to be accounted for from the module.modulemap
+  ///   file directory on down, leave this empty.)
+  /// \param CommandLine Compile command line arguments.
+  /// \param ModuleMap The module map to check.
+  CoverageChecker(llvm::StringRef ModuleMapPath,
+    std::vector<std::string> &IncludePaths,
+    llvm::ArrayRef<std::string> CommandLine,
+    clang::ModuleMap *ModuleMap);
+
+  /// Create instance of CoverageChecker.
+  /// \param ModuleMapPath The module.modulemap file path.
+  ///   Can be relative or absolute.
+  /// \param IncludePaths The include paths to check for files.
+  ///   (Note that other directories above these paths are ignored.
+  ///   To expect all files to be accounted for from the module.modulemap
+  ///   file directory on down, leave this empty.)
+  /// \param CommandLine Compile command line arguments.
+  /// \param ModuleMap The module map to check.
+  /// \returns Initialized CoverageChecker object.
+  static std::unique_ptr<CoverageChecker> createCoverageChecker(
+      llvm::StringRef ModuleMapPath, std::vector<std::string> &IncludePaths,
+      llvm::ArrayRef<std::string> CommandLine, clang::ModuleMap *ModuleMap);
+
+  /// Do checks.
+  /// Starting from the directory of the module.modulemap file,
+  /// Find all header files, optionally looking only at files
+  /// covered by the include path options, and compare against
+  /// the headers referenced by the module.modulemap file.
+  /// Display warnings for unaccounted-for header files.
+  /// \returns 0 if there were no errors or warnings, 1 if there
+  ///   were warnings, 2 if any other problem, such as a bad
+  ///   module map path argument was specified.
+  std::error_code doChecks();
+
+  // The following functions are called by doChecks.
+
+  /// Collect module headers.
+  /// Walks the modules and collects referenced headers into
+  /// ModuleMapHeadersSet.
+  void collectModuleHeaders();
+
+  /// Collect referenced headers from one module.
+  /// Collects the headers referenced in the given module into
+  /// ModuleMapHeadersSet.
+  /// \param Mod The module reference.
+  /// \return True if no errors.
+  bool collectModuleHeaders(const clang::Module &Mod);
+
+  /// Collect headers from an umbrella directory.
+  /// \param UmbrellaDirName The umbrella directory name.
+  /// \return True if no errors.
+  bool collectUmbrellaHeaders(llvm::StringRef UmbrellaDirName);
+
+  /// Collect headers rferenced from an umbrella file.
+  /// \param UmbrellaHeaderName The umbrella file path.
+  /// \return True if no errors.
+  bool collectUmbrellaHeaderHeaders(llvm::StringRef UmbrellaHeaderName);
+
+  /// Called from CoverageCheckerCallbacks to track a header included
+  /// from an umbrella header.
+  /// \param HeaderName The header file path.
+  void collectUmbrellaHeaderHeader(llvm::StringRef HeaderName);
+
+  /// Collect file system header files.
+  /// This function scans the file system for header files,
+  /// starting at the directory of the module.modulemap file,
+  /// optionally filtering out all but the files covered by
+  /// the include path options.
+  /// \returns True if no errors.
+  bool collectFileSystemHeaders();
+
+  /// Collect file system header files from the given path.
+  /// This function scans the file system for header files,
+  /// starting at the given directory, which is assumed to be
+  /// relative to the directory of the module.modulemap file.
+  /// \returns True if no errors.
+  bool collectFileSystemHeaders(llvm::StringRef IncludePath);
+
+  /// Find headers unaccounted-for in module map.
+  /// This function compares the list of collected header files
+  /// against those referenced in the module map.  Display
+  /// warnings for unaccounted-for header files.
+  /// Save unaccounted-for file list for possible.
+  /// fixing action.
+  void findUnaccountedForHeaders();
+};
+
+} // end namespace Modularize
+
+#endif // COVERAGECHECKER_H
diff --git a/modularize/Modularize.cpp b/modularize/Modularize.cpp
new file mode 100644 (file)
index 0000000..83f2340
--- /dev/null
@@ -0,0 +1,998 @@
+//===- extra/modularize/Modularize.cpp - Check modularized headers --------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Introduction
+//
+// This file implements a tool that checks whether a set of headers provides
+// the consistent definitions required to use modules.  It can also check an
+// existing module map for full coverage of the headers in a directory tree.
+//
+// For example, in examining headers, it detects whether the same entity
+// (say, a NULL macro or size_t typedef) is defined in multiple headers
+// or whether a header produces different definitions under
+// different circumstances. These conditions cause modules built from the
+// headers to behave poorly, and should be fixed before introducing a module
+// map.
+//
+// Modularize takes as input either one or more module maps (by default,
+// "module.modulemap") or one or more text files contatining lists of headers
+// to check.
+//
+// In the case of a module map, the module map must be well-formed in
+// terms of syntax.  Modularize will extract the header file names
+// from the map.  Only normal headers are checked, assuming headers
+// marked "private", "textual", or "exclude" are not to be checked
+// as a top-level include, assuming they either are included by
+// other headers which are checked, or they are not suitable for
+// modules.
+//
+// In the case of a file list, the list is a newline-separated list of headers
+// to check with respect to each other.
+// Lines beginning with '#' and empty lines are ignored.
+// Header file names followed by a colon and other space-separated
+// file names will include those extra files as dependencies.
+// The file names can be relative or full paths, but must be on the
+// same line.
+//
+// Modularize also accepts regular clang front-end arguments.
+//
+// Usage:   modularize [(modularize options)]
+//   [(include-files_list)|(module map)]+ [(front-end-options) ...]
+//
+// Options:
+//    -prefix=(optional header path prefix)
+//          Note that unless a "-prefix (header path)" option is specified,
+//          non-absolute file paths in the header list file will be relative
+//          to the header list file directory.  Use -prefix to specify a
+//          different directory.
+//    -module-map-path=(module map)
+//          Skip the checks, and instead act as a module.map generation
+//          assistant, generating a module map file based on the header list.
+//          An optional "-root-module=(rootName)" argument can specify a root
+//          module to be created in the generated module.map file.  Note that
+//          you will likely need to edit this file to suit the needs of your
+//          headers.
+//    -problem-files-list=(problem files list file name)
+//          For use only with module map assistant.  Input list of files that
+//          have problems with respect to modules.  These will still be
+//          included in the generated module map, but will be marked as
+//          "excluded" headers.
+//    -root-module=(root module name)
+//          Specifies a root module to be created in the generated module.map
+//          file.
+//    -block-check-header-list-only
+//          Only warn if #include directives are inside extern or namespace
+//          blocks if the included header is in the header list.
+//    -no-coverage-check
+//          Don't do the coverage check.
+//    -coverage-check-only
+//          Only do the coverage check.
+//    -display-file-lists
+//          Display lists of good files (no compile errors), problem files,
+//          and a combined list with problem files preceded by a '#'.
+//          This can be used to quickly determine which files have problems.
+//          The latter combined list might be useful in starting to modularize
+//          a set of headers.  You can start with a full list of headers,
+//          use -display-file-lists option, and then use the combined list as
+//          your intermediate list, uncommenting-out headers as you fix them.
+//
+// Note that by default, the modularize assumes .h files contain C++ source.
+// If your .h files in the file list contain another language, you should
+// append an appropriate -x option to your command line, i.e.:  -x c
+//
+// Modularization Issue Checks
+//
+// In the process of checking headers for modularization issues, modularize
+// will do normal parsing, reporting normal errors and warnings,
+// but will also report special error messages like the following:
+//
+//   error: '(symbol)' defined at multiple locations:
+//       (file):(row):(column)
+//       (file):(row):(column)
+//
+//   error: header '(file)' has different contents depending on how it was
+//     included
+//
+// The latter might be followed by messages like the following:
+//
+//   note: '(symbol)' in (file) at (row):(column) not always provided
+//
+// Checks will also be performed for macro expansions, defined(macro)
+// expressions, and preprocessor conditional directives that evaluate
+// inconsistently, and can produce error messages like the following:
+//
+//   (...)/SubHeader.h:11:5:
+//   #if SYMBOL == 1
+//       ^
+//   error: Macro instance 'SYMBOL' has different values in this header,
+//          depending on how it was included.
+//     'SYMBOL' expanded to: '1' with respect to these inclusion paths:
+//       (...)/Header1.h
+//         (...)/SubHeader.h
+//   (...)/SubHeader.h:3:9:
+//   #define SYMBOL 1
+//             ^
+//   Macro defined here.
+//     'SYMBOL' expanded to: '2' with respect to these inclusion paths:
+//       (...)/Header2.h
+//           (...)/SubHeader.h
+//   (...)/SubHeader.h:7:9:
+//   #define SYMBOL 2
+//             ^
+//   Macro defined here.
+//
+// Checks will also be performed for '#include' directives that are
+// nested inside 'extern "C/C++" {}' or 'namespace (name) {}' blocks,
+// and can produce error message like the following:
+//
+// IncludeInExtern.h:2:3
+//   #include "Empty.h"
+//   ^
+// error: Include directive within extern "C" {}.
+// IncludeInExtern.h:1:1
+// extern "C" {
+// ^
+// The "extern "C" {}" block is here.
+//
+// See PreprocessorTracker.cpp for additional details.
+//
+// Module Map Coverage Check
+//
+// The coverage check uses the Clang ModuleMap class to read and parse the
+// module map file.  Starting at the module map file directory, or just the
+// include paths, if specified, it will collect the names of all the files it
+// considers headers (no extension, .h, or .inc--if you need more, modify the
+// isHeader function).  It then compares the headers against those referenced
+// in the module map, either explicitly named, or implicitly named via an
+// umbrella directory or umbrella file, as parsed by the ModuleMap object.
+// If headers are found which are not referenced or covered by an umbrella
+// directory or file, warning messages will be produced, and this program
+// will return an error code of 1.  Other errors result in an error code of 2.
+// If no problems are found, an error code of 0 is returned.
+//
+// Note that in the case of umbrella headers, this tool invokes the compiler
+// to preprocess the file, and uses a callback to collect the header files
+// included by the umbrella header or any of its nested includes.  If any
+// front end options are needed for these compiler invocations, these
+// can be included on the command line after the module map file argument.
+//
+// Warning message have the form:
+//
+//  warning: module.modulemap does not account for file: Level3A.h
+//
+// Note that for the case of the module map referencing a file that does
+// not exist, the module map parser in Clang will (at the time of this
+// writing) display an error message.
+//
+// Module Map Assistant - Module Map Generation
+//
+// Modularize also has an option ("-module-map-path=module.modulemap") that will
+// skip the checks, and instead act as a module.modulemap generation assistant,
+// generating a module map file based on the header list.  An optional
+// "-root-module=(rootName)" argument can specify a root module to be
+// created in the generated module.modulemap file.  Note that you will likely
+// need to edit this file to suit the needs of your headers.
+//
+// An example command line for generating a module.modulemap file:
+//
+//   modularize -module-map-path=module.modulemap -root-module=myroot \
+//      headerlist.txt
+//
+// Note that if the headers in the header list have partial paths, sub-modules
+// will be created for the subdirectires involved, assuming that the
+// subdirectories contain headers to be grouped into a module, but still with
+// individual modules for the headers in the subdirectory.
+//
+// See the ModuleAssistant.cpp file comments for additional details about the
+// implementation of the assistant mode.
+//
+// Future directions:
+//
+// Basically, we want to add new checks for whatever we can check with respect
+// to checking headers for module'ability.
+//
+// Some ideas:
+//
+// 1. Omit duplicate "not always provided" messages
+//
+// 2. Add options to disable any of the checks, in case
+// there is some problem with them, or the messages get too verbose.
+//
+// 3. Try to figure out the preprocessor conditional directives that
+// contribute to problems and tie them to the inconsistent definitions.
+//
+// 4. There are some legitimate uses of preprocessor macros that
+// modularize will flag as errors, such as repeatedly #include'ing
+// a file and using interleaving defined/undefined macros
+// to change declarations in the included file.  Is there a way
+// to address this?  Maybe have modularize accept a list of macros
+// to ignore.  Otherwise you can just exclude the file, after checking
+// for legitimate errors.
+//
+// 5. What else?
+//
+// General clean-up and refactoring:
+//
+// 1. The Location class seems to be something that we might
+// want to design to be applicable to a wider range of tools, and stick it
+// somewhere into Tooling/ in mainline
+//
+//===----------------------------------------------------------------------===//
+
+#include "Modularize.h"
+#include "ModularizeUtilities.h"
+#include "PreprocessorTracker.h"
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Driver/Options.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendActions.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Tooling/CompilationDatabase.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/Option/Arg.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Option/OptTable.h"
+#include "llvm/Option/Option.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include <algorithm>
+#include <fstream>
+#include <iterator>
+#include <string>
+#include <vector>
+
+using namespace clang;
+using namespace clang::driver;
+using namespace clang::driver::options;
+using namespace clang::tooling;
+using namespace llvm;
+using namespace llvm::opt;
+using namespace Modularize;
+
+// Option to specify a file name for a list of header files to check.
+static cl::list<std::string>
+    ListFileNames(cl::Positional, cl::value_desc("list"),
+                  cl::desc("<list of one or more header list files>"),
+                  cl::CommaSeparated);
+
+// Collect all other arguments, which will be passed to the front end.
+static cl::list<std::string>
+    CC1Arguments(cl::ConsumeAfter,
+                 cl::desc("<arguments to be passed to front end>..."));
+
+// Option to specify a prefix to be prepended to the header names.
+static cl::opt<std::string> HeaderPrefix(
+    "prefix", cl::init(""),
+    cl::desc(
+        "Prepend header file paths with this prefix."
+        " If not specified,"
+        " the files are considered to be relative to the header list file."));
+
+// Option for assistant mode, telling modularize to output a module map
+// based on the headers list, and where to put it.
+static cl::opt<std::string> ModuleMapPath(
+    "module-map-path", cl::init(""),
+    cl::desc("Turn on module map output and specify output path or file name."
+             " If no path is specified and if prefix option is specified,"
+             " use prefix for file path."));
+
+// Option to specify list of problem files for assistant.
+// This will cause assistant to exclude these files.
+static cl::opt<std::string> ProblemFilesList(
+  "problem-files-list", cl::init(""),
+  cl::desc(
+  "List of files with compilation or modularization problems for"
+    " assistant mode.  This will be excluded."));
+
+// Option for assistant mode, telling modularize the name of the root module.
+static cl::opt<std::string>
+RootModule("root-module", cl::init(""),
+           cl::desc("Specify the name of the root module."));
+
+// Option for limiting the #include-inside-extern-or-namespace-block
+// check to only those headers explicitly listed in the header list.
+// This is a work-around for private includes that purposefully get
+// included inside blocks.
+static cl::opt<bool>
+BlockCheckHeaderListOnly("block-check-header-list-only", cl::init(false),
+cl::desc("Only warn if #include directives are inside extern or namespace"
+  " blocks if the included header is in the header list."));
+
+// Option for include paths for coverage check.
+static cl::list<std::string>
+IncludePaths("I", cl::desc("Include path for coverage check."),
+cl::ZeroOrMore, cl::value_desc("path"));
+
+// Option for disabling the coverage check.
+static cl::opt<bool>
+NoCoverageCheck("no-coverage-check", cl::init(false),
+cl::desc("Don't do the coverage check."));
+
+// Option for just doing the coverage check.
+static cl::opt<bool>
+CoverageCheckOnly("coverage-check-only", cl::init(false),
+cl::desc("Only do the coverage check."));
+
+// Option for displaying lists of good, bad, and mixed files.
+static cl::opt<bool>
+DisplayFileLists("display-file-lists", cl::init(false),
+cl::desc("Display lists of good files (no compile errors), problem files,"
+  " and a combined list with problem files preceded by a '#'."));
+
+// Save the program name for error messages.
+const char *Argv0;
+// Save the command line for comments.
+std::string CommandLine;
+
+// Helper function for finding the input file in an arguments list.
+static std::string findInputFile(const CommandLineArguments &CLArgs) {
+  std::unique_ptr<OptTable> Opts(createDriverOptTable());
+  const unsigned IncludedFlagsBitmask = options::CC1Option;
+  unsigned MissingArgIndex, MissingArgCount;
+  SmallVector<const char *, 256> Argv;
+  for (auto I = CLArgs.begin(), E = CLArgs.end(); I != E; ++I)
+    Argv.push_back(I->c_str());
+  InputArgList Args = Opts->ParseArgs(Argv, MissingArgIndex, MissingArgCount,
+                                      IncludedFlagsBitmask);
+  std::vector<std::string> Inputs = Args.getAllArgValues(OPT_INPUT);
+  return ModularizeUtilities::getCanonicalPath(Inputs.back());
+}
+
+// This arguments adjuster inserts "-include (file)" arguments for header
+// dependencies.  It also inserts a "-w" option and a "-x c++",
+// if no other "-x" option is present.
+static ArgumentsAdjuster
+getModularizeArgumentsAdjuster(DependencyMap &Dependencies) {
+  return [&Dependencies](const CommandLineArguments &Args,
+                         StringRef /*unused*/) {
+    std::string InputFile = findInputFile(Args);
+    DependentsVector &FileDependents = Dependencies[InputFile];
+    CommandLineArguments NewArgs(Args);
+    if (int Count = FileDependents.size()) {
+      for (int Index = 0; Index < Count; ++Index) {
+        NewArgs.push_back("-include");
+        std::string File(std::string("\"") + FileDependents[Index] +
+                         std::string("\""));
+        NewArgs.push_back(FileDependents[Index]);
+      }
+    }
+    // Ignore warnings.  (Insert after "clang_tool" at beginning.)
+    NewArgs.insert(NewArgs.begin() + 1, "-w");
+    // Since we are compiling .h files, assume C++ unless given a -x option.
+    if (std::find(NewArgs.begin(), NewArgs.end(), "-x") == NewArgs.end()) {
+      NewArgs.insert(NewArgs.begin() + 2, "-x");
+      NewArgs.insert(NewArgs.begin() + 3, "c++");
+    }
+    return NewArgs;
+  };
+}
+
+// FIXME: The Location class seems to be something that we might
+// want to design to be applicable to a wider range of tools, and stick it
+// somewhere into Tooling/ in mainline
+struct Location {
+  const FileEntry *File;
+  unsigned Line, Column;
+
+  Location() : File(), Line(), Column() {}
+
+  Location(SourceManager &SM, SourceLocation Loc) : File(), Line(), Column() {
+    Loc = SM.getExpansionLoc(Loc);
+    if (Loc.isInvalid())
+      return;
+
+    std::pair<FileID, unsigned> Decomposed = SM.getDecomposedLoc(Loc);
+    File = SM.getFileEntryForID(Decomposed.first);
+    if (!File)
+      return;
+
+    Line = SM.getLineNumber(Decomposed.first, Decomposed.second);
+    Column = SM.getColumnNumber(Decomposed.first, Decomposed.second);
+  }
+
+  operator bool() const { return File != nullptr; }
+
+  friend bool operator==(const Location &X, const Location &Y) {
+    return X.File == Y.File && X.Line == Y.Line && X.Column == Y.Column;
+  }
+
+  friend bool operator!=(const Location &X, const Location &Y) {
+    return !(X == Y);
+  }
+
+  friend bool operator<(const Location &X, const Location &Y) {
+    if (X.File != Y.File)
+      return X.File < Y.File;
+    if (X.Line != Y.Line)
+      return X.Line < Y.Line;
+    return X.Column < Y.Column;
+  }
+  friend bool operator>(const Location &X, const Location &Y) { return Y < X; }
+  friend bool operator<=(const Location &X, const Location &Y) {
+    return !(Y < X);
+  }
+  friend bool operator>=(const Location &X, const Location &Y) {
+    return !(X < Y);
+  }
+};
+
+struct Entry {
+  enum EntryKind {
+    EK_Tag,
+    EK_Value,
+    EK_Macro,
+
+    EK_NumberOfKinds
+  } Kind;
+
+  Location Loc;
+
+  StringRef getKindName() { return getKindName(Kind); }
+  static StringRef getKindName(EntryKind kind);
+};
+
+// Return a string representing the given kind.
+StringRef Entry::getKindName(Entry::EntryKind kind) {
+  switch (kind) {
+  case EK_Tag:
+    return "tag";
+  case EK_Value:
+    return "value";
+  case EK_Macro:
+    return "macro";
+  case EK_NumberOfKinds:
+    break;
+  }
+  llvm_unreachable("invalid Entry kind");
+}
+
+struct HeaderEntry {
+  std::string Name;
+  Location Loc;
+
+  friend bool operator==(const HeaderEntry &X, const HeaderEntry &Y) {
+    return X.Loc == Y.Loc && X.Name == Y.Name;
+  }
+  friend bool operator!=(const HeaderEntry &X, const HeaderEntry &Y) {
+    return !(X == Y);
+  }
+  friend bool operator<(const HeaderEntry &X, const HeaderEntry &Y) {
+    return X.Loc < Y.Loc || (X.Loc == Y.Loc && X.Name < Y.Name);
+  }
+  friend bool operator>(const HeaderEntry &X, const HeaderEntry &Y) {
+    return Y < X;
+  }
+  friend bool operator<=(const HeaderEntry &X, const HeaderEntry &Y) {
+    return !(Y < X);
+  }
+  friend bool operator>=(const HeaderEntry &X, const HeaderEntry &Y) {
+    return !(X < Y);
+  }
+};
+
+typedef std::vector<HeaderEntry> HeaderContents;
+
+class EntityMap : public StringMap<SmallVector<Entry, 2> > {
+public:
+  DenseMap<const FileEntry *, HeaderContents> HeaderContentMismatches;
+
+  void add(const std::string &Name, enum Entry::EntryKind Kind, Location Loc) {
+    // Record this entity in its header.
+    HeaderEntry HE = { Name, Loc };
+    CurHeaderContents[Loc.File].push_back(HE);
+
+    // Check whether we've seen this entry before.
+    SmallVector<Entry, 2> &Entries = (*this)[Name];
+    for (unsigned I = 0, N = Entries.size(); I != N; ++I) {
+      if (Entries[I].Kind == Kind && Entries[I].Loc == Loc)
+        return;
+    }
+
+    // We have not seen this entry before; record it.
+    Entry E = { Kind, Loc };
+    Entries.push_back(E);
+  }
+
+  void mergeCurHeaderContents() {
+    for (DenseMap<const FileEntry *, HeaderContents>::iterator
+             H = CurHeaderContents.begin(),
+             HEnd = CurHeaderContents.end();
+         H != HEnd; ++H) {
+      // Sort contents.
+      std::sort(H->second.begin(), H->second.end());
+
+      // Check whether we've seen this header before.
+      DenseMap<const FileEntry *, HeaderContents>::iterator KnownH =
+          AllHeaderContents.find(H->first);
+      if (KnownH == AllHeaderContents.end()) {
+        // We haven't seen this header before; record its contents.
+        AllHeaderContents.insert(*H);
+        continue;
+      }
+
+      // If the header contents are the same, we're done.
+      if (H->second == KnownH->second)
+        continue;
+
+      // Determine what changed.
+      std::set_symmetric_difference(
+          H->second.begin(), H->second.end(), KnownH->second.begin(),
+          KnownH->second.end(),
+          std::back_inserter(HeaderContentMismatches[H->first]));
+    }
+
+    CurHeaderContents.clear();
+  }
+
+private:
+  DenseMap<const FileEntry *, HeaderContents> CurHeaderContents;
+  DenseMap<const FileEntry *, HeaderContents> AllHeaderContents;
+};
+
+class CollectEntitiesVisitor
+    : public RecursiveASTVisitor<CollectEntitiesVisitor> {
+public:
+  CollectEntitiesVisitor(SourceManager &SM, EntityMap &Entities,
+                         Preprocessor &PP, PreprocessorTracker &PPTracker,
+                         int &HadErrors)
+      : SM(SM), Entities(Entities), PP(PP), PPTracker(PPTracker),
+        HadErrors(HadErrors) {}
+
+  bool TraverseStmt(Stmt *S) { return true; }
+  bool TraverseType(QualType T) { return true; }
+  bool TraverseTypeLoc(TypeLoc TL) { return true; }
+  bool TraverseNestedNameSpecifier(NestedNameSpecifier *NNS) { return true; }
+  bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS) {
+    return true;
+  }
+  bool TraverseDeclarationNameInfo(DeclarationNameInfo NameInfo) {
+    return true;
+  }
+  bool TraverseTemplateName(TemplateName Template) { return true; }
+  bool TraverseTemplateArgument(const TemplateArgument &Arg) { return true; }
+  bool TraverseTemplateArgumentLoc(const TemplateArgumentLoc &ArgLoc) {
+    return true;
+  }
+  bool TraverseTemplateArguments(const TemplateArgument *Args,
+                                 unsigned NumArgs) {
+    return true;
+  }
+  bool TraverseConstructorInitializer(CXXCtorInitializer *Init) { return true; }
+  bool TraverseLambdaCapture(LambdaExpr *LE, const LambdaCapture *C,
+                             Expr *Init) {
+    return true;
+  }
+
+  // Check 'extern "*" {}' block for #include directives.
+  bool VisitLinkageSpecDecl(LinkageSpecDecl *D) {
+    // Bail if not a block.
+    if (!D->hasBraces())
+      return true;
+    SourceRange BlockRange = D->getSourceRange();
+    const char *LinkageLabel;
+    switch (D->getLanguage()) {
+    case LinkageSpecDecl::lang_c:
+      LinkageLabel = "extern \"C\" {}";
+      break;
+    case LinkageSpecDecl::lang_cxx:
+      LinkageLabel = "extern \"C++\" {}";
+      break;
+    }
+    if (!PPTracker.checkForIncludesInBlock(PP, BlockRange, LinkageLabel,
+                                           errs()))
+      HadErrors = 1;
+    return true;
+  }
+
+  // Check 'namespace (name) {}' block for #include directives.
+  bool VisitNamespaceDecl(const NamespaceDecl *D) {
+    SourceRange BlockRange = D->getSourceRange();
+    std::string Label("namespace ");
+    Label += D->getName();
+    Label += " {}";
+    if (!PPTracker.checkForIncludesInBlock(PP, BlockRange, Label.c_str(),
+                                           errs()))
+      HadErrors = 1;
+    return true;
+  }
+
+  // Collect definition entities.
+  bool VisitNamedDecl(NamedDecl *ND) {
+    // We only care about file-context variables.
+    if (!ND->getDeclContext()->isFileContext())
+      return true;
+
+    // Skip declarations that tend to be properly multiply-declared.
+    if (isa<NamespaceDecl>(ND) || isa<UsingDirectiveDecl>(ND) ||
+        isa<NamespaceAliasDecl>(ND) ||
+        isa<ClassTemplateSpecializationDecl>(ND) || isa<UsingDecl>(ND) ||
+        isa<ClassTemplateDecl>(ND) || isa<TemplateTypeParmDecl>(ND) ||
+        isa<TypeAliasTemplateDecl>(ND) || isa<UsingShadowDecl>(ND) ||
+        isa<FunctionDecl>(ND) || isa<FunctionTemplateDecl>(ND) ||
+        (isa<TagDecl>(ND) &&
+         !cast<TagDecl>(ND)->isThisDeclarationADefinition()))
+      return true;
+
+    // Skip anonymous declarations.
+    if (!ND->getDeclName())
+      return true;
+
+    // Get the qualified name.
+    std::string Name;
+    llvm::raw_string_ostream OS(Name);
+    ND->printQualifiedName(OS);
+    OS.flush();
+    if (Name.empty())
+      return true;
+
+    Location Loc(SM, ND->getLocation());
+    if (!Loc)
+      return true;
+
+    Entities.add(Name, isa<TagDecl>(ND) ? Entry::EK_Tag : Entry::EK_Value, Loc);
+    return true;
+  }
+
+private:
+  SourceManager &SM;
+  EntityMap &Entities;
+  Preprocessor &PP;
+  PreprocessorTracker &PPTracker;
+  int &HadErrors;
+};
+
+class CollectEntitiesConsumer : public ASTConsumer {
+public:
+  CollectEntitiesConsumer(EntityMap &Entities,
+                          PreprocessorTracker &preprocessorTracker,
+                          Preprocessor &PP, StringRef InFile, int &HadErrors)
+      : Entities(Entities), PPTracker(preprocessorTracker), PP(PP),
+        HadErrors(HadErrors) {
+    PPTracker.handlePreprocessorEntry(PP, InFile);
+  }
+
+  ~CollectEntitiesConsumer() override { PPTracker.handlePreprocessorExit(); }
+
+  void HandleTranslationUnit(ASTContext &Ctx) override {
+    SourceManager &SM = Ctx.getSourceManager();
+
+    // Collect declared entities.
+    CollectEntitiesVisitor(SM, Entities, PP, PPTracker, HadErrors)
+        .TraverseDecl(Ctx.getTranslationUnitDecl());
+
+    // Collect macro definitions.
+    for (Preprocessor::macro_iterator M = PP.macro_begin(),
+                                      MEnd = PP.macro_end();
+         M != MEnd; ++M) {
+      Location Loc(SM, M->second.getLatest()->getLocation());
+      if (!Loc)
+        continue;
+
+      Entities.add(M->first->getName().str(), Entry::EK_Macro, Loc);
+    }
+
+    // Merge header contents.
+    Entities.mergeCurHeaderContents();
+  }
+
+private:
+  EntityMap &Entities;
+  PreprocessorTracker &PPTracker;
+  Preprocessor &PP;
+  int &HadErrors;
+};
+
+class CollectEntitiesAction : public SyntaxOnlyAction {
+public:
+  CollectEntitiesAction(EntityMap &Entities,
+                        PreprocessorTracker &preprocessorTracker,
+                        int &HadErrors)
+      : Entities(Entities), PPTracker(preprocessorTracker),
+        HadErrors(HadErrors) {}
+
+protected:
+  std::unique_ptr<clang::ASTConsumer>
+  CreateASTConsumer(CompilerInstance &CI, StringRef InFile) override {
+    return llvm::make_unique<CollectEntitiesConsumer>(
+        Entities, PPTracker, CI.getPreprocessor(), InFile, HadErrors);
+  }
+
+private:
+  EntityMap &Entities;
+  PreprocessorTracker &PPTracker;
+  int &HadErrors;
+};
+
+class ModularizeFrontendActionFactory : public FrontendActionFactory {
+public:
+  ModularizeFrontendActionFactory(EntityMap &Entities,
+                                  PreprocessorTracker &preprocessorTracker,
+                                  int &HadErrors)
+      : Entities(Entities), PPTracker(preprocessorTracker),
+        HadErrors(HadErrors) {}
+
+  CollectEntitiesAction *create() override {
+    return new CollectEntitiesAction(Entities, PPTracker, HadErrors);
+  }
+
+private:
+  EntityMap &Entities;
+  PreprocessorTracker &PPTracker;
+  int &HadErrors;
+};
+
+class CompileCheckVisitor
+  : public RecursiveASTVisitor<CompileCheckVisitor> {
+public:
+  CompileCheckVisitor() {}
+
+  bool TraverseStmt(Stmt *S) { return true; }
+  bool TraverseType(QualType T) { return true; }
+  bool TraverseTypeLoc(TypeLoc TL) { return true; }
+  bool TraverseNestedNameSpecifier(NestedNameSpecifier *NNS) { return true; }
+  bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS) {
+    return true;
+  }
+  bool TraverseDeclarationNameInfo(DeclarationNameInfo NameInfo) {
+    return true;
+  }
+  bool TraverseTemplateName(TemplateName Template) { return true; }
+  bool TraverseTemplateArgument(const TemplateArgument &Arg) { return true; }
+  bool TraverseTemplateArgumentLoc(const TemplateArgumentLoc &ArgLoc) {
+    return true;
+  }
+  bool TraverseTemplateArguments(const TemplateArgument *Args,
+    unsigned NumArgs) {
+    return true;
+  }
+  bool TraverseConstructorInitializer(CXXCtorInitializer *Init) { return true; }
+  bool TraverseLambdaCapture(LambdaExpr *LE, const LambdaCapture *C,
+                             Expr *Init) {
+    return true;
+  }
+
+  // Check 'extern "*" {}' block for #include directives.
+  bool VisitLinkageSpecDecl(LinkageSpecDecl *D) {
+    return true;
+  }
+
+  // Check 'namespace (name) {}' block for #include directives.
+  bool VisitNamespaceDecl(const NamespaceDecl *D) {
+    return true;
+  }
+
+  // Collect definition entities.
+  bool VisitNamedDecl(NamedDecl *ND) {
+    return true;
+  }
+};
+
+class CompileCheckConsumer : public ASTConsumer {
+public:
+  CompileCheckConsumer() {}
+
+  void HandleTranslationUnit(ASTContext &Ctx) override {
+    CompileCheckVisitor().TraverseDecl(Ctx.getTranslationUnitDecl());
+  }
+};
+
+class CompileCheckAction : public SyntaxOnlyAction {
+public:
+  CompileCheckAction() {}
+
+protected:
+  std::unique_ptr<clang::ASTConsumer>
+    CreateASTConsumer(CompilerInstance &CI, StringRef InFile) override {
+    return llvm::make_unique<CompileCheckConsumer>();
+  }
+};
+
+class CompileCheckFrontendActionFactory : public FrontendActionFactory {
+public:
+  CompileCheckFrontendActionFactory() {}
+
+  CompileCheckAction *create() override {
+    return new CompileCheckAction();
+  }
+};
+
+int main(int Argc, const char **Argv) {
+
+  // Save program name for error messages.
+  Argv0 = Argv[0];
+
+  // Save program arguments for use in module.modulemap comment.
+  CommandLine = sys::path::stem(sys::path::filename(Argv0));
+  for (int ArgIndex = 1; ArgIndex < Argc; ArgIndex++) {
+    CommandLine.append(" ");
+    CommandLine.append(Argv[ArgIndex]);
+  }
+
+  // This causes options to be parsed.
+  cl::ParseCommandLineOptions(Argc, Argv, "modularize.\n");
+
+  // No go if we have no header list file.
+  if (ListFileNames.size() == 0) {
+    cl::PrintHelpMessage();
+    return 1;
+  }
+
+  std::unique_ptr<ModularizeUtilities> ModUtil;
+  int HadErrors = 0;
+
+  ModUtil.reset(
+    ModularizeUtilities::createModularizeUtilities(
+      ListFileNames, HeaderPrefix, ProblemFilesList));
+
+  // Get header file names and dependencies.
+  if (ModUtil->loadAllHeaderListsAndDependencies())
+    HadErrors = 1;
+
+  // If we are in assistant mode, output the module map and quit.
+  if (ModuleMapPath.length() != 0) {
+    if (!createModuleMap(ModuleMapPath, ModUtil->HeaderFileNames,
+                         ModUtil->ProblemFileNames,
+                         ModUtil->Dependencies, HeaderPrefix, RootModule))
+      return 1; // Failed.
+    return 0;   // Success - Skip checks in assistant mode.
+  }
+
+  // If we're doing module maps.
+  if (!NoCoverageCheck && ModUtil->HasModuleMap) {
+    // Do coverage check.
+    if (ModUtil->doCoverageCheck(IncludePaths, CommandLine))
+      HadErrors = 1;
+  }
+
+  // Bail early if only doing the coverage check.
+  if (CoverageCheckOnly)
+    return HadErrors;
+
+  // Create the compilation database.
+  SmallString<256> PathBuf;
+  sys::fs::current_path(PathBuf);
+  std::unique_ptr<CompilationDatabase> Compilations;
+  Compilations.reset(
+      new FixedCompilationDatabase(Twine(PathBuf), CC1Arguments));
+
+  // Create preprocessor tracker, to watch for macro and conditional problems.
+  std::unique_ptr<PreprocessorTracker> PPTracker(
+    PreprocessorTracker::create(ModUtil->HeaderFileNames,
+                                BlockCheckHeaderListOnly));
+
+  // Coolect entities here.
+  EntityMap Entities;
+
+  // Because we can't easily determine which files failed
+  // during the tool run, if we're collecting the file lists
+  // for display, we do a first compile pass on individual
+  // files to find which ones don't compile stand-alone.
+  if (DisplayFileLists) {
+    // First, make a pass to just get compile errors.
+    for (auto &CompileCheckFile : ModUtil->HeaderFileNames) {
+      llvm::SmallVector<std::string, 32> CompileCheckFileArray;
+      CompileCheckFileArray.push_back(CompileCheckFile);
+      ClangTool CompileCheckTool(*Compilations, CompileCheckFileArray);
+      CompileCheckTool.appendArgumentsAdjuster(
+        getModularizeArgumentsAdjuster(ModUtil->Dependencies));
+      int CompileCheckFileErrors = 0;
+      CompileCheckFrontendActionFactory CompileCheckFactory;
+      CompileCheckFileErrors |= CompileCheckTool.run(&CompileCheckFactory);
+      if (CompileCheckFileErrors != 0) {
+        ModUtil->addUniqueProblemFile(CompileCheckFile);   // Save problem file.
+        HadErrors |= 1;
+      }
+      else
+        ModUtil->addNoCompileErrorsFile(CompileCheckFile); // Save good file.
+    }
+  }
+
+  // Then we make another pass on the good files to do the rest of the work.
+  ClangTool Tool(*Compilations,
+    (DisplayFileLists ? ModUtil->GoodFileNames : ModUtil->HeaderFileNames));
+  Tool.appendArgumentsAdjuster(
+    getModularizeArgumentsAdjuster(ModUtil->Dependencies));
+  ModularizeFrontendActionFactory Factory(Entities, *PPTracker, HadErrors);
+  HadErrors |= Tool.run(&Factory);
+
+  // Create a place to save duplicate entity locations, separate bins per kind.
+  typedef SmallVector<Location, 8> LocationArray;
+  typedef SmallVector<LocationArray, Entry::EK_NumberOfKinds> EntryBinArray;
+  EntryBinArray EntryBins;
+  int KindIndex;
+  for (KindIndex = 0; KindIndex < Entry::EK_NumberOfKinds; ++KindIndex) {
+    LocationArray Array;
+    EntryBins.push_back(Array);
+  }
+
+  // Check for the same entity being defined in multiple places.
+  for (EntityMap::iterator E = Entities.begin(), EEnd = Entities.end();
+       E != EEnd; ++E) {
+    // If only one occurrence, exit early.
+    if (E->second.size() == 1)
+      continue;
+    // Clear entity locations.
+    for (EntryBinArray::iterator CI = EntryBins.begin(), CE = EntryBins.end();
+         CI != CE; ++CI) {
+      CI->clear();
+    }
+    // Walk the entities of a single name, collecting the locations,
+    // separated into separate bins.
+    for (unsigned I = 0, N = E->second.size(); I != N; ++I) {
+      EntryBins[E->second[I].Kind].push_back(E->second[I].Loc);
+    }
+    // Report any duplicate entity definition errors.
+    int KindIndex = 0;
+    for (EntryBinArray::iterator DI = EntryBins.begin(), DE = EntryBins.end();
+         DI != DE; ++DI, ++KindIndex) {
+      int ECount = DI->size();
+      // If only 1 occurrence of this entity, skip it, we only report duplicates.
+      if (ECount <= 1)
+        continue;
+      LocationArray::iterator FI = DI->begin();
+      StringRef kindName = Entry::getKindName((Entry::EntryKind)KindIndex);
+      errs() << "error: " << kindName << " '" << E->first()
+             << "' defined at multiple locations:\n";
+      for (LocationArray::iterator FE = DI->end(); FI != FE; ++FI) {
+        errs() << "    " << FI->File->getName() << ":" << FI->Line << ":"
+               << FI->Column << "\n";
+        ModUtil->addUniqueProblemFile(FI->File->getName());
+      }
+      HadErrors = 1;
+    }
+  }
+
+  // Complain about macro instance in header files that differ based on how
+  // they are included.
+  if (PPTracker->reportInconsistentMacros(errs()))
+    HadErrors = 1;
+
+  // Complain about preprocessor conditional directives in header files that
+  // differ based on how they are included.
+  if (PPTracker->reportInconsistentConditionals(errs()))
+    HadErrors = 1;
+
+  // Complain about any headers that have contents that differ based on how
+  // they are included.
+  // FIXME: Could we provide information about which preprocessor conditionals
+  // are involved?
+  for (DenseMap<const FileEntry *, HeaderContents>::iterator
+           H = Entities.HeaderContentMismatches.begin(),
+           HEnd = Entities.HeaderContentMismatches.end();
+       H != HEnd; ++H) {
+    if (H->second.empty()) {
+      errs() << "internal error: phantom header content mismatch\n";
+      continue;
+    }
+
+    HadErrors = 1;
+    ModUtil->addUniqueProblemFile(H->first->getName());
+    errs() << "error: header '" << H->first->getName()
+           << "' has different contents depending on how it was included.\n";
+    for (unsigned I = 0, N = H->second.size(); I != N; ++I) {
+      errs() << "note: '" << H->second[I].Name << "' in "
+             << H->second[I].Loc.File->getName() << " at "
+             << H->second[I].Loc.Line << ":" << H->second[I].Loc.Column
+             << " not always provided\n";
+    }
+  }
+
+  if (DisplayFileLists) {
+    ModUtil->displayProblemFiles();
+    ModUtil->displayGoodFiles();
+    ModUtil->displayCombinedFiles();
+  }
+
+  return HadErrors;
+}
diff --git a/modularize/Modularize.h b/modularize/Modularize.h
new file mode 100644 (file)
index 0000000..a3f2ad3
--- /dev/null
@@ -0,0 +1,54 @@
+//===--- Modularize.h - Common definitions for Modularize -*- C++ -*-----===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===--------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Common definitions for Modularize.
+///
+//===--------------------------------------------------------------------===//
+
+#ifndef MODULARIZE_H
+#define MODULARIZE_H
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/StringRef.h"
+#include <string>
+#include <vector>
+
+// Save the program name for error messages.
+extern const char *Argv0;
+// Save the command line for comments.
+extern std::string CommandLine;
+
+// Dependency types.
+typedef llvm::SmallVector<std::string, 4> DependentsVector;
+typedef llvm::StringMap<DependentsVector> DependencyMap;
+
+// Global function declarations.
+
+/// Create the module map file.
+/// \param ModuleMapPath The path to the module map file to be generated.
+/// \param HeaderFileNames The list of header files, absolute native paths.
+/// \param ProblemFileNames The list of problem header files.
+/// \param Dependencies Map of headers that depend on other headers.
+/// \param HeaderPrefix Tells the code where the headers are, if they
+///   aren's in the current directory, allowing the generator to strip
+///   the leading, non-relative beginning of the header paths.
+/// \brief RootModuleName If not empty, specifies that a root module
+///   should be created with this name.
+/// \returns True if successful.
+bool createModuleMap(llvm::StringRef ModuleMapPath,
+                     llvm::ArrayRef<std::string> HeaderFileNames,
+                     llvm::ArrayRef<std::string> ProblemFileNames,
+                     DependencyMap &Dependencies, llvm::StringRef HeaderPrefix,
+                     llvm::StringRef RootModuleName);
+
+#endif // MODULARIZE_H
diff --git a/modularize/ModularizeUtilities.cpp b/modularize/ModularizeUtilities.cpp
new file mode 100644 (file)
index 0000000..874742d
--- /dev/null
@@ -0,0 +1,545 @@
+//===--- extra/modularize/ModularizeUtilities.cpp -------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements a class for loading and validating a module map or
+// header list by checking that all headers in the corresponding directories
+// are accounted for.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Basic/SourceManager.h"
+#include "clang/Driver/Options.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendActions.h"
+#include "CoverageChecker.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/FileUtilities.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/raw_ostream.h"
+#include "ModularizeUtilities.h"
+
+using namespace clang;
+using namespace llvm;
+using namespace Modularize;
+
+namespace {
+// Subclass TargetOptions so we can construct it inline with
+// the minimal option, the triple.
+class ModuleMapTargetOptions : public clang::TargetOptions {
+public:
+  ModuleMapTargetOptions() { Triple = llvm::sys::getDefaultTargetTriple(); }
+};
+} // namespace
+
+// ModularizeUtilities class implementation.
+
+// Constructor.
+ModularizeUtilities::ModularizeUtilities(std::vector<std::string> &InputPaths,
+                                         llvm::StringRef Prefix,
+                                         llvm::StringRef ProblemFilesListPath)
+    : InputFilePaths(InputPaths), HeaderPrefix(Prefix),
+      ProblemFilesPath(ProblemFilesListPath), HasModuleMap(false),
+      MissingHeaderCount(0),
+      // Init clang stuff needed for loading the module map and preprocessing.
+      LangOpts(new LangOptions()), DiagIDs(new DiagnosticIDs()),
+      DiagnosticOpts(new DiagnosticOptions()),
+      DC(llvm::errs(), DiagnosticOpts.get()),
+      Diagnostics(
+          new DiagnosticsEngine(DiagIDs, DiagnosticOpts.get(), &DC, false)),
+      TargetOpts(new ModuleMapTargetOptions()),
+      Target(TargetInfo::CreateTargetInfo(*Diagnostics, TargetOpts)),
+      FileMgr(new FileManager(FileSystemOpts)),
+      SourceMgr(new SourceManager(*Diagnostics, *FileMgr, false)),
+      HeaderInfo(new HeaderSearch(std::make_shared<HeaderSearchOptions>(),
+                                  *SourceMgr, *Diagnostics, *LangOpts,
+                                  Target.get())) {}
+
+// Create instance of ModularizeUtilities, to simplify setting up
+// subordinate objects.
+ModularizeUtilities *ModularizeUtilities::createModularizeUtilities(
+    std::vector<std::string> &InputPaths, llvm::StringRef Prefix,
+    llvm::StringRef ProblemFilesListPath) {
+
+  return new ModularizeUtilities(InputPaths, Prefix, ProblemFilesListPath);
+}
+
+// Load all header lists and dependencies.
+std::error_code ModularizeUtilities::loadAllHeaderListsAndDependencies() {
+  // For each input file.
+  for (auto I = InputFilePaths.begin(), E = InputFilePaths.end(); I != E; ++I) {
+    llvm::StringRef InputPath = *I;
+    // If it's a module map.
+    if (InputPath.endswith(".modulemap")) {
+      // Load the module map.
+      if (std::error_code EC = loadModuleMap(InputPath))
+        return EC;
+    }
+    else {
+      // Else we assume it's a header list and load it.
+      if (std::error_code EC = loadSingleHeaderListsAndDependencies(InputPath)) {
+        errs() << "modularize: error: Unable to get header list '" << InputPath
+          << "': " << EC.message() << '\n';
+        return EC;
+      }
+    }
+  }
+  // If we have a problem files list.
+  if (ProblemFilesPath.size() != 0) {
+    // Load problem files list.
+    if (std::error_code EC = loadProblemHeaderList(ProblemFilesPath)) {
+      errs() << "modularize: error: Unable to get problem header list '" << ProblemFilesPath
+        << "': " << EC.message() << '\n';
+      return EC;
+    }
+  }
+  return std::error_code();
+}
+
+// Do coverage checks.
+// For each loaded module map, do header coverage check.
+// Starting from the directory of the module.map file,
+// Find all header files, optionally looking only at files
+// covered by the include path options, and compare against
+// the headers referenced by the module.map file.
+// Display warnings for unaccounted-for header files.
+// Returns 0 if there were no errors or warnings, 1 if there
+// were warnings, 2 if any other problem, such as a bad
+// module map path argument was specified.
+std::error_code ModularizeUtilities::doCoverageCheck(
+    std::vector<std::string> &IncludePaths,
+    llvm::ArrayRef<std::string> CommandLine) {
+  int ModuleMapCount = ModuleMaps.size();
+  int ModuleMapIndex;
+  std::error_code EC;
+  for (ModuleMapIndex = 0; ModuleMapIndex < ModuleMapCount; ++ModuleMapIndex) {
+    std::unique_ptr<clang::ModuleMap> &ModMap = ModuleMaps[ModuleMapIndex];
+    auto Checker = CoverageChecker::createCoverageChecker(
+        InputFilePaths[ModuleMapIndex], IncludePaths, CommandLine,
+        ModMap.get());
+    std::error_code LocalEC = Checker->doChecks();
+    if (LocalEC.value() > 0)
+      EC = LocalEC;
+  }
+  return EC;
+}
+
+// Load single header list and dependencies.
+std::error_code ModularizeUtilities::loadSingleHeaderListsAndDependencies(
+    llvm::StringRef InputPath) {
+
+  // By default, use the path component of the list file name.
+  SmallString<256> HeaderDirectory(InputPath);
+  llvm::sys::path::remove_filename(HeaderDirectory);
+  SmallString<256> CurrentDirectory;
+  llvm::sys::fs::current_path(CurrentDirectory);
+
+  // Get the prefix if we have one.
+  if (HeaderPrefix.size() != 0)
+    HeaderDirectory = HeaderPrefix;
+
+  // Read the header list file into a buffer.
+  ErrorOr<std::unique_ptr<MemoryBuffer>> listBuffer =
+    MemoryBuffer::getFile(InputPath);
+  if (std::error_code EC = listBuffer.getError())
+    return EC;
+
+  // Parse the header list into strings.
+  SmallVector<StringRef, 32> Strings;
+  listBuffer.get()->getBuffer().split(Strings, "\n", -1, false);
+
+  // Collect the header file names from the string list.
+  for (SmallVectorImpl<StringRef>::iterator I = Strings.begin(),
+    E = Strings.end();
+    I != E; ++I) {
+    StringRef Line = I->trim();
+    // Ignore comments and empty lines.
+    if (Line.empty() || (Line[0] == '#'))
+      continue;
+    std::pair<StringRef, StringRef> TargetAndDependents = Line.split(':');
+    SmallString<256> HeaderFileName;
+    // Prepend header file name prefix if it's not absolute.
+    if (llvm::sys::path::is_absolute(TargetAndDependents.first))
+      llvm::sys::path::native(TargetAndDependents.first, HeaderFileName);
+    else {
+      if (HeaderDirectory.size() != 0)
+        HeaderFileName = HeaderDirectory;
+      else
+        HeaderFileName = CurrentDirectory;
+      llvm::sys::path::append(HeaderFileName, TargetAndDependents.first);
+      llvm::sys::path::native(HeaderFileName);
+    }
+    // Handle optional dependencies.
+    DependentsVector Dependents;
+    SmallVector<StringRef, 4> DependentsList;
+    TargetAndDependents.second.split(DependentsList, " ", -1, false);
+    int Count = DependentsList.size();
+    for (int Index = 0; Index < Count; ++Index) {
+      SmallString<256> Dependent;
+      if (llvm::sys::path::is_absolute(DependentsList[Index]))
+        Dependent = DependentsList[Index];
+      else {
+        if (HeaderDirectory.size() != 0)
+          Dependent = HeaderDirectory;
+        else
+          Dependent = CurrentDirectory;
+        llvm::sys::path::append(Dependent, DependentsList[Index]);
+      }
+      llvm::sys::path::native(Dependent);
+      Dependents.push_back(getCanonicalPath(Dependent.str()));
+    }
+    // Get canonical form.
+    HeaderFileName = getCanonicalPath(HeaderFileName);
+    // Save the resulting header file path and dependencies.
+    HeaderFileNames.push_back(HeaderFileName.str());
+    Dependencies[HeaderFileName.str()] = Dependents;
+  }
+  return std::error_code();
+}
+
+// Load problem header list.
+std::error_code ModularizeUtilities::loadProblemHeaderList(
+  llvm::StringRef InputPath) {
+
+  // By default, use the path component of the list file name.
+  SmallString<256> HeaderDirectory(InputPath);
+  llvm::sys::path::remove_filename(HeaderDirectory);
+  SmallString<256> CurrentDirectory;
+  llvm::sys::fs::current_path(CurrentDirectory);
+
+  // Get the prefix if we have one.
+  if (HeaderPrefix.size() != 0)
+    HeaderDirectory = HeaderPrefix;
+
+  // Read the header list file into a buffer.
+  ErrorOr<std::unique_ptr<MemoryBuffer>> listBuffer =
+    MemoryBuffer::getFile(InputPath);
+  if (std::error_code EC = listBuffer.getError())
+    return EC;
+
+  // Parse the header list into strings.
+  SmallVector<StringRef, 32> Strings;
+  listBuffer.get()->getBuffer().split(Strings, "\n", -1, false);
+
+  // Collect the header file names from the string list.
+  for (SmallVectorImpl<StringRef>::iterator I = Strings.begin(),
+    E = Strings.end();
+    I != E; ++I) {
+    StringRef Line = I->trim();
+    // Ignore comments and empty lines.
+    if (Line.empty() || (Line[0] == '#'))
+      continue;
+    SmallString<256> HeaderFileName;
+    // Prepend header file name prefix if it's not absolute.
+    if (llvm::sys::path::is_absolute(Line))
+      llvm::sys::path::native(Line, HeaderFileName);
+    else {
+      if (HeaderDirectory.size() != 0)
+        HeaderFileName = HeaderDirectory;
+      else
+        HeaderFileName = CurrentDirectory;
+      llvm::sys::path::append(HeaderFileName, Line);
+      llvm::sys::path::native(HeaderFileName);
+    }
+    // Get canonical form.
+    HeaderFileName = getCanonicalPath(HeaderFileName);
+    // Save the resulting header file path.
+    ProblemFileNames.push_back(HeaderFileName.str());
+  }
+  return std::error_code();
+}
+
+// Load single module map and extract header file list.
+std::error_code ModularizeUtilities::loadModuleMap(
+    llvm::StringRef InputPath) {
+  // Get file entry for module.modulemap file.
+  const FileEntry *ModuleMapEntry =
+    SourceMgr->getFileManager().getFile(InputPath);
+
+  // return error if not found.
+  if (!ModuleMapEntry) {
+    llvm::errs() << "error: File \"" << InputPath << "\" not found.\n";
+    return std::error_code(1, std::generic_category());
+  }
+
+  // Because the module map parser uses a ForwardingDiagnosticConsumer,
+  // which doesn't forward the BeginSourceFile call, we do it explicitly here.
+  DC.BeginSourceFile(*LangOpts, nullptr);
+
+  // Figure out the home directory for the module map file.
+  const DirectoryEntry *Dir = ModuleMapEntry->getDir();
+  StringRef DirName(Dir->getName());
+  if (llvm::sys::path::filename(DirName) == "Modules") {
+    DirName = llvm::sys::path::parent_path(DirName);
+    if (DirName.endswith(".framework"))
+      Dir = FileMgr->getDirectory(DirName);
+    // FIXME: This assert can fail if there's a race between the above check
+    // and the removal of the directory.
+    assert(Dir && "parent must exist");
+  }
+
+  std::unique_ptr<ModuleMap> ModMap;
+  ModMap.reset(new ModuleMap(*SourceMgr, *Diagnostics, *LangOpts,
+    Target.get(), *HeaderInfo));
+
+  // Parse module.modulemap file into module map.
+  if (ModMap->parseModuleMapFile(ModuleMapEntry, false, Dir)) {
+    return std::error_code(1, std::generic_category());
+  }
+
+  // Do matching end call.
+  DC.EndSourceFile();
+
+  // Reset missing header count.
+  MissingHeaderCount = 0;
+
+  if (!collectModuleMapHeaders(ModMap.get()))
+    return std::error_code(1, std::generic_category());
+
+  // Save module map.
+  ModuleMaps.push_back(std::move(ModMap));
+
+  // Indicate we are using module maps.
+  HasModuleMap = true;
+
+  // Return code of 1 for missing headers.
+  if (MissingHeaderCount)
+    return std::error_code(1, std::generic_category());
+
+  return std::error_code();
+}
+
+// Collect module map headers.
+// Walks the modules and collects referenced headers into
+// HeaderFileNames.
+bool ModularizeUtilities::collectModuleMapHeaders(clang::ModuleMap *ModMap) {
+  for (ModuleMap::module_iterator I = ModMap->module_begin(),
+    E = ModMap->module_end();
+    I != E; ++I) {
+    if (!collectModuleHeaders(*I->second))
+      return false;
+  }
+  return true;
+}
+
+// Collect referenced headers from one module.
+// Collects the headers referenced in the given module into
+// HeaderFileNames.
+bool ModularizeUtilities::collectModuleHeaders(const clang::Module &Mod) {
+
+  // Ignore explicit modules because they often have dependencies
+  // we can't know.
+  if (Mod.IsExplicit)
+    return true;
+
+  // Treat headers in umbrella directory as dependencies.
+  DependentsVector UmbrellaDependents;
+
+  // Recursively do submodules.
+  for (auto MI = Mod.submodule_begin(), MIEnd = Mod.submodule_end();
+       MI != MIEnd; ++MI)
+    collectModuleHeaders(**MI);
+
+  if (const FileEntry *UmbrellaHeader = Mod.getUmbrellaHeader().Entry) {
+    std::string HeaderPath = getCanonicalPath(UmbrellaHeader->getName());
+    // Collect umbrella header.
+    HeaderFileNames.push_back(HeaderPath);
+
+    // FUTURE: When needed, umbrella header header collection goes here.
+  }
+  else if (const DirectoryEntry *UmbrellaDir = Mod.getUmbrellaDir().Entry) {
+    // If there normal headers, assume these are umbrellas and skip collection.
+    if (Mod.Headers->size() == 0) {
+      // Collect headers in umbrella directory.
+      if (!collectUmbrellaHeaders(UmbrellaDir->getName(), UmbrellaDependents))
+        return false;
+    }
+  }
+
+  // We ignore HK_Private, HK_Textual, HK_PrivateTextual, and HK_Excluded,
+  // assuming they are marked as such either because of unsuitability for
+  // modules or because they are meant to be included by another header,
+  // and thus should be ignored by modularize.
+
+  int NormalHeaderCount = Mod.Headers[clang::Module::HK_Normal].size();
+
+  for (int Index = 0; Index < NormalHeaderCount; ++Index) {
+    DependentsVector NormalDependents;
+    // Collect normal header.
+    const clang::Module::Header &Header(
+      Mod.Headers[clang::Module::HK_Normal][Index]);
+    std::string HeaderPath = getCanonicalPath(Header.Entry->getName());
+    HeaderFileNames.push_back(HeaderPath);
+  }
+
+  int MissingCountThisModule = Mod.MissingHeaders.size();
+
+  for (int Index = 0; Index < MissingCountThisModule; ++Index) {
+    std::string MissingFile = Mod.MissingHeaders[Index].FileName;
+    SourceLocation Loc = Mod.MissingHeaders[Index].FileNameLoc;
+    errs() << Loc.printToString(*SourceMgr)
+      << ": error : Header not found: " << MissingFile << "\n";
+  }
+
+  MissingHeaderCount += MissingCountThisModule;
+
+  return true;
+}
+
+// Collect headers from an umbrella directory.
+bool ModularizeUtilities::collectUmbrellaHeaders(StringRef UmbrellaDirName,
+  DependentsVector &Dependents) {
+  // Initialize directory name.
+  SmallString<256> Directory(UmbrellaDirName);
+  // Walk the directory.
+  std::error_code EC;
+  llvm::sys::fs::file_status Status;
+  for (llvm::sys::fs::directory_iterator I(Directory.str(), EC), E; I != E;
+    I.increment(EC)) {
+    if (EC)
+      return false;
+    std::string File(I->path());
+    I->status(Status);
+    llvm::sys::fs::file_type Type = Status.type();
+    // If the file is a directory, ignore the name and recurse.
+    if (Type == llvm::sys::fs::file_type::directory_file) {
+      if (!collectUmbrellaHeaders(File, Dependents))
+        return false;
+      continue;
+    }
+    // If the file does not have a common header extension, ignore it.
+    if (!isHeader(File))
+      continue;
+    // Save header name.
+    std::string HeaderPath = getCanonicalPath(File);
+    Dependents.push_back(HeaderPath);
+  }
+  return true;
+}
+
+// Replace .. embedded in path for purposes of having
+// a canonical path.
+static std::string replaceDotDot(StringRef Path) {
+  SmallString<128> Buffer;
+  llvm::sys::path::const_iterator B = llvm::sys::path::begin(Path),
+    E = llvm::sys::path::end(Path);
+  while (B != E) {
+    if (B->compare(".") == 0) {
+    }
+    else if (B->compare("..") == 0)
+      llvm::sys::path::remove_filename(Buffer);
+    else
+      llvm::sys::path::append(Buffer, *B);
+    ++B;
+  }
+  if (Path.endswith("/") || Path.endswith("\\"))
+    Buffer.append(1, Path.back());
+  return Buffer.c_str();
+}
+
+// Convert header path to canonical form.
+// The canonical form is basically just use forward slashes, and remove "./".
+// \param FilePath The file path, relative to the module map directory.
+// \returns The file path in canonical form.
+std::string ModularizeUtilities::getCanonicalPath(StringRef FilePath) {
+  std::string Tmp(replaceDotDot(FilePath));
+  std::replace(Tmp.begin(), Tmp.end(), '\\', '/');
+  StringRef Tmp2(Tmp);
+  if (Tmp2.startswith("./"))
+    Tmp = Tmp2.substr(2);
+  return Tmp;
+}
+
+// Check for header file extension.
+// If the file extension is .h, .inc, or missing, it's
+// assumed to be a header.
+// \param FileName The file name.  Must not be a directory.
+// \returns true if it has a header extension or no extension.
+bool ModularizeUtilities::isHeader(StringRef FileName) {
+  StringRef Extension = llvm::sys::path::extension(FileName);
+  if (Extension.size() == 0)
+    return true;
+  if (Extension.equals_lower(".h"))
+    return true;
+  if (Extension.equals_lower(".inc"))
+    return true;
+  return false;
+}
+
+// Get directory path component from file path.
+// \returns the component of the given path, which will be
+// relative if the given path is relative, absolute if the
+// given path is absolute, or "." if the path has no leading
+// path component.
+std::string ModularizeUtilities::getDirectoryFromPath(StringRef Path) {
+  SmallString<256> Directory(Path);
+  sys::path::remove_filename(Directory);
+  if (Directory.size() == 0)
+    return ".";
+  return Directory.str();
+}
+
+// Add unique problem file.
+// Also standardizes the path.
+void ModularizeUtilities::addUniqueProblemFile(std::string FilePath) {
+  FilePath = getCanonicalPath(FilePath);
+  // Don't add if already present.
+  for(auto &TestFilePath : ProblemFileNames) {
+    if (TestFilePath == FilePath)
+      return;
+  }
+  ProblemFileNames.push_back(FilePath);
+}
+
+// Add file with no compile errors.
+// Also standardizes the path.
+void ModularizeUtilities::addNoCompileErrorsFile(std::string FilePath) {
+  FilePath = getCanonicalPath(FilePath);
+  GoodFileNames.push_back(FilePath);
+}
+
+// List problem files.
+void ModularizeUtilities::displayProblemFiles() {
+  errs() << "\nThese are the files with possible errors:\n\n";
+  for (auto &ProblemFile : ProblemFileNames) {
+    errs() << ProblemFile << "\n";
+  }
+}
+
+// List files with no problems.
+void ModularizeUtilities::displayGoodFiles() {
+  errs() << "\nThese are the files with no detected errors:\n\n";
+  for (auto &GoodFile : HeaderFileNames) {
+    bool Good = true;
+    for (auto &ProblemFile : ProblemFileNames) {
+      if (ProblemFile == GoodFile) {
+        Good = false;
+        break;
+      }
+    }
+    if (Good)
+      errs() << GoodFile << "\n";
+  }
+}
+
+// List files with problem files commented out.
+void ModularizeUtilities::displayCombinedFiles() {
+  errs() <<
+    "\nThese are the combined files, with problem files preceded by #:\n\n";
+  for (auto &File : HeaderFileNames) {
+    bool Good = true;
+    for (auto &ProblemFile : ProblemFileNames) {
+      if (ProblemFile == File) {
+        Good = false;
+        break;
+      }
+    }
+    errs() << (Good ? "" : "#") << File << "\n";
+  }
+}
diff --git a/modularize/ModularizeUtilities.h b/modularize/ModularizeUtilities.h
new file mode 100644 (file)
index 0000000..4ad2b56
--- /dev/null
@@ -0,0 +1,225 @@
+//=====-- ModularizeUtilities.h - Utilities for modularize -*- C++ -*-======//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===--------------------------------------------------------------------===//
+///
+/// \file
+/// \brief ModularizeUtilities class definition.
+///
+//===--------------------------------------------------------------------===//
+
+#ifndef MODULARIZEUTILITIES_H
+#define MODULARIZEUTILITIES_H
+
+#include "Modularize.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/TargetInfo.h"
+#include "clang/Basic/TargetOptions.h"
+#include "clang/Frontend/TextDiagnosticPrinter.h"
+#include "clang/Lex/HeaderSearch.h"
+#include "clang/Lex/HeaderSearchOptions.h"
+#include "clang/Lex/ModuleMap.h"
+#include "clang/Lex/Preprocessor.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringSet.h"
+#include <string>
+#include <vector>
+
+namespace Modularize {
+
+/// Modularize utilities class.
+/// Support functions and data for modularize.
+class ModularizeUtilities {
+public:
+  // Input arguments.
+
+  /// The input file paths.
+  std::vector<std::string> InputFilePaths;
+  /// The header prefix.
+  llvm::StringRef HeaderPrefix;
+  /// The path of problem files list file.
+  llvm::StringRef ProblemFilesPath;
+
+  // Output data.
+
+  /// List of top-level header files.
+  llvm::SmallVector<std::string, 32> HeaderFileNames;
+  /// Map of top-level header file dependencies.
+  DependencyMap Dependencies;
+  /// True if we have module maps.
+  bool HasModuleMap;
+  /// Missing header count.
+  int MissingHeaderCount;
+  /// List of header files with no problems during the first pass,
+  /// that is, no compile errors.
+  llvm::SmallVector<std::string, 32> GoodFileNames;
+  /// List of header files with problems.
+  llvm::SmallVector<std::string, 32> ProblemFileNames;
+
+  // Functions.
+
+  /// Constructor.
+  /// You can use the static createModularizeUtilities to create an instance
+  /// of this object.
+  /// \param InputPaths The input file paths.
+  /// \param Prefix The headear path prefix.
+  /// \param ProblemFilesListPath The problem header list path.
+  ModularizeUtilities(std::vector<std::string> &InputPaths,
+                      llvm::StringRef Prefix,
+                      llvm::StringRef ProblemFilesListPath);
+
+  /// Create instance of ModularizeUtilities.
+  /// \param InputPaths The input file paths.
+  /// \param Prefix The headear path prefix.
+  /// \param ProblemFilesListPath The problem header list path.
+  /// \returns Initialized ModularizeUtilities object.
+  static ModularizeUtilities *createModularizeUtilities(
+      std::vector<std::string> &InputPaths,
+      llvm::StringRef Prefix,
+      llvm::StringRef ProblemFilesListPath);
+
+  /// Load header list and dependencies.
+  /// \returns std::error_code.
+  std::error_code loadAllHeaderListsAndDependencies();
+
+  /// Do coverage checks.
+  /// For each loaded module map, do header coverage check.
+  /// Starting from the directory of the module.map file,
+  /// Find all header files, optionally looking only at files
+  /// covered by the include path options, and compare against
+  /// the headers referenced by the module.map file.
+  /// Display warnings for unaccounted-for header files.
+  /// \param IncludePaths The include paths to check for files.
+  ///   (Note that other directories above these paths are ignored.
+  ///   To expect all files to be accounted for from the module.modulemap
+  ///   file directory on down, leave this empty.)
+  /// \param CommandLine Compile command line arguments.
+  /// \returns 0 if there were no errors or warnings, 1 if there
+  ///   were warnings, 2 if any other problem, such as a bad
+  ///   module map path argument was specified.
+  std::error_code doCoverageCheck(std::vector<std::string> &IncludePaths,
+                                  llvm::ArrayRef<std::string> CommandLine);
+
+  /// Add unique problem file.
+  /// Also standardizes the path.
+  /// \param FilePath Problem file path.
+  void addUniqueProblemFile(std::string FilePath);
+
+  /// Add file with no compile errors.
+  /// Also standardizes the path.
+  /// \param FilePath Problem file path.
+  void addNoCompileErrorsFile(std::string FilePath);
+
+  /// List problem files.
+  void displayProblemFiles();
+
+  /// List files with no problems.
+  void displayGoodFiles();
+
+  /// List files with problem files commented out.
+  void displayCombinedFiles();
+
+  // Internal.
+
+protected:
+
+  /// Load single header list and dependencies.
+  /// \param InputPath The input file path.
+  /// \returns std::error_code.
+  std::error_code loadSingleHeaderListsAndDependencies(
+      llvm::StringRef InputPath);
+
+  /// Load problem header list.
+  /// \param InputPath The input file path.
+  /// \returns std::error_code.
+  std::error_code loadProblemHeaderList(
+    llvm::StringRef InputPath);
+
+  /// Load single module map and extract header file list.
+  /// \param InputPath The input file path.
+  /// \returns std::error_code.
+  std::error_code loadModuleMap(
+    llvm::StringRef InputPath);
+
+  /// Collect module Map headers.
+  /// Walks the modules and collects referenced headers into
+  /// HeaderFileNames.
+  /// \param ModMap A loaded module map object.
+  /// \return True if no errors.
+  bool collectModuleMapHeaders(clang::ModuleMap *ModMap);
+
+  /// Collect referenced headers from one module.
+  /// Collects the headers referenced in the given module into
+  /// HeaderFileNames.
+  /// \param Mod The module reference.
+  /// \return True if no errors.
+  bool collectModuleHeaders(const clang::Module &Mod);
+
+  /// Collect headers from an umbrella directory.
+  /// \param UmbrellaDirName The umbrella directory name.
+  /// \return True if no errors.
+  bool collectUmbrellaHeaders(llvm::StringRef UmbrellaDirName,
+    DependentsVector &Dependents);
+
+public:
+
+  // Utility functions.
+
+  /// Convert header path to canonical form.
+  /// The canonical form is basically just use forward slashes,
+  /// and remove "./".
+  /// \param FilePath The file path.
+  /// \returns The file path in canonical form.
+  static std::string getCanonicalPath(llvm::StringRef FilePath);
+
+  /// Check for header file extension.
+  /// If the file extension is .h, .inc, or missing, it's
+  /// assumed to be a header.
+  /// \param FileName The file name.  Must not be a directory.
+  /// \returns true if it has a header extension or no extension.
+  static bool isHeader(llvm::StringRef FileName);
+
+  /// Get directory path component from file path.
+  /// \returns the component of the given path, which will be
+  /// relative if the given path is relative, absolute if the
+  /// given path is absolute, or "." if the path has no leading
+  /// path component.
+  static std::string getDirectoryFromPath(llvm::StringRef Path);
+
+  // Internal data.
+
+  /// Options controlling the language variant.
+  std::shared_ptr<clang::LangOptions> LangOpts;
+  /// Diagnostic IDs.
+  const llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs> DiagIDs;
+  /// Options controlling the diagnostic engine.
+  llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> DiagnosticOpts;
+  /// Diagnostic consumer.
+  clang::TextDiagnosticPrinter DC;
+  /// Diagnostic engine.
+  llvm::IntrusiveRefCntPtr<clang::DiagnosticsEngine> Diagnostics;
+  /// Options controlling the target.
+  std::shared_ptr<clang::TargetOptions> TargetOpts;
+  /// Target information.
+  llvm::IntrusiveRefCntPtr<clang::TargetInfo> Target;
+  /// Options controlling the file system manager.
+  clang::FileSystemOptions FileSystemOpts;
+  /// File system manager.
+  llvm::IntrusiveRefCntPtr<clang::FileManager> FileMgr;
+  /// Source manager.
+  llvm::IntrusiveRefCntPtr<clang::SourceManager> SourceMgr;
+  /// Header search manager.
+  std::unique_ptr<clang::HeaderSearch> HeaderInfo;
+  // The loaded module map objects.
+  std::vector<std::unique_ptr<clang::ModuleMap>> ModuleMaps;
+};
+
+} // end namespace Modularize
+
+#endif // MODULARIZEUTILITIES_H
diff --git a/modularize/ModuleAssistant.cpp b/modularize/ModuleAssistant.cpp
new file mode 100644 (file)
index 0000000..26c5925
--- /dev/null
@@ -0,0 +1,316 @@
+//===--- ModuleAssistant.cpp - Module map generation manager -*- C++ -*---===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===---------------------------------------------------------------------===//
+//
+// This file defines the module generation entry point function,
+// createModuleMap, a Module class for representing a module,
+// and various implementation functions for doing the underlying
+// work, described below.
+//
+// The "Module" class represents a module, with members for storing the module
+// name, associated header file names, and sub-modules, and an "output"
+// function that recursively writes the module definitions.
+//
+// The "createModuleMap" function implements the top-level logic of the
+// assistant mode.  It calls a loadModuleDescriptions function to walk
+// the header list passed to it and creates a tree of Module objects
+// representing the module hierarchy, represented by a "Module" object,
+// the "RootModule".  This root module may or may not represent an actual
+// module in the module map, depending on the "--root-module" option passed
+// to modularize.  It then calls a writeModuleMap function to set up the
+// module map file output and walk the module tree, outputting the module
+// map file using a stream obtained and managed by an
+// llvm::tool_output_file object.
+//
+//===---------------------------------------------------------------------===//
+
+#include "Modularize.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/ToolOutputFile.h"
+#include <vector>
+
+// Local definitions:
+
+namespace {
+
+// Internal class definitions:
+
+// Represents a module.
+class Module {
+public:
+  Module(llvm::StringRef Name, bool Problem);
+  Module();
+  ~Module();
+  bool output(llvm::raw_fd_ostream &OS, int Indent);
+  Module *findSubModule(llvm::StringRef SubName);
+
+public:
+  std::string Name;
+  std::vector<std::string> HeaderFileNames;
+  std::vector<Module *> SubModules;
+  bool IsProblem;
+};
+
+} // end anonymous namespace.
+
+// Module functions:
+
+// Constructors.
+Module::Module(llvm::StringRef Name, bool Problem)
+  : Name(Name), IsProblem(Problem) {}
+Module::Module() : IsProblem(false) {}
+
+// Destructor.
+Module::~Module() {
+  // Free submodules.
+  while (!SubModules.empty()) {
+    Module *last = SubModules.back();
+    SubModules.pop_back();
+    delete last;
+  }
+}
+
+// Write a module hierarchy to the given output stream.
+bool Module::output(llvm::raw_fd_ostream &OS, int Indent) {
+  // If this is not the nameless root module, start a module definition.
+  if (Name.size() != 0) {
+    OS.indent(Indent);
+    OS << "module " << Name << " {\n";
+    Indent += 2;
+  }
+
+  // Output submodules.
+  for (auto I = SubModules.begin(), E = SubModules.end(); I != E; ++I) {
+    if (!(*I)->output(OS, Indent))
+      return false;
+  }
+
+  // Output header files.
+  for (auto I = HeaderFileNames.begin(), E = HeaderFileNames.end(); I != E;
+       ++I) {
+    OS.indent(Indent);
+    if (IsProblem || strstr((*I).c_str(), ".inl"))
+      OS << "exclude header \"" << *I << "\"\n";
+    else
+      OS << "header \"" << *I << "\"\n";
+  }
+
+  // If this module has header files, output export directive.
+  if (HeaderFileNames.size() != 0) {
+    OS.indent(Indent);
+    OS << "export *\n";
+  }
+
+  // If this is not the nameless root module, close the module definition.
+  if (Name.size() != 0) {
+    Indent -= 2;
+    OS.indent(Indent);
+    OS << "}\n";
+  }
+
+  return true;
+}
+
+// Lookup a sub-module.
+Module *Module::findSubModule(llvm::StringRef SubName) {
+  for (auto I = SubModules.begin(), E = SubModules.end(); I != E; ++I) {
+    if ((*I)->Name == SubName)
+      return *I;
+  }
+  return nullptr;
+}
+
+// Implementation functions:
+
+// Reserved keywords in module.modulemap syntax.
+// Keep in sync with keywords in module map parser in Lex/ModuleMap.cpp,
+// such as in ModuleMapParser::consumeToken().
+static const char *const ReservedNames[] = {
+  "config_macros", "export",   "module", "conflict", "framework",
+  "requires",      "exclude",  "header", "private",  "explicit",
+  "link",          "umbrella", "extern", "use",      nullptr // Flag end.
+};
+
+// Convert module name to a non-keyword.
+// Prepends a '_' to the name if and only if the name is a keyword.
+static std::string
+ensureNoCollisionWithReservedName(llvm::StringRef MightBeReservedName) {
+  std::string SafeName = MightBeReservedName;
+  for (int Index = 0; ReservedNames[Index] != nullptr; ++Index) {
+    if (MightBeReservedName == ReservedNames[Index]) {
+      SafeName.insert(0, "_");
+      break;
+    }
+  }
+  return SafeName;
+}
+
+// Convert module name to a non-keyword.
+// Prepends a '_' to the name if and only if the name is a keyword.
+static std::string
+ensureVaidModuleName(llvm::StringRef MightBeInvalidName) {
+  std::string SafeName = MightBeInvalidName;
+  std::replace(SafeName.begin(), SafeName.end(), '-', '_');
+  std::replace(SafeName.begin(), SafeName.end(), '.', '_');
+  if (isdigit(SafeName[0]))
+    SafeName = "_" + SafeName;
+  return SafeName;
+}
+
+// Add one module, given a header file path.
+static bool addModuleDescription(Module *RootModule,
+                                 llvm::StringRef HeaderFilePath,
+                                 llvm::StringRef HeaderPrefix,
+                                 DependencyMap &Dependencies,
+                                 bool IsProblemFile) {
+  Module *CurrentModule = RootModule;
+  DependentsVector &FileDependents = Dependencies[HeaderFilePath];
+  std::string FilePath;
+  // Strip prefix.
+  // HeaderFilePath should be compared to natively-canonicalized Prefix.
+  llvm::SmallString<256> NativePath, NativePrefix;
+  llvm::sys::path::native(HeaderFilePath, NativePath);
+  llvm::sys::path::native(HeaderPrefix, NativePrefix);
+  if (NativePath.startswith(NativePrefix))
+    FilePath = NativePath.substr(NativePrefix.size() + 1);
+  else
+    FilePath = HeaderFilePath;
+  int Count = FileDependents.size();
+  // Headers that go into modules must not depend on other files being
+  // included first.  If there are any dependents, warn user and omit.
+  if (Count != 0) {
+    llvm::errs() << "warning: " << FilePath
+                 << " depends on other headers being included first,"
+                    " meaning the module.modulemap won't compile."
+                    "  This header will be omitted from the module map.\n";
+    return true;
+  }
+  // Make canonical.
+  std::replace(FilePath.begin(), FilePath.end(), '\\', '/');
+  // Insert module into tree, using subdirectories as submodules.
+  for (llvm::sys::path::const_iterator I = llvm::sys::path::begin(FilePath),
+                                       E = llvm::sys::path::end(FilePath);
+       I != E; ++I) {
+    if ((*I)[0] == '.')
+      continue;
+    std::string Stem = llvm::sys::path::stem(*I);
+    Stem = ensureNoCollisionWithReservedName(Stem);
+    Stem = ensureVaidModuleName(Stem);
+    Module *SubModule = CurrentModule->findSubModule(Stem);
+    if (!SubModule) {
+      SubModule = new Module(Stem, IsProblemFile);
+      CurrentModule->SubModules.push_back(SubModule);
+    }
+    CurrentModule = SubModule;
+  }
+  // Add header file name to headers.
+  CurrentModule->HeaderFileNames.push_back(FilePath);
+  return true;
+}
+
+// Create the internal module tree representation.
+static Module *loadModuleDescriptions(
+    llvm::StringRef RootModuleName, llvm::ArrayRef<std::string> HeaderFileNames,
+    llvm::ArrayRef<std::string> ProblemFileNames,
+    DependencyMap &Dependencies, llvm::StringRef HeaderPrefix) {
+
+  // Create root module.
+  auto *RootModule = new Module(RootModuleName, false);
+
+  llvm::SmallString<256> CurrentDirectory;
+  llvm::sys::fs::current_path(CurrentDirectory);
+
+  // If no header prefix, use current directory.
+  if (HeaderPrefix.size() == 0)
+    HeaderPrefix = CurrentDirectory;
+
+  // Walk the header file names and output the module map.
+  for (llvm::ArrayRef<std::string>::iterator I = HeaderFileNames.begin(),
+                                             E = HeaderFileNames.end();
+       I != E; ++I) {
+    std::string Header(*I);
+    bool IsProblemFile = false;
+    for (auto &ProblemFile : ProblemFileNames) {
+      if (ProblemFile == Header) {
+        IsProblemFile = true;
+        break;
+      }
+    }
+    // Add as a module.
+    if (!addModuleDescription(RootModule, Header, HeaderPrefix, Dependencies, IsProblemFile))
+      return nullptr;
+  }
+
+  return RootModule;
+}
+
+// Kick off the writing of the module map.
+static bool writeModuleMap(llvm::StringRef ModuleMapPath,
+                           llvm::StringRef HeaderPrefix, Module *RootModule) {
+  llvm::SmallString<256> HeaderDirectory(ModuleMapPath);
+  llvm::sys::path::remove_filename(HeaderDirectory);
+  llvm::SmallString<256> FilePath;
+
+  // Get the module map file path to be used.
+  if ((HeaderDirectory.size() == 0) && (HeaderPrefix.size() != 0)) {
+    FilePath = HeaderPrefix;
+    // Prepend header file name prefix if it's not absolute.
+    llvm::sys::path::append(FilePath, ModuleMapPath);
+    llvm::sys::path::native(FilePath);
+  } else {
+    FilePath = ModuleMapPath;
+    llvm::sys::path::native(FilePath);
+  }
+
+  // Set up module map output file.
+  std::error_code EC;
+  llvm::tool_output_file Out(FilePath, EC, llvm::sys::fs::F_Text);
+  if (EC) {
+    llvm::errs() << Argv0 << ": error opening " << FilePath << ":"
+                 << EC.message() << "\n";
+    return false;
+  }
+
+  // Get output stream from tool output buffer/manager.
+  llvm::raw_fd_ostream &OS = Out.os();
+
+  // Output file comment.
+  OS << "// " << ModuleMapPath << "\n";
+  OS << "// Generated by: " << CommandLine << "\n\n";
+
+  // Write module hierarchy from internal representation.
+  if (!RootModule->output(OS, 0))
+    return false;
+
+  // Tell tool_output_file that we want to keep the file.
+  Out.keep();
+
+  return true;
+}
+
+// Global functions:
+
+// Module map generation entry point.
+bool createModuleMap(llvm::StringRef ModuleMapPath,
+                     llvm::ArrayRef<std::string> HeaderFileNames,
+                     llvm::ArrayRef<std::string> ProblemFileNames,
+                     DependencyMap &Dependencies, llvm::StringRef HeaderPrefix,
+                     llvm::StringRef RootModuleName) {
+  // Load internal representation of modules.
+  std::unique_ptr<Module> RootModule(
+    loadModuleDescriptions(
+      RootModuleName, HeaderFileNames, ProblemFileNames, Dependencies,
+      HeaderPrefix));
+  if (!RootModule.get())
+    return false;
+
+  // Write module map file.
+  return writeModuleMap(ModuleMapPath, HeaderPrefix, RootModule.get());
+}
diff --git a/modularize/PreprocessorTracker.cpp b/modularize/PreprocessorTracker.cpp
new file mode 100644 (file)
index 0000000..1df9069
--- /dev/null
@@ -0,0 +1,1393 @@
+//===--- PreprocessorTracker.cpp - Preprocessor tracking -*- C++ -*------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===--------------------------------------------------------------------===//
+//
+// The Basic Idea (Macro and Conditional Checking)
+//
+// Basically we install a PPCallbacks-derived object to track preprocessor
+// activity, namely when a header file is entered/exited, when a macro
+// is expanded, when "defined" is used, and when #if, #elif, #ifdef,
+// and #ifndef are used.  We save the state of macro and "defined"
+// expressions in a map, keyed on a name/file/line/column quadruple.
+// The map entries store the different states (values) that a macro expansion,
+// "defined" expression, or condition expression has in the course of
+// processing for the one location in the one header containing it,
+// plus a list of the nested include stacks for the states.  When a macro
+// or "defined" expression evaluates to the same value, which is the
+// desired case, only one state is stored.  Similarly, for conditional
+// directives, we save the condition expression states in a separate map.
+//
+// This information is collected as modularize compiles all the headers
+// given to it to process.  After all the compilations are performed,
+// a check is performed for any entries in the maps that contain more
+// than one different state, and for these an output message is generated.
+//
+// For example:
+//
+//   (...)/SubHeader.h:11:5:
+//   #if SYMBOL == 1
+//       ^
+//   error: Macro instance 'SYMBOL' has different values in this header,
+//          depending on how it was included.
+//     'SYMBOL' expanded to: '1' with respect to these inclusion paths:
+//       (...)/Header1.h
+//         (...)/SubHeader.h
+//   (...)/SubHeader.h:3:9:
+//   #define SYMBOL 1
+//             ^
+//   Macro defined here.
+//     'SYMBOL' expanded to: '2' with respect to these inclusion paths:
+//       (...)/Header2.h
+//           (...)/SubHeader.h
+//   (...)/SubHeader.h:7:9:
+//   #define SYMBOL 2
+//             ^
+//   Macro defined here.
+//
+// The Basic Idea ('Extern "C/C++" {}' Or 'namespace {}') With Nested
+// '#include' Checking)
+//
+// To check for '#include' directives nested inside 'Extern "C/C++" {}'
+// or 'namespace {}' blocks, we keep track of the '#include' directives
+// while running the preprocessor, and later during a walk of the AST
+// we call a function to check for any '#include' directies inside
+// an 'Extern "C/C++" {}' or 'namespace {}' block, given its source
+// range.
+//
+// Design and Implementation Details (Macro and Conditional Checking)
+//
+// A PreprocessorTrackerImpl class implements the PreprocessorTracker
+// interface. It uses a PreprocessorCallbacks class derived from PPCallbacks
+// to track preprocessor activity, namely entering/exiting a header, macro
+// expansions, use of "defined" expressions, and #if, #elif, #ifdef, and
+// #ifndef conditional directives. PreprocessorTrackerImpl stores a map
+// of MacroExpansionTracker objects keyed on a name/file/line/column
+// value represented by a light-weight PPItemKey value object. This
+// is the key top-level data structure tracking the values of macro
+// expansion instances.  Similarly, it stores a map of ConditionalTracker
+// objects with the same kind of key, for tracking preprocessor conditional
+// directives.
+//
+// The MacroExpansionTracker object represents one macro reference or use
+// of a "defined" expression in a header file. It stores a handle to a
+// string representing the unexpanded macro instance, a handle to a string
+// representing the unpreprocessed source line containing the unexpanded
+// macro instance, and a vector of one or more MacroExpansionInstance
+// objects.
+//
+// The MacroExpansionInstance object represents one or more expansions
+// of a macro reference, for the case where the macro expands to the same
+// value. MacroExpansionInstance stores a handle to a string representing
+// the expanded macro value, a PPItemKey representing the file/line/column
+// where the macro was defined, a handle to a string representing the source
+// line containing the macro definition, and a vector of InclusionPathHandle
+// values that represents the hierarchies of include files for each case
+// where the particular header containing the macro reference was referenced
+// or included.
+
+// In the normal case where a macro instance always expands to the same
+// value, the MacroExpansionTracker object will only contain one
+// MacroExpansionInstance representing all the macro expansion instances.
+// If a case was encountered where a macro instance expands to a value
+// that is different from that seen before, or the macro was defined in
+// a different place, a new MacroExpansionInstance object representing
+// that case will be added to the vector in MacroExpansionTracker. If a
+// macro instance expands to a value already seen before, the
+// InclusionPathHandle representing that case's include file hierarchy
+// will be added to the existing MacroExpansionInstance object.
+
+// For checking conditional directives, the ConditionalTracker class
+// functions similarly to MacroExpansionTracker, but tracks an #if,
+// #elif, #ifdef, or #ifndef directive in a header file.  It stores
+// a vector of one or two ConditionalExpansionInstance objects,
+// representing the cases where the conditional expression evaluates
+// to true or false.  This latter object stores the evaluated value
+// of the condition expression (a bool) and a vector of
+// InclusionPathHandles.
+//
+// To reduce the instances of string and object copying, the
+// PreprocessorTrackerImpl class uses a StringPool to save all stored
+// strings, and defines a StringHandle type to abstract the references
+// to the strings.
+//
+// PreprocessorTrackerImpl also maintains a list representing the unique
+// headers, which is just a vector of StringHandle's for the header file
+// paths. A HeaderHandle abstracts a reference to a header, and is simply
+// the index of the stored header file path.
+//
+// A HeaderInclusionPath class abstracts a unique hierarchy of header file
+// inclusions. It simply stores a vector of HeaderHandles ordered from the
+// top-most header (the one from the header list passed to modularize) down
+// to the header containing the macro reference. PreprocessorTrackerImpl
+// stores a vector of these objects. An InclusionPathHandle typedef
+// abstracts a reference to one of the HeaderInclusionPath objects, and is
+// simply the index of the stored HeaderInclusionPath object. The
+// MacroExpansionInstance object stores a vector of these handles so that
+// the reporting function can display the include hierarchies for the macro
+// expansion instances represented by that object, to help the user
+// understand how the header was included. (A future enhancement might
+// be to associate a line number for the #include directives, but I
+// think not doing so is good enough for the present.)
+//
+// A key reason for using these opaque handles was to try to keep all the
+// internal objects light-weight value objects, in order to reduce string
+// and object copying overhead, and to abstract this implementation detail.
+//
+// The key data structures are built up while modularize runs the headers
+// through the compilation. A PreprocessorTracker instance is created and
+// passed down to the AST action and consumer objects in modularize. For
+// each new compilation instance, the consumer calls the
+// PreprocessorTracker's handleNewPreprocessorEntry function, which sets
+// up a PreprocessorCallbacks object for the preprocessor. At the end of
+// the compilation instance, the PreprocessorTracker's
+// handleNewPreprocessorExit function handles cleaning up with respect
+// to the preprocessing instance.
+//
+// The PreprocessorCallbacks object uses an overidden FileChanged callback
+// to determine when a header is entered and exited (including exiting the
+// header during #include directives). It calls PreprocessorTracker's
+// handleHeaderEntry and handleHeaderExit functions upon entering and
+// exiting a header. These functions manage a stack of header handles
+// representing by a vector, pushing and popping header handles as headers
+// are entered and exited. When a HeaderInclusionPath object is created,
+// it simply copies this stack.
+//
+// The PreprocessorCallbacks object uses an overridden MacroExpands callback
+// to track when a macro expansion is performed. It calls a couple of helper
+// functions to get the unexpanded and expanded macro values as strings, but
+// then calls PreprocessorTrackerImpl's addMacroExpansionInstance function to
+// do the rest of the work. The getMacroExpandedString function uses the
+// preprocessor's getSpelling to convert tokens to strings using the
+// information passed to the MacroExpands callback, and simply concatenates
+// them. It makes recursive calls to itself to handle nested macro
+// definitions, and also handles function-style macros.
+//
+// PreprocessorTrackerImpl's addMacroExpansionInstance function looks for
+// an existing MacroExpansionTracker entry in its map of MacroExampleTracker
+// objects. If none exists, it adds one with one MacroExpansionInstance and
+// returns. If a MacroExpansionTracker object already exists, it looks for
+// an existing MacroExpansionInstance object stored in the
+// MacroExpansionTracker object, one that matches the macro expanded value
+// and the macro definition location. If a matching MacroExpansionInstance
+// object is found, it just adds the current HeaderInclusionPath object to
+// it. If not found, it creates and stores a new MacroExpantionInstance
+// object. The addMacroExpansionInstance function calls a couple of helper
+// functions to get the pre-formatted location and source line strings for
+// the macro reference and the macro definition stored as string handles.
+// These helper functions use the current source manager from the
+// preprocessor. This is done in advance at this point in time because the
+// source manager doesn't exist at the time of the reporting.
+//
+// For conditional check, the PreprocessorCallbacks class overrides the
+// PPCallbacks handlers for #if, #elif, #ifdef, and #ifndef.  These handlers
+// call the addConditionalExpansionInstance method of
+// PreprocessorTrackerImpl.  The process is similar to that of macros, but
+// with some different data and error messages.  A lookup is performed for
+// the conditional, and if a ConditionalTracker object doesn't yet exist for
+// the conditional, a new one is added, including adding a
+// ConditionalExpansionInstance object to it to represent the condition
+// expression state.  If a ConditionalTracker for the conditional does
+// exist, a lookup is made for a ConditionalExpansionInstance object
+// matching the condition expression state.  If one exists, a
+// HeaderInclusionPath is added to it.  Otherwise a new
+// ConditionalExpansionInstance  entry is made.  If a ConditionalTracker
+// has two ConditionalExpansionInstance objects, it means there was a
+// conflict, meaning the conditional expression evaluated differently in
+// one or more cases.
+//
+// After modularize has performed all the compilations, it enters a phase
+// of error reporting. This new feature adds to this reporting phase calls
+// to the PreprocessorTracker's reportInconsistentMacros and
+// reportInconsistentConditionals functions. These functions walk the maps
+// of MacroExpansionTracker's and ConditionalTracker's respectively. If
+// any of these objects have more than one MacroExpansionInstance or
+// ConditionalExpansionInstance objects, it formats and outputs an error
+// message like the example shown previously, using the stored data.
+//
+// A potential issue is that there is some overlap between the #if/#elif
+// conditional and macro reporting.  I could disable the #if and #elif,
+// leaving just the #ifdef and #ifndef, since these don't overlap.  Or,
+// to make clearer the separate reporting phases, I could add an output
+// message marking the phases.
+//
+// Design and Implementation Details ('Extern "C/C++" {}' Or
+// 'namespace {}') With Nested '#include' Checking)
+//
+// We override the InclusionDirective in PPCallbacks to record information
+// about each '#include' directive encountered during preprocessing.
+// We co-opt the PPItemKey class to store the information about each
+// '#include' directive, including the source file name containing the
+// directive, the name of the file being included, and the source line
+// and column of the directive.  We store these object in a vector,
+// after first check to see if an entry already exists.
+//
+// Later, while the AST is being walked for other checks, we provide
+// visit handlers for 'extern "C/C++" {}' and 'namespace (name) {}'
+// blocks, checking to see if any '#include' directives occurred
+// within the blocks, reporting errors if any found.
+//
+// Future Directions
+//
+// We probably should add options to disable any of the checks, in case
+// there is some problem with them, or the messages get too verbose.
+//
+// With the map of all the macro and conditional expansion instances,
+// it might be possible to add to the existing modularize error messages
+// (the second part referring to definitions being different), attempting
+// to tie them to the last macro conflict encountered with respect to the
+// order of the code encountered.
+//
+//===--------------------------------------------------------------------===//
+
+#include "clang/Lex/LexDiagnostic.h"
+#include "PreprocessorTracker.h"
+#include "clang/Lex/MacroArgs.h"
+#include "clang/Lex/PPCallbacks.h"
+#include "llvm/ADT/SmallSet.h"
+#include "llvm/Support/StringPool.h"
+#include "llvm/Support/raw_ostream.h"
+#include "ModularizeUtilities.h"
+
+namespace Modularize {
+
+// Some handle types
+typedef llvm::PooledStringPtr StringHandle;
+
+typedef int HeaderHandle;
+const HeaderHandle HeaderHandleInvalid = -1;
+
+typedef int InclusionPathHandle;
+const InclusionPathHandle InclusionPathHandleInvalid = -1;
+
+// Some utility functions.
+
+// Get a "file:line:column" source location string.
+static std::string getSourceLocationString(clang::Preprocessor &PP,
+                                           clang::SourceLocation Loc) {
+  if (Loc.isInvalid())
+    return std::string("(none)");
+  else
+    return Loc.printToString(PP.getSourceManager());
+}
+
+// Get just the file name from a source location.
+static std::string getSourceLocationFile(clang::Preprocessor &PP,
+                                         clang::SourceLocation Loc) {
+  std::string Source(getSourceLocationString(PP, Loc));
+  size_t Offset = Source.find(':', 2);
+  if (Offset == std::string::npos)
+    return Source;
+  return Source.substr(0, Offset);
+}
+
+// Get just the line and column from a source location.
+static void getSourceLocationLineAndColumn(clang::Preprocessor &PP,
+                                           clang::SourceLocation Loc, int &Line,
+                                           int &Column) {
+  clang::PresumedLoc PLoc = PP.getSourceManager().getPresumedLoc(Loc);
+  if (PLoc.isInvalid()) {
+    Line = 0;
+    Column = 0;
+    return;
+  }
+  Line = PLoc.getLine();
+  Column = PLoc.getColumn();
+}
+
+// Retrieve source snippet from file image.
+static std::string getSourceString(clang::Preprocessor &PP,
+                                   clang::SourceRange Range) {
+  clang::SourceLocation BeginLoc = Range.getBegin();
+  clang::SourceLocation EndLoc = Range.getEnd();
+  const char *BeginPtr = PP.getSourceManager().getCharacterData(BeginLoc);
+  const char *EndPtr = PP.getSourceManager().getCharacterData(EndLoc);
+  size_t Length = EndPtr - BeginPtr;
+  return llvm::StringRef(BeginPtr, Length).trim().str();
+}
+
+// Retrieve source line from file image given a location.
+static std::string getSourceLine(clang::Preprocessor &PP,
+                                 clang::SourceLocation Loc) {
+  const llvm::MemoryBuffer *MemBuffer =
+      PP.getSourceManager().getBuffer(PP.getSourceManager().getFileID(Loc));
+  const char *Buffer = MemBuffer->getBufferStart();
+  const char *BufferEnd = MemBuffer->getBufferEnd();
+  const char *BeginPtr = PP.getSourceManager().getCharacterData(Loc);
+  const char *EndPtr = BeginPtr;
+  while (BeginPtr > Buffer) {
+    if (*BeginPtr == '\n') {
+      BeginPtr++;
+      break;
+    }
+    BeginPtr--;
+  }
+  while (EndPtr < BufferEnd) {
+    if (*EndPtr == '\n') {
+      break;
+    }
+    EndPtr++;
+  }
+  size_t Length = EndPtr - BeginPtr;
+  return llvm::StringRef(BeginPtr, Length).str();
+}
+
+// Retrieve source line from file image given a file ID and line number.
+static std::string getSourceLine(clang::Preprocessor &PP, clang::FileID FileID,
+                                 int Line) {
+  const llvm::MemoryBuffer *MemBuffer = PP.getSourceManager().getBuffer(FileID);
+  const char *Buffer = MemBuffer->getBufferStart();
+  const char *BufferEnd = MemBuffer->getBufferEnd();
+  const char *BeginPtr = Buffer;
+  const char *EndPtr = BufferEnd;
+  int LineCounter = 1;
+  if (Line == 1)
+    BeginPtr = Buffer;
+  else {
+    while (Buffer < BufferEnd) {
+      if (*Buffer == '\n') {
+        if (++LineCounter == Line) {
+          BeginPtr = Buffer++ + 1;
+          break;
+        }
+      }
+      Buffer++;
+    }
+  }
+  while (Buffer < BufferEnd) {
+    if (*Buffer == '\n') {
+      EndPtr = Buffer;
+      break;
+    }
+    Buffer++;
+  }
+  size_t Length = EndPtr - BeginPtr;
+  return llvm::StringRef(BeginPtr, Length).str();
+}
+
+// Get the string for the Unexpanded macro instance.
+// The soureRange is expected to end at the last token
+// for the macro instance, which in the case of a function-style
+// macro will be a ')', but for an object-style macro, it
+// will be the macro name itself.
+static std::string getMacroUnexpandedString(clang::SourceRange Range,
+                                            clang::Preprocessor &PP,
+                                            llvm::StringRef MacroName,
+                                            const clang::MacroInfo *MI) {
+  clang::SourceLocation BeginLoc(Range.getBegin());
+  const char *BeginPtr = PP.getSourceManager().getCharacterData(BeginLoc);
+  size_t Length;
+  std::string Unexpanded;
+  if (MI->isFunctionLike()) {
+    clang::SourceLocation EndLoc(Range.getEnd());
+    const char *EndPtr = PP.getSourceManager().getCharacterData(EndLoc) + 1;
+    Length = (EndPtr - BeginPtr) + 1; // +1 is ')' width.
+  } else
+    Length = MacroName.size();
+  return llvm::StringRef(BeginPtr, Length).trim().str();
+}
+
+// Get the expansion for a macro instance, given the information
+// provided by PPCallbacks.
+// FIXME: This doesn't support function-style macro instances
+// passed as arguments to another function-style macro. However,
+// since it still expands the inner arguments, it still
+// allows modularize to effectively work with respect to macro
+// consistency checking, although it displays the incorrect
+// expansion in error messages.
+static std::string getMacroExpandedString(clang::Preprocessor &PP,
+                                          llvm::StringRef MacroName,
+                                          const clang::MacroInfo *MI,
+                                          const clang::MacroArgs *Args) {
+  std::string Expanded;
+  // Walk over the macro Tokens.
+  for (const auto &T : MI->tokens()) {
+    clang::IdentifierInfo *II = T.getIdentifierInfo();
+    int ArgNo = (II && Args ? MI->getParameterNum(II) : -1);
+    if (ArgNo == -1) {
+      // This isn't an argument, just add it.
+      if (II == nullptr)
+        Expanded += PP.getSpelling(T); // Not an identifier.
+      else {
+        // Token is for an identifier.
+        std::string Name = II->getName().str();
+        // Check for nexted macro references.
+        clang::MacroInfo *MacroInfo = PP.getMacroInfo(II);
+        if (MacroInfo && (Name != MacroName))
+          Expanded += getMacroExpandedString(PP, Name, MacroInfo, nullptr);
+        else
+          Expanded += Name;
+      }
+      continue;
+    }
+    // We get here if it's a function-style macro with arguments.
+    const clang::Token *ResultArgToks;
+    const clang::Token *ArgTok = Args->getUnexpArgument(ArgNo);
+    if (Args->ArgNeedsPreexpansion(ArgTok, PP))
+      ResultArgToks = &(const_cast<clang::MacroArgs *>(Args))
+          ->getPreExpArgument(ArgNo, MI, PP)[0];
+    else
+      ResultArgToks = ArgTok; // Use non-preexpanded Tokens.
+    // If the arg token didn't expand into anything, ignore it.
+    if (ResultArgToks->is(clang::tok::eof))
+      continue;
+    unsigned NumToks = clang::MacroArgs::getArgLength(ResultArgToks);
+    // Append the resulting argument expansions.
+    for (unsigned ArgumentIndex = 0; ArgumentIndex < NumToks; ++ArgumentIndex) {
+      const clang::Token &AT = ResultArgToks[ArgumentIndex];
+      clang::IdentifierInfo *II = AT.getIdentifierInfo();
+      if (II == nullptr)
+        Expanded += PP.getSpelling(AT); // Not an identifier.
+      else {
+        // It's an identifier.  Check for further expansion.
+        std::string Name = II->getName().str();
+        clang::MacroInfo *MacroInfo = PP.getMacroInfo(II);
+        if (MacroInfo)
+          Expanded += getMacroExpandedString(PP, Name, MacroInfo, nullptr);
+        else
+          Expanded += Name;
+      }
+    }
+  }
+  return Expanded;
+}
+
+namespace {
+
+// ConditionValueKind strings.
+const char *
+ConditionValueKindStrings[] = {
+  "(not evaluated)", "false", "true"
+};
+
+bool operator<(const StringHandle &H1, const StringHandle &H2) {
+  const char *S1 = (H1 ? *H1 : "");
+  const char *S2 = (H2 ? *H2 : "");
+  int Diff = strcmp(S1, S2);
+  return Diff < 0;
+}
+bool operator>(const StringHandle &H1, const StringHandle &H2) {
+  const char *S1 = (H1 ? *H1 : "");
+  const char *S2 = (H2 ? *H2 : "");
+  int Diff = strcmp(S1, S2);
+  return Diff > 0;
+}
+
+// Preprocessor item key.
+//
+// This class represents a location in a source file, for use
+// as a key representing a unique name/file/line/column quadruplet,
+// which in this case is used to identify a macro expansion instance,
+// but could be used for other things as well.
+// The file is a header file handle, the line is a line number,
+// and the column is a column number.
+class PPItemKey {
+public:
+  PPItemKey(clang::Preprocessor &PP, StringHandle Name, HeaderHandle File,
+            clang::SourceLocation Loc)
+      : Name(Name), File(File) {
+    getSourceLocationLineAndColumn(PP, Loc, Line, Column);
+  }
+  PPItemKey(StringHandle Name, HeaderHandle File, int Line, int Column)
+      : Name(Name), File(File), Line(Line), Column(Column) {}
+  PPItemKey(const PPItemKey &Other)
+      : Name(Other.Name), File(Other.File), Line(Other.Line),
+        Column(Other.Column) {}
+  PPItemKey() : File(HeaderHandleInvalid), Line(0), Column(0) {}
+  bool operator==(const PPItemKey &Other) const {
+    if (Name != Other.Name)
+      return false;
+    if (File != Other.File)
+      return false;
+    if (Line != Other.Line)
+      return false;
+    return Column == Other.Column;
+  }
+  bool operator<(const PPItemKey &Other) const {
+    if (Name < Other.Name)
+      return true;
+    else if (Name > Other.Name)
+      return false;
+    if (File < Other.File)
+      return true;
+    else if (File > Other.File)
+      return false;
+    if (Line < Other.Line)
+      return true;
+    else if (Line > Other.Line)
+      return false;
+    return Column < Other.Column;
+  }
+  StringHandle Name;
+  HeaderHandle File;
+  int Line;
+  int Column;
+};
+
+// Header inclusion path.
+class HeaderInclusionPath {
+public:
+  HeaderInclusionPath(std::vector<HeaderHandle> HeaderInclusionPath)
+      : Path(HeaderInclusionPath) {}
+  HeaderInclusionPath(const HeaderInclusionPath &Other) : Path(Other.Path) {}
+  HeaderInclusionPath() {}
+  std::vector<HeaderHandle> Path;
+};
+
+// Macro expansion instance.
+//
+// This class represents an instance of a macro expansion with a
+// unique value.  It also stores the unique header inclusion paths
+// for use in telling the user the nested include path to the header.
+class MacroExpansionInstance {
+public:
+  MacroExpansionInstance(StringHandle MacroExpanded,
+                         PPItemKey &DefinitionLocation,
+                         StringHandle DefinitionSourceLine,
+                         InclusionPathHandle H)
+      : MacroExpanded(MacroExpanded), DefinitionLocation(DefinitionLocation),
+        DefinitionSourceLine(DefinitionSourceLine) {
+    InclusionPathHandles.push_back(H);
+  }
+  MacroExpansionInstance() {}
+
+  // Check for the presence of a header inclusion path handle entry.
+  // Return false if not found.
+  bool haveInclusionPathHandle(InclusionPathHandle H) {
+    for (auto I = InclusionPathHandles.begin(), E = InclusionPathHandles.end();
+         I != E; ++I) {
+      if (*I == H)
+        return true;
+    }
+    return InclusionPathHandleInvalid;
+  }
+  // Add a new header inclusion path entry, if not already present.
+  void addInclusionPathHandle(InclusionPathHandle H) {
+    if (!haveInclusionPathHandle(H))
+      InclusionPathHandles.push_back(H);
+  }
+
+  // A string representing the macro instance after preprocessing.
+  StringHandle MacroExpanded;
+  // A file/line/column triplet representing the macro definition location.
+  PPItemKey DefinitionLocation;
+  // A place to save the macro definition line string.
+  StringHandle DefinitionSourceLine;
+  // The header inclusion path handles for all the instances.
+  std::vector<InclusionPathHandle> InclusionPathHandles;
+};
+
+// Macro expansion instance tracker.
+//
+// This class represents one macro expansion, keyed by a PPItemKey.
+// It stores a string representing the macro reference in the source,
+// and a list of ConditionalExpansionInstances objects representing
+// the unique values the condition expands to in instances of the header.
+class MacroExpansionTracker {
+public:
+  MacroExpansionTracker(StringHandle MacroUnexpanded,
+                        StringHandle MacroExpanded,
+                        StringHandle InstanceSourceLine,
+                        PPItemKey &DefinitionLocation,
+                        StringHandle DefinitionSourceLine,
+                        InclusionPathHandle InclusionPathHandle)
+      : MacroUnexpanded(MacroUnexpanded),
+        InstanceSourceLine(InstanceSourceLine) {
+    addMacroExpansionInstance(MacroExpanded, DefinitionLocation,
+                              DefinitionSourceLine, InclusionPathHandle);
+  }
+  MacroExpansionTracker() {}
+
+  // Find a matching macro expansion instance.
+  MacroExpansionInstance *
+  findMacroExpansionInstance(StringHandle MacroExpanded,
+                             PPItemKey &DefinitionLocation) {
+    for (auto I = MacroExpansionInstances.begin(),
+              E = MacroExpansionInstances.end();
+         I != E; ++I) {
+      if ((I->MacroExpanded == MacroExpanded) &&
+          (I->DefinitionLocation == DefinitionLocation)) {
+        return &*I; // Found.
+      }
+    }
+    return nullptr; // Not found.
+  }
+
+  // Add a macro expansion instance.
+  void addMacroExpansionInstance(StringHandle MacroExpanded,
+                                 PPItemKey &DefinitionLocation,
+                                 StringHandle DefinitionSourceLine,
+                                 InclusionPathHandle InclusionPathHandle) {
+    MacroExpansionInstances.push_back(
+        MacroExpansionInstance(MacroExpanded, DefinitionLocation,
+                               DefinitionSourceLine, InclusionPathHandle));
+  }
+
+  // Return true if there is a mismatch.
+  bool hasMismatch() { return MacroExpansionInstances.size() > 1; }
+
+  // A string representing the macro instance without expansion.
+  StringHandle MacroUnexpanded;
+  // A place to save the macro instance source line string.
+  StringHandle InstanceSourceLine;
+  // The macro expansion instances.
+  // If all instances of the macro expansion expand to the same value,
+  // This vector will only have one instance.
+  std::vector<MacroExpansionInstance> MacroExpansionInstances;
+};
+
+// Conditional expansion instance.
+//
+// This class represents an instance of a condition exoression result
+// with a unique value.  It also stores the unique header inclusion paths
+// for use in telling the user the nested include path to the header.
+class ConditionalExpansionInstance {
+public:
+  ConditionalExpansionInstance(clang::PPCallbacks::ConditionValueKind ConditionValue, InclusionPathHandle H)
+      : ConditionValue(ConditionValue) {
+    InclusionPathHandles.push_back(H);
+  }
+  ConditionalExpansionInstance() {}
+
+  // Check for the presence of a header inclusion path handle entry.
+  // Return false if not found.
+  bool haveInclusionPathHandle(InclusionPathHandle H) {
+    for (auto I = InclusionPathHandles.begin(), E = InclusionPathHandles.end();
+         I != E; ++I) {
+      if (*I == H)
+        return true;
+    }
+    return InclusionPathHandleInvalid;
+  }
+  // Add a new header inclusion path entry, if not already present.
+  void addInclusionPathHandle(InclusionPathHandle H) {
+    if (!haveInclusionPathHandle(H))
+      InclusionPathHandles.push_back(H);
+  }
+
+  // A flag representing the evaluated condition value.
+  clang::PPCallbacks::ConditionValueKind ConditionValue;
+  // The header inclusion path handles for all the instances.
+  std::vector<InclusionPathHandle> InclusionPathHandles;
+};
+
+// Conditional directive instance tracker.
+//
+// This class represents one conditional directive, keyed by a PPItemKey.
+// It stores a string representing the macro reference in the source,
+// and a list of ConditionExpansionInstance objects representing
+// the unique value the condition expression expands to in instances of
+// the header.
+class ConditionalTracker {
+public:
+  ConditionalTracker(clang::tok::PPKeywordKind DirectiveKind,
+                     clang::PPCallbacks::ConditionValueKind ConditionValue,
+                     StringHandle ConditionUnexpanded,
+                     InclusionPathHandle InclusionPathHandle)
+      : DirectiveKind(DirectiveKind), ConditionUnexpanded(ConditionUnexpanded) {
+    addConditionalExpansionInstance(ConditionValue, InclusionPathHandle);
+  }
+  ConditionalTracker() {}
+
+  // Find a matching condition expansion instance.
+  ConditionalExpansionInstance *
+  findConditionalExpansionInstance(clang::PPCallbacks::ConditionValueKind ConditionValue) {
+    for (auto I = ConditionalExpansionInstances.begin(),
+              E = ConditionalExpansionInstances.end();
+         I != E; ++I) {
+      if (I->ConditionValue == ConditionValue) {
+        return &*I; // Found.
+      }
+    }
+    return nullptr; // Not found.
+  }
+
+  // Add a conditional expansion instance.
+  void
+  addConditionalExpansionInstance(clang::PPCallbacks::ConditionValueKind ConditionValue,
+                                  InclusionPathHandle InclusionPathHandle) {
+    ConditionalExpansionInstances.push_back(
+        ConditionalExpansionInstance(ConditionValue, InclusionPathHandle));
+  }
+
+  // Return true if there is a mismatch.
+  bool hasMismatch() { return ConditionalExpansionInstances.size() > 1; }
+
+  // The kind of directive.
+  clang::tok::PPKeywordKind DirectiveKind;
+  // A string representing the macro instance without expansion.
+  StringHandle ConditionUnexpanded;
+  // The condition expansion instances.
+  // If all instances of the conditional expression expand to the same value,
+  // This vector will only have one instance.
+  std::vector<ConditionalExpansionInstance> ConditionalExpansionInstances;
+};
+
+class PreprocessorTrackerImpl;
+
+// Preprocessor callbacks for modularize.
+//
+// This class derives from the Clang PPCallbacks class to track preprocessor
+// actions, such as changing files and handling preprocessor directives and
+// macro expansions.  It has to figure out when a new header file is entered
+// and left, as the provided handler is not particularly clear about it.
+class PreprocessorCallbacks : public clang::PPCallbacks {
+public:
+  PreprocessorCallbacks(PreprocessorTrackerImpl &ppTracker,
+                        clang::Preprocessor &PP, llvm::StringRef rootHeaderFile)
+      : PPTracker(ppTracker), PP(PP), RootHeaderFile(rootHeaderFile) {}
+  ~PreprocessorCallbacks() override {}
+
+  // Overridden handlers.
+  void InclusionDirective(clang::SourceLocation HashLoc,
+                          const clang::Token &IncludeTok,
+                          llvm::StringRef FileName, bool IsAngled,
+                          clang::CharSourceRange FilenameRange,
+                          const clang::FileEntry *File,
+                          llvm::StringRef SearchPath,
+                          llvm::StringRef RelativePath,
+                          const clang::Module *Imported) override;
+  void FileChanged(clang::SourceLocation Loc,
+                   clang::PPCallbacks::FileChangeReason Reason,
+                   clang::SrcMgr::CharacteristicKind FileType,
+                   clang::FileID PrevFID = clang::FileID()) override;
+  void MacroExpands(const clang::Token &MacroNameTok,
+                    const clang::MacroDefinition &MD, clang::SourceRange Range,
+                    const clang::MacroArgs *Args) override;
+  void Defined(const clang::Token &MacroNameTok,
+               const clang::MacroDefinition &MD,
+               clang::SourceRange Range) override;
+  void If(clang::SourceLocation Loc, clang::SourceRange ConditionRange,
+          clang::PPCallbacks::ConditionValueKind ConditionResult) override;
+  void Elif(clang::SourceLocation Loc, clang::SourceRange ConditionRange,
+            clang::PPCallbacks::ConditionValueKind ConditionResult,
+            clang::SourceLocation IfLoc) override;
+  void Ifdef(clang::SourceLocation Loc, const clang::Token &MacroNameTok,
+             const clang::MacroDefinition &MD) override;
+  void Ifndef(clang::SourceLocation Loc, const clang::Token &MacroNameTok,
+              const clang::MacroDefinition &MD) override;
+
+private:
+  PreprocessorTrackerImpl &PPTracker;
+  clang::Preprocessor &PP;
+  std::string RootHeaderFile;
+};
+
+// Preprocessor macro expansion item map types.
+typedef std::map<PPItemKey, MacroExpansionTracker> MacroExpansionMap;
+typedef std::map<PPItemKey, MacroExpansionTracker>::iterator
+MacroExpansionMapIter;
+
+// Preprocessor conditional expansion item map types.
+typedef std::map<PPItemKey, ConditionalTracker> ConditionalExpansionMap;
+typedef std::map<PPItemKey, ConditionalTracker>::iterator
+ConditionalExpansionMapIter;
+
+// Preprocessor tracker for modularize.
+//
+// This class stores information about all the headers processed in the
+// course of running modularize.
+class PreprocessorTrackerImpl : public PreprocessorTracker {
+public:
+  PreprocessorTrackerImpl(llvm::SmallVector<std::string, 32> &Headers,
+        bool DoBlockCheckHeaderListOnly)
+      : BlockCheckHeaderListOnly(DoBlockCheckHeaderListOnly),
+        CurrentInclusionPathHandle(InclusionPathHandleInvalid),
+        InNestedHeader(false) {
+    // Use canonical header path representation.
+    for (llvm::ArrayRef<std::string>::iterator I = Headers.begin(),
+      E = Headers.end();
+      I != E; ++I) {
+      HeaderList.push_back(getCanonicalPath(*I));
+    }
+  }
+
+  ~PreprocessorTrackerImpl() override {}
+
+  // Handle entering a preprocessing session.
+  void handlePreprocessorEntry(clang::Preprocessor &PP,
+                               llvm::StringRef rootHeaderFile) override {
+    HeadersInThisCompile.clear();
+    assert((HeaderStack.size() == 0) && "Header stack should be empty.");
+    pushHeaderHandle(addHeader(rootHeaderFile));
+    PP.addPPCallbacks(llvm::make_unique<PreprocessorCallbacks>(*this, PP,
+                                                               rootHeaderFile));
+  }
+  // Handle exiting a preprocessing session.
+  void handlePreprocessorExit() override { HeaderStack.clear(); }
+
+  // Handle include directive.
+  // This function is called every time an include directive is seen by the
+  // preprocessor, for the purpose of later checking for 'extern "" {}' or
+  // "namespace {}" blocks containing #include directives.
+  void handleIncludeDirective(llvm::StringRef DirectivePath, int DirectiveLine,
+                              int DirectiveColumn,
+                              llvm::StringRef TargetPath) override {
+    // If it's not a header in the header list, ignore it with respect to
+    // the check.
+    if (BlockCheckHeaderListOnly && !isHeaderListHeader(TargetPath))
+      return;
+    HeaderHandle CurrentHeaderHandle = findHeaderHandle(DirectivePath);
+    StringHandle IncludeHeaderHandle = addString(TargetPath);
+    for (std::vector<PPItemKey>::const_iterator I = IncludeDirectives.begin(),
+                                                E = IncludeDirectives.end();
+         I != E; ++I) {
+      // If we already have an entry for this directive, return now.
+      if ((I->File == CurrentHeaderHandle) && (I->Line == DirectiveLine))
+        return;
+    }
+    PPItemKey IncludeDirectiveItem(IncludeHeaderHandle, CurrentHeaderHandle,
+                                   DirectiveLine, DirectiveColumn);
+    IncludeDirectives.push_back(IncludeDirectiveItem);
+  }
+
+  // Check for include directives within the given source line range.
+  // Report errors if any found.  Returns true if no include directives
+  // found in block.
+  bool checkForIncludesInBlock(clang::Preprocessor &PP,
+                               clang::SourceRange BlockSourceRange,
+                               const char *BlockIdentifierMessage,
+                               llvm::raw_ostream &OS) override {
+    clang::SourceLocation BlockStartLoc = BlockSourceRange.getBegin();
+    clang::SourceLocation BlockEndLoc = BlockSourceRange.getEnd();
+    // Use block location to get FileID of both the include directive
+    // and block statement.
+    clang::FileID FileID = PP.getSourceManager().getFileID(BlockStartLoc);
+    std::string SourcePath = getSourceLocationFile(PP, BlockStartLoc);
+    SourcePath = ModularizeUtilities::getCanonicalPath(SourcePath);
+    HeaderHandle SourceHandle = findHeaderHandle(SourcePath);
+    if (SourceHandle == -1)
+      return true;
+    int BlockStartLine, BlockStartColumn, BlockEndLine, BlockEndColumn;
+    bool returnValue = true;
+    getSourceLocationLineAndColumn(PP, BlockStartLoc, BlockStartLine,
+                                   BlockStartColumn);
+    getSourceLocationLineAndColumn(PP, BlockEndLoc, BlockEndLine,
+                                   BlockEndColumn);
+    for (std::vector<PPItemKey>::const_iterator I = IncludeDirectives.begin(),
+                                                E = IncludeDirectives.end();
+         I != E; ++I) {
+      // If we find an entry within the block, report an error.
+      if ((I->File == SourceHandle) && (I->Line >= BlockStartLine) &&
+          (I->Line < BlockEndLine)) {
+        returnValue = false;
+        OS << SourcePath << ":" << I->Line << ":" << I->Column << ":\n";
+        OS << getSourceLine(PP, FileID, I->Line) << "\n";
+        if (I->Column > 0)
+          OS << std::string(I->Column - 1, ' ') << "^\n";
+        OS << "error: Include directive within " << BlockIdentifierMessage
+           << ".\n";
+        OS << SourcePath << ":" << BlockStartLine << ":" << BlockStartColumn
+           << ":\n";
+        OS << getSourceLine(PP, BlockStartLoc) << "\n";
+        if (BlockStartColumn > 0)
+          OS << std::string(BlockStartColumn - 1, ' ') << "^\n";
+        OS << "The \"" << BlockIdentifierMessage << "\" block is here.\n";
+      }
+    }
+    return returnValue;
+  }
+
+  // Handle entering a header source file.
+  void handleHeaderEntry(clang::Preprocessor &PP, llvm::StringRef HeaderPath) {
+    // Ignore <built-in> and <command-line> to reduce message clutter.
+    if (HeaderPath.startswith("<"))
+      return;
+    HeaderHandle H = addHeader(HeaderPath);
+    if (H != getCurrentHeaderHandle())
+      pushHeaderHandle(H);
+    // Check for nested header.
+    if (!InNestedHeader)
+      InNestedHeader = !HeadersInThisCompile.insert(H).second;
+  }
+
+  // Handle exiting a header source file.
+  void handleHeaderExit(llvm::StringRef HeaderPath) {
+    // Ignore <built-in> and <command-line> to reduce message clutter.
+    if (HeaderPath.startswith("<"))
+      return;
+    HeaderHandle H = findHeaderHandle(HeaderPath);
+    HeaderHandle TH;
+    if (isHeaderHandleInStack(H)) {
+      do {
+        TH = getCurrentHeaderHandle();
+        popHeaderHandle();
+      } while ((TH != H) && (HeaderStack.size() != 0));
+    }
+    InNestedHeader = false;
+  }
+
+  // Lookup/add string.
+  StringHandle addString(llvm::StringRef Str) { return Strings.intern(Str); }
+
+  // Convert to a canonical path.
+  std::string getCanonicalPath(llvm::StringRef path) const {
+    std::string CanonicalPath(path);
+    std::replace(CanonicalPath.begin(), CanonicalPath.end(), '\\', '/');
+    return CanonicalPath;
+  }
+
+  // Return true if the given header is in the header list.
+  bool isHeaderListHeader(llvm::StringRef HeaderPath) const {
+    std::string CanonicalPath = getCanonicalPath(HeaderPath);
+    for (llvm::ArrayRef<std::string>::iterator I = HeaderList.begin(),
+        E = HeaderList.end();
+        I != E; ++I) {
+      if (*I == CanonicalPath)
+        return true;
+    }
+    return false;
+  }
+
+  // Get the handle of a header file entry.
+  // Return HeaderHandleInvalid if not found.
+  HeaderHandle findHeaderHandle(llvm::StringRef HeaderPath) const {
+    std::string CanonicalPath = getCanonicalPath(HeaderPath);
+    HeaderHandle H = 0;
+    for (auto I = HeaderPaths.begin(), E = HeaderPaths.end(); I != E;
+         ++I, ++H) {
+      if (**I == CanonicalPath)
+        return H;
+    }
+    return HeaderHandleInvalid;
+  }
+
+  // Add a new header file entry, or return existing handle.
+  // Return the header handle.
+  HeaderHandle addHeader(llvm::StringRef HeaderPath) {
+    std::string CanonicalPath = getCanonicalPath(HeaderPath);
+    HeaderHandle H = findHeaderHandle(CanonicalPath);
+    if (H == HeaderHandleInvalid) {
+      H = HeaderPaths.size();
+      HeaderPaths.push_back(addString(CanonicalPath));
+    }
+    return H;
+  }
+
+  // Return a header file path string given its handle.
+  StringHandle getHeaderFilePath(HeaderHandle H) const {
+    if ((H >= 0) && (H < (HeaderHandle)HeaderPaths.size()))
+      return HeaderPaths[H];
+    return StringHandle();
+  }
+
+  // Returns a handle to the inclusion path.
+  InclusionPathHandle pushHeaderHandle(HeaderHandle H) {
+    HeaderStack.push_back(H);
+    return CurrentInclusionPathHandle = addInclusionPathHandle(HeaderStack);
+  }
+  // Pops the last header handle from the stack;
+  void popHeaderHandle() {
+    // assert((HeaderStack.size() != 0) && "Header stack already empty.");
+    if (HeaderStack.size() != 0) {
+      HeaderStack.pop_back();
+      CurrentInclusionPathHandle = addInclusionPathHandle(HeaderStack);
+    }
+  }
+  // Get the top handle on the header stack.
+  HeaderHandle getCurrentHeaderHandle() const {
+    if (HeaderStack.size() != 0)
+      return HeaderStack.back();
+    return HeaderHandleInvalid;
+  }
+
+  // Check for presence of header handle in the header stack.
+  bool isHeaderHandleInStack(HeaderHandle H) const {
+    for (auto I = HeaderStack.begin(), E = HeaderStack.end(); I != E; ++I) {
+      if (*I == H)
+        return true;
+    }
+    return false;
+  }
+
+  // Get the handle of a header inclusion path entry.
+  // Return InclusionPathHandleInvalid if not found.
+  InclusionPathHandle
+  findInclusionPathHandle(const std::vector<HeaderHandle> &Path) const {
+    InclusionPathHandle H = 0;
+    for (auto I = InclusionPaths.begin(), E = InclusionPaths.end(); I != E;
+         ++I, ++H) {
+      if (I->Path == Path)
+        return H;
+    }
+    return HeaderHandleInvalid;
+  }
+  // Add a new header inclusion path entry, or return existing handle.
+  // Return the header inclusion path entry handle.
+  InclusionPathHandle
+  addInclusionPathHandle(const std::vector<HeaderHandle> &Path) {
+    InclusionPathHandle H = findInclusionPathHandle(Path);
+    if (H == HeaderHandleInvalid) {
+      H = InclusionPaths.size();
+      InclusionPaths.push_back(HeaderInclusionPath(Path));
+    }
+    return H;
+  }
+  // Return the current inclusion path handle.
+  InclusionPathHandle getCurrentInclusionPathHandle() const {
+    return CurrentInclusionPathHandle;
+  }
+
+  // Return an inclusion path given its handle.
+  const std::vector<HeaderHandle> &
+  getInclusionPath(InclusionPathHandle H) const {
+    if ((H >= 0) && (H <= (InclusionPathHandle)InclusionPaths.size()))
+      return InclusionPaths[H].Path;
+    static std::vector<HeaderHandle> Empty;
+    return Empty;
+  }
+
+  // Add a macro expansion instance.
+  void addMacroExpansionInstance(clang::Preprocessor &PP, HeaderHandle H,
+                                 clang::SourceLocation InstanceLoc,
+                                 clang::SourceLocation DefinitionLoc,
+                                 clang::IdentifierInfo *II,
+                                 llvm::StringRef MacroUnexpanded,
+                                 llvm::StringRef MacroExpanded,
+                                 InclusionPathHandle InclusionPathHandle) {
+    if (InNestedHeader)
+      return;
+    StringHandle MacroName = addString(II->getName());
+    PPItemKey InstanceKey(PP, MacroName, H, InstanceLoc);
+    PPItemKey DefinitionKey(PP, MacroName, H, DefinitionLoc);
+    auto I = MacroExpansions.find(InstanceKey);
+    // If existing instance of expansion not found, add one.
+    if (I == MacroExpansions.end()) {
+      std::string InstanceSourceLine =
+          getSourceLocationString(PP, InstanceLoc) + ":\n" +
+          getSourceLine(PP, InstanceLoc) + "\n";
+      std::string DefinitionSourceLine =
+          getSourceLocationString(PP, DefinitionLoc) + ":\n" +
+          getSourceLine(PP, DefinitionLoc) + "\n";
+      MacroExpansions[InstanceKey] = MacroExpansionTracker(
+          addString(MacroUnexpanded), addString(MacroExpanded),
+          addString(InstanceSourceLine), DefinitionKey,
+          addString(DefinitionSourceLine), InclusionPathHandle);
+    } else {
+      // We've seen the macro before.  Get its tracker.
+      MacroExpansionTracker &CondTracker = I->second;
+      // Look up an existing instance value for the macro.
+      MacroExpansionInstance *MacroInfo =
+          CondTracker.findMacroExpansionInstance(addString(MacroExpanded),
+                                                 DefinitionKey);
+      // If found, just add the inclusion path to the instance.
+      if (MacroInfo)
+        MacroInfo->addInclusionPathHandle(InclusionPathHandle);
+      else {
+        // Otherwise add a new instance with the unique value.
+        std::string DefinitionSourceLine =
+            getSourceLocationString(PP, DefinitionLoc) + ":\n" +
+            getSourceLine(PP, DefinitionLoc) + "\n";
+        CondTracker.addMacroExpansionInstance(
+            addString(MacroExpanded), DefinitionKey,
+            addString(DefinitionSourceLine), InclusionPathHandle);
+      }
+    }
+  }
+
+  // Add a conditional expansion instance.
+  void
+  addConditionalExpansionInstance(clang::Preprocessor &PP, HeaderHandle H,
+                                  clang::SourceLocation InstanceLoc,
+                                  clang::tok::PPKeywordKind DirectiveKind,
+                                  clang::PPCallbacks::ConditionValueKind ConditionValue,
+                                  llvm::StringRef ConditionUnexpanded,
+                                  InclusionPathHandle InclusionPathHandle) {
+    // Ignore header guards, assuming the header guard is the only conditional.
+    if (InNestedHeader)
+      return;
+    StringHandle ConditionUnexpandedHandle(addString(ConditionUnexpanded));
+    PPItemKey InstanceKey(PP, ConditionUnexpandedHandle, H, InstanceLoc);
+    auto I = ConditionalExpansions.find(InstanceKey);
+    // If existing instance of condition not found, add one.
+    if (I == ConditionalExpansions.end()) {
+      std::string InstanceSourceLine =
+          getSourceLocationString(PP, InstanceLoc) + ":\n" +
+          getSourceLine(PP, InstanceLoc) + "\n";
+      ConditionalExpansions[InstanceKey] =
+          ConditionalTracker(DirectiveKind, ConditionValue,
+                             ConditionUnexpandedHandle, InclusionPathHandle);
+    } else {
+      // We've seen the conditional before.  Get its tracker.
+      ConditionalTracker &CondTracker = I->second;
+      // Look up an existing instance value for the condition.
+      ConditionalExpansionInstance *MacroInfo =
+          CondTracker.findConditionalExpansionInstance(ConditionValue);
+      // If found, just add the inclusion path to the instance.
+      if (MacroInfo)
+        MacroInfo->addInclusionPathHandle(InclusionPathHandle);
+      else {
+        // Otherwise add a new instance with the unique value.
+        CondTracker.addConditionalExpansionInstance(ConditionValue,
+                                                    InclusionPathHandle);
+      }
+    }
+  }
+
+  // Report on inconsistent macro instances.
+  // Returns true if any mismatches.
+  bool reportInconsistentMacros(llvm::raw_ostream &OS) override {
+    bool ReturnValue = false;
+    // Walk all the macro expansion trackers in the map.
+    for (auto I = MacroExpansions.begin(), E = MacroExpansions.end(); I != E;
+         ++I) {
+      const PPItemKey &ItemKey = I->first;
+      MacroExpansionTracker &MacroExpTracker = I->second;
+      // If no mismatch (only one instance value) continue.
+      if (!MacroExpTracker.hasMismatch())
+        continue;
+      // Tell caller we found one or more errors.
+      ReturnValue = true;
+      // Start the error message.
+      OS << *MacroExpTracker.InstanceSourceLine;
+      if (ItemKey.Column > 0)
+        OS << std::string(ItemKey.Column - 1, ' ') << "^\n";
+      OS << "error: Macro instance '" << *MacroExpTracker.MacroUnexpanded
+         << "' has different values in this header, depending on how it was "
+            "included.\n";
+      // Walk all the instances.
+      for (auto IMT = MacroExpTracker.MacroExpansionInstances.begin(),
+                EMT = MacroExpTracker.MacroExpansionInstances.end();
+           IMT != EMT; ++IMT) {
+        MacroExpansionInstance &MacroInfo = *IMT;
+        OS << "  '" << *MacroExpTracker.MacroUnexpanded << "' expanded to: '"
+           << *MacroInfo.MacroExpanded
+           << "' with respect to these inclusion paths:\n";
+        // Walk all the inclusion path hierarchies.
+        for (auto IIP = MacroInfo.InclusionPathHandles.begin(),
+                  EIP = MacroInfo.InclusionPathHandles.end();
+             IIP != EIP; ++IIP) {
+          const std::vector<HeaderHandle> &ip = getInclusionPath(*IIP);
+          auto Count = (int)ip.size();
+          for (int Index = 0; Index < Count; ++Index) {
+            HeaderHandle H = ip[Index];
+            OS << std::string((Index * 2) + 4, ' ') << *getHeaderFilePath(H)
+               << "\n";
+          }
+        }
+        // For a macro that wasn't defined, we flag it by using the
+        // instance location.
+        // If there is a definition...
+        if (MacroInfo.DefinitionLocation.Line != ItemKey.Line) {
+          OS << *MacroInfo.DefinitionSourceLine;
+          if (MacroInfo.DefinitionLocation.Column > 0)
+            OS << std::string(MacroInfo.DefinitionLocation.Column - 1, ' ')
+               << "^\n";
+          OS << "Macro defined here.\n";
+        } else
+          OS << "(no macro definition)"
+             << "\n";
+      }
+    }
+    return ReturnValue;
+  }
+
+  // Report on inconsistent conditional instances.
+  // Returns true if any mismatches.
+  bool reportInconsistentConditionals(llvm::raw_ostream &OS) override {
+    bool ReturnValue = false;
+    // Walk all the conditional trackers in the map.
+    for (auto I = ConditionalExpansions.begin(),
+              E = ConditionalExpansions.end();
+         I != E; ++I) {
+      const PPItemKey &ItemKey = I->first;
+      ConditionalTracker &CondTracker = I->second;
+      if (!CondTracker.hasMismatch())
+        continue;
+      // Tell caller we found one or more errors.
+      ReturnValue = true;
+      // Start the error message.
+      OS << *HeaderPaths[ItemKey.File] << ":" << ItemKey.Line << ":"
+         << ItemKey.Column << "\n";
+      OS << "#" << getDirectiveSpelling(CondTracker.DirectiveKind) << " "
+         << *CondTracker.ConditionUnexpanded << "\n";
+      OS << "^\n";
+      OS << "error: Conditional expression instance '"
+         << *CondTracker.ConditionUnexpanded
+         << "' has different values in this header, depending on how it was "
+            "included.\n";
+      // Walk all the instances.
+      for (auto IMT = CondTracker.ConditionalExpansionInstances.begin(),
+                EMT = CondTracker.ConditionalExpansionInstances.end();
+           IMT != EMT; ++IMT) {
+        ConditionalExpansionInstance &MacroInfo = *IMT;
+        OS << "  '" << *CondTracker.ConditionUnexpanded << "' expanded to: '"
+           << ConditionValueKindStrings[MacroInfo.ConditionValue]
+           << "' with respect to these inclusion paths:\n";
+        // Walk all the inclusion path hierarchies.
+        for (auto IIP = MacroInfo.InclusionPathHandles.begin(),
+                  EIP = MacroInfo.InclusionPathHandles.end();
+             IIP != EIP; ++IIP) {
+          const std::vector<HeaderHandle> &ip = getInclusionPath(*IIP);
+          auto Count = (int)ip.size();
+          for (int Index = 0; Index < Count; ++Index) {
+            HeaderHandle H = ip[Index];
+            OS << std::string((Index * 2) + 4, ' ') << *getHeaderFilePath(H)
+               << "\n";
+          }
+        }
+      }
+    }
+    return ReturnValue;
+  }
+
+  // Get directive spelling.
+  static const char *getDirectiveSpelling(clang::tok::PPKeywordKind kind) {
+    switch (kind) {
+    case clang::tok::pp_if:
+      return "if";
+    case clang::tok::pp_elif:
+      return "elif";
+    case clang::tok::pp_ifdef:
+      return "ifdef";
+    case clang::tok::pp_ifndef:
+      return "ifndef";
+    default:
+      return "(unknown)";
+    }
+  }
+
+private:
+  llvm::SmallVector<std::string, 32> HeaderList;
+  // Only do extern, namespace check for headers in HeaderList.
+  bool BlockCheckHeaderListOnly;
+  llvm::StringPool Strings;
+  std::vector<StringHandle> HeaderPaths;
+  std::vector<HeaderHandle> HeaderStack;
+  std::vector<HeaderInclusionPath> InclusionPaths;
+  InclusionPathHandle CurrentInclusionPathHandle;
+  llvm::SmallSet<HeaderHandle, 32> HeadersInThisCompile;
+  std::vector<PPItemKey> IncludeDirectives;
+  MacroExpansionMap MacroExpansions;
+  ConditionalExpansionMap ConditionalExpansions;
+  bool InNestedHeader;
+};
+
+} // namespace
+
+// PreprocessorTracker functions.
+
+// PreprocessorTracker desctructor.
+PreprocessorTracker::~PreprocessorTracker() {}
+
+// Create instance of PreprocessorTracker.
+PreprocessorTracker *PreprocessorTracker::create(
+    llvm::SmallVector<std::string, 32> &Headers,
+    bool DoBlockCheckHeaderListOnly) {
+  return new PreprocessorTrackerImpl(Headers, DoBlockCheckHeaderListOnly);
+}
+
+// Preprocessor callbacks for modularize.
+
+// Handle include directive.
+void PreprocessorCallbacks::InclusionDirective(
+    clang::SourceLocation HashLoc, const clang::Token &IncludeTok,
+    llvm::StringRef FileName, bool IsAngled,
+    clang::CharSourceRange FilenameRange, const clang::FileEntry *File,
+    llvm::StringRef SearchPath, llvm::StringRef RelativePath,
+    const clang::Module *Imported) {
+  int DirectiveLine, DirectiveColumn;
+  std::string HeaderPath = getSourceLocationFile(PP, HashLoc);
+  getSourceLocationLineAndColumn(PP, HashLoc, DirectiveLine, DirectiveColumn);
+  PPTracker.handleIncludeDirective(HeaderPath, DirectiveLine, DirectiveColumn,
+                                   FileName);
+}
+
+// Handle file entry/exit.
+void PreprocessorCallbacks::FileChanged(
+    clang::SourceLocation Loc, clang::PPCallbacks::FileChangeReason Reason,
+    clang::SrcMgr::CharacteristicKind FileType, clang::FileID PrevFID) {
+  switch (Reason) {
+  case EnterFile:
+    PPTracker.handleHeaderEntry(PP, getSourceLocationFile(PP, Loc));
+    break;
+  case ExitFile: {
+    const clang::FileEntry *F =
+        PP.getSourceManager().getFileEntryForID(PrevFID);
+    if (F)
+      PPTracker.handleHeaderExit(F->getName());
+  } break;
+  case SystemHeaderPragma:
+  case RenameFile:
+    break;
+  }
+}
+
+// Handle macro expansion.
+void PreprocessorCallbacks::MacroExpands(const clang::Token &MacroNameTok,
+                                         const clang::MacroDefinition &MD,
+                                         clang::SourceRange Range,
+                                         const clang::MacroArgs *Args) {
+  clang::SourceLocation Loc = Range.getBegin();
+  // Ignore macro argument expansions.
+  if (!Loc.isFileID())
+    return;
+  clang::IdentifierInfo *II = MacroNameTok.getIdentifierInfo();
+  const clang::MacroInfo *MI = MD.getMacroInfo();
+  std::string MacroName = II->getName().str();
+  std::string Unexpanded(getMacroUnexpandedString(Range, PP, MacroName, MI));
+  std::string Expanded(getMacroExpandedString(PP, MacroName, MI, Args));
+  PPTracker.addMacroExpansionInstance(
+      PP, PPTracker.getCurrentHeaderHandle(), Loc, MI->getDefinitionLoc(), II,
+      Unexpanded, Expanded, PPTracker.getCurrentInclusionPathHandle());
+}
+
+void PreprocessorCallbacks::Defined(const clang::Token &MacroNameTok,
+                                    const clang::MacroDefinition &MD,
+                                    clang::SourceRange Range) {
+  clang::SourceLocation Loc(Range.getBegin());
+  clang::IdentifierInfo *II = MacroNameTok.getIdentifierInfo();
+  const clang::MacroInfo *MI = MD.getMacroInfo();
+  std::string MacroName = II->getName().str();
+  std::string Unexpanded(getSourceString(PP, Range));
+  PPTracker.addMacroExpansionInstance(
+      PP, PPTracker.getCurrentHeaderHandle(), Loc,
+      (MI ? MI->getDefinitionLoc() : Loc), II, Unexpanded,
+      (MI ? "true" : "false"), PPTracker.getCurrentInclusionPathHandle());
+}
+
+void PreprocessorCallbacks::If(clang::SourceLocation Loc,
+                               clang::SourceRange ConditionRange,
+                               clang::PPCallbacks::ConditionValueKind ConditionResult) {
+  std::string Unexpanded(getSourceString(PP, ConditionRange));
+  PPTracker.addConditionalExpansionInstance(
+      PP, PPTracker.getCurrentHeaderHandle(), Loc, clang::tok::pp_if,
+      ConditionResult, Unexpanded, PPTracker.getCurrentInclusionPathHandle());
+}
+
+void PreprocessorCallbacks::Elif(clang::SourceLocation Loc,
+                                 clang::SourceRange ConditionRange,
+                                 clang::PPCallbacks::ConditionValueKind ConditionResult,
+                                 clang::SourceLocation IfLoc) {
+  std::string Unexpanded(getSourceString(PP, ConditionRange));
+  PPTracker.addConditionalExpansionInstance(
+      PP, PPTracker.getCurrentHeaderHandle(), Loc, clang::tok::pp_elif,
+      ConditionResult, Unexpanded, PPTracker.getCurrentInclusionPathHandle());
+}
+
+void PreprocessorCallbacks::Ifdef(clang::SourceLocation Loc,
+                                  const clang::Token &MacroNameTok,
+                                  const clang::MacroDefinition &MD) {
+  clang::PPCallbacks::ConditionValueKind IsDefined =
+    (MD ? clang::PPCallbacks::CVK_True : clang::PPCallbacks::CVK_False );
+  PPTracker.addConditionalExpansionInstance(
+      PP, PPTracker.getCurrentHeaderHandle(), Loc, clang::tok::pp_ifdef,
+      IsDefined, PP.getSpelling(MacroNameTok),
+      PPTracker.getCurrentInclusionPathHandle());
+}
+
+void PreprocessorCallbacks::Ifndef(clang::SourceLocation Loc,
+                                   const clang::Token &MacroNameTok,
+                                   const clang::MacroDefinition &MD) {
+  clang::PPCallbacks::ConditionValueKind IsNotDefined =
+    (!MD ? clang::PPCallbacks::CVK_True : clang::PPCallbacks::CVK_False );
+  PPTracker.addConditionalExpansionInstance(
+      PP, PPTracker.getCurrentHeaderHandle(), Loc, clang::tok::pp_ifndef,
+      IsNotDefined, PP.getSpelling(MacroNameTok),
+      PPTracker.getCurrentInclusionPathHandle());
+}
+} // end namespace Modularize
diff --git a/modularize/PreprocessorTracker.h b/modularize/PreprocessorTracker.h
new file mode 100644 (file)
index 0000000..a283d9f
--- /dev/null
@@ -0,0 +1,87 @@
+//===- PreprocessorTracker.h - Tracks preprocessor activities -*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===--------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Macro expansions and preprocessor conditional consistency checker.
+///
+//===--------------------------------------------------------------------===//
+
+#ifndef MODULARIZE_PREPROCESSOR_TRACKER_H
+#define MODULARIZE_PREPROCESSOR_TRACKER_H
+
+#include "clang/Lex/Preprocessor.h"
+
+namespace Modularize {
+
+/// \brief Preprocessor tracker for modularize.
+///
+/// The PreprocessorTracker class defines an API for
+/// checking macro expansions and preprocessor conditional expressions
+/// in a header file for consistency among one or more compilations of
+/// the header in a #include scenario.  This is for helping a user
+/// find which macro expansions or conditionals might be problematic with
+/// respect to using the headers in the modules scenario, because they
+/// evaluate to different values depending on how or where a header
+/// is included.
+///
+/// The handlePreprocessorEntry function implementation will register
+/// a PPCallbacks object in the given Preprocessor object.  The calls to
+/// the callbacks will collect information about the macro expansions
+/// and preprocessor conditionals encountered, for later analysis and
+/// reporting of inconsistencies between runs performed by calls to
+/// the reportInconsistentMacros and reportInconsistentConditionals
+/// functions respectively.  The handlePreprocessorExit informs the
+/// implementation that a preprocessing session is complete, allowing
+/// it to do any needed compilation completion activities in the checker.
+class PreprocessorTracker {
+public:
+  virtual ~PreprocessorTracker();
+
+  // Handle entering a preprocessing session.
+  // (Called after a Preprocessor object is created, but before preprocessing.)
+  virtual void handlePreprocessorEntry(clang::Preprocessor &PP,
+                                       llvm::StringRef RootHeaderFile) = 0;
+  // Handle exiting a preprocessing session.
+  // (Called after preprocessing is complete, but before the Preprocessor
+  // object is destroyed.)
+  virtual void handlePreprocessorExit() = 0;
+
+  // Handle include directive.
+  // This function is called every time an include directive is seen by the
+  // preprocessor, for the purpose of later checking for 'extern "" {}' or
+  // "namespace {}" blocks containing #include directives.
+  virtual void handleIncludeDirective(llvm::StringRef DirectivePath,
+                                      int DirectiveLine, int DirectiveColumn,
+                                      llvm::StringRef TargetPath) = 0;
+
+  // Check for include directives within the given source line range.
+  // Report errors if any found.  Returns true if no include directives
+  // found in block.
+  virtual bool checkForIncludesInBlock(clang::Preprocessor &PP,
+                                       clang::SourceRange BlockSourceRange,
+                                       const char *BlockIdentifierMessage,
+                                       llvm::raw_ostream &OS) = 0;
+
+  // Report on inconsistent macro instances.
+  // Returns true if any mismatches.
+  virtual bool reportInconsistentMacros(llvm::raw_ostream &OS) = 0;
+
+  // Report on inconsistent conditional directive instances.
+  // Returns true if any mismatches.
+  virtual bool reportInconsistentConditionals(llvm::raw_ostream &OS) = 0;
+
+  // Create instance of PreprocessorTracker.
+  static PreprocessorTracker *create(
+    llvm::SmallVector<std::string, 32> &Headers,
+    bool DoBlockCheckHeaderListOnly);
+};
+
+} // end namespace Modularize
+
+#endif
diff --git a/pp-trace/CMakeLists.txt b/pp-trace/CMakeLists.txt
new file mode 100644 (file)
index 0000000..95a1f10
--- /dev/null
@@ -0,0 +1,16 @@
+set(LLVM_LINK_COMPONENTS
+  Support
+  )
+
+add_clang_executable(pp-trace
+  PPTrace.cpp
+  PPCallbacksTracker.cpp
+  )
+
+target_link_libraries(pp-trace
+  clangAST
+  clangBasic
+  clangFrontend
+  clangLex
+  clangTooling
+  )
diff --git a/pp-trace/PPCallbacksTracker.cpp b/pp-trace/PPCallbacksTracker.cpp
new file mode 100644 (file)
index 0000000..ca634d2
--- /dev/null
@@ -0,0 +1,653 @@
+//===--- PPCallbacksTracker.cpp - Preprocessor tracker -*--*---------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Implementations for preprocessor tracking.
+///
+/// See the header for details.
+///
+//===----------------------------------------------------------------------===//
+
+#include "PPCallbacksTracker.h"
+#include "clang/Lex/MacroArgs.h"
+#include "llvm/Support/raw_ostream.h"
+
+// Utility functions.
+
+// Get a "file:line:column" source location string.
+static std::string getSourceLocationString(clang::Preprocessor &PP,
+                                           clang::SourceLocation Loc) {
+  if (Loc.isInvalid())
+    return std::string("(none)");
+
+  if (Loc.isFileID()) {
+    clang::PresumedLoc PLoc = PP.getSourceManager().getPresumedLoc(Loc);
+
+    if (PLoc.isInvalid()) {
+      return std::string("(invalid)");
+    }
+
+    std::string Str;
+    llvm::raw_string_ostream SS(Str);
+
+    // The macro expansion and spelling pos is identical for file locs.
+    SS << "\"" << PLoc.getFilename() << ':' << PLoc.getLine() << ':'
+       << PLoc.getColumn() << "\"";
+
+    std::string Result = SS.str();
+
+    // YAML treats backslash as escape, so use forward slashes.
+    std::replace(Result.begin(), Result.end(), '\\', '/');
+
+    return Result;
+  }
+
+  return std::string("(nonfile)");
+}
+
+// Enum string tables.
+
+// FileChangeReason strings.
+static const char *const FileChangeReasonStrings[] = {
+  "EnterFile", "ExitFile", "SystemHeaderPragma", "RenameFile"
+};
+
+// CharacteristicKind strings.
+static const char *const CharacteristicKindStrings[] = { "C_User", "C_System",
+                                                         "C_ExternCSystem" };
+
+// MacroDirective::Kind strings.
+static const char *const MacroDirectiveKindStrings[] = {
+  "MD_Define","MD_Undefine", "MD_Visibility"
+};
+
+// PragmaIntroducerKind strings.
+static const char *const PragmaIntroducerKindStrings[] = { "PIK_HashPragma",
+                                                           "PIK__Pragma",
+                                                           "PIK___pragma" };
+
+// PragmaMessageKind strings.
+static const char *const PragmaMessageKindStrings[] = {
+  "PMK_Message", "PMK_Warning", "PMK_Error"
+};
+
+// ConditionValueKind strings.
+static const char *const ConditionValueKindStrings[] = {
+  "CVK_NotEvaluated", "CVK_False", "CVK_True"
+};
+
+// Mapping strings.
+static const char *const MappingStrings[] = { "0",          "MAP_IGNORE",
+                                              "MAP_REMARK", "MAP_WARNING",
+                                              "MAP_ERROR",  "MAP_FATAL" };
+
+// PPCallbacksTracker functions.
+
+PPCallbacksTracker::PPCallbacksTracker(llvm::SmallSet<std::string, 4> &Ignore,
+                                       std::vector<CallbackCall> &CallbackCalls,
+                                       clang::Preprocessor &PP)
+    : CallbackCalls(CallbackCalls), Ignore(Ignore), PP(PP) {}
+
+PPCallbacksTracker::~PPCallbacksTracker() {}
+
+// Callback functions.
+
+// Callback invoked whenever a source file is entered or exited.
+void PPCallbacksTracker::FileChanged(
+    clang::SourceLocation Loc, clang::PPCallbacks::FileChangeReason Reason,
+    clang::SrcMgr::CharacteristicKind FileType, clang::FileID PrevFID) {
+  beginCallback("FileChanged");
+  appendArgument("Loc", Loc);
+  appendArgument("Reason", Reason, FileChangeReasonStrings);
+  appendArgument("FileType", FileType, CharacteristicKindStrings);
+  appendArgument("PrevFID", PrevFID);
+}
+
+// Callback invoked whenever a source file is skipped as the result
+// of header guard optimization.
+void
+PPCallbacksTracker::FileSkipped(const clang::FileEntry &SkippedFile,
+                                const clang::Token &FilenameTok,
+                                clang::SrcMgr::CharacteristicKind FileType) {
+  beginCallback("FileSkipped");
+  appendArgument("ParentFile", &SkippedFile);
+  appendArgument("FilenameTok", FilenameTok);
+  appendArgument("FileType", FileType, CharacteristicKindStrings);
+}
+
+// Callback invoked whenever an inclusion directive results in a
+// file-not-found error.
+bool
+PPCallbacksTracker::FileNotFound(llvm::StringRef FileName,
+                                 llvm::SmallVectorImpl<char> &RecoveryPath) {
+  beginCallback("FileNotFound");
+  appendFilePathArgument("FileName", FileName);
+  return false;
+}
+
+// Callback invoked whenever an inclusion directive of
+// any kind (#include, #import, etc.) has been processed, regardless
+// of whether the inclusion will actually result in an inclusion.
+void PPCallbacksTracker::InclusionDirective(
+    clang::SourceLocation HashLoc, const clang::Token &IncludeTok,
+    llvm::StringRef FileName, bool IsAngled,
+    clang::CharSourceRange FilenameRange, const clang::FileEntry *File,
+    llvm::StringRef SearchPath, llvm::StringRef RelativePath,
+    const clang::Module *Imported) {
+  beginCallback("InclusionDirective");
+  appendArgument("IncludeTok", IncludeTok);
+  appendFilePathArgument("FileName", FileName);
+  appendArgument("IsAngled", IsAngled);
+  appendArgument("FilenameRange", FilenameRange);
+  appendArgument("File", File);
+  appendFilePathArgument("SearchPath", SearchPath);
+  appendFilePathArgument("RelativePath", RelativePath);
+  appendArgument("Imported", Imported);
+}
+
+// Callback invoked whenever there was an explicit module-import
+// syntax.
+void PPCallbacksTracker::moduleImport(clang::SourceLocation ImportLoc,
+                                      clang::ModuleIdPath Path,
+                                      const clang::Module *Imported) {
+  beginCallback("moduleImport");
+  appendArgument("ImportLoc", ImportLoc);
+  appendArgument("Path", Path);
+  appendArgument("Imported", Imported);
+}
+
+// Callback invoked when the end of the main file is reached.
+// No subsequent callbacks will be made.
+void PPCallbacksTracker::EndOfMainFile() { beginCallback("EndOfMainFile"); }
+
+// Callback invoked when a #ident or #sccs directive is read.
+void PPCallbacksTracker::Ident(clang::SourceLocation Loc, llvm::StringRef Str) {
+  beginCallback("Ident");
+  appendArgument("Loc", Loc);
+  appendArgument("Str", Str);
+}
+
+// Callback invoked when start reading any pragma directive.
+void
+PPCallbacksTracker::PragmaDirective(clang::SourceLocation Loc,
+                                    clang::PragmaIntroducerKind Introducer) {
+  beginCallback("PragmaDirective");
+  appendArgument("Loc", Loc);
+  appendArgument("Introducer", Introducer, PragmaIntroducerKindStrings);
+}
+
+// Callback invoked when a #pragma comment directive is read.
+void PPCallbacksTracker::PragmaComment(clang::SourceLocation Loc,
+                                       const clang::IdentifierInfo *Kind,
+                                       llvm::StringRef Str) {
+  beginCallback("PragmaComment");
+  appendArgument("Loc", Loc);
+  appendArgument("Kind", Kind);
+  appendArgument("Str", Str);
+}
+
+// Callback invoked when a #pragma detect_mismatch directive is
+// read.
+void PPCallbacksTracker::PragmaDetectMismatch(clang::SourceLocation Loc,
+                                              llvm::StringRef Name,
+                                              llvm::StringRef Value) {
+  beginCallback("PragmaDetectMismatch");
+  appendArgument("Loc", Loc);
+  appendArgument("Name", Name);
+  appendArgument("Value", Value);
+}
+
+// Callback invoked when a #pragma clang __debug directive is read.
+void PPCallbacksTracker::PragmaDebug(clang::SourceLocation Loc,
+                                     llvm::StringRef DebugType) {
+  beginCallback("PragmaDebug");
+  appendArgument("Loc", Loc);
+  appendArgument("DebugType", DebugType);
+}
+
+// Callback invoked when a #pragma message directive is read.
+void PPCallbacksTracker::PragmaMessage(
+    clang::SourceLocation Loc, llvm::StringRef Namespace,
+    clang::PPCallbacks::PragmaMessageKind Kind, llvm::StringRef Str) {
+  beginCallback("PragmaMessage");
+  appendArgument("Loc", Loc);
+  appendArgument("Namespace", Namespace);
+  appendArgument("Kind", Kind, PragmaMessageKindStrings);
+  appendArgument("Str", Str);
+}
+
+// Callback invoked when a #pragma gcc dianostic push directive
+// is read.
+void PPCallbacksTracker::PragmaDiagnosticPush(clang::SourceLocation Loc,
+                                              llvm::StringRef Namespace) {
+  beginCallback("PragmaDiagnosticPush");
+  appendArgument("Loc", Loc);
+  appendArgument("Namespace", Namespace);
+}
+
+// Callback invoked when a #pragma gcc dianostic pop directive
+// is read.
+void PPCallbacksTracker::PragmaDiagnosticPop(clang::SourceLocation Loc,
+                                             llvm::StringRef Namespace) {
+  beginCallback("PragmaDiagnosticPop");
+  appendArgument("Loc", Loc);
+  appendArgument("Namespace", Namespace);
+}
+
+// Callback invoked when a #pragma gcc dianostic directive is read.
+void PPCallbacksTracker::PragmaDiagnostic(clang::SourceLocation Loc,
+                                          llvm::StringRef Namespace,
+                                          clang::diag::Severity Mapping,
+                                          llvm::StringRef Str) {
+  beginCallback("PragmaDiagnostic");
+  appendArgument("Loc", Loc);
+  appendArgument("Namespace", Namespace);
+  appendArgument("Mapping", (unsigned)Mapping, MappingStrings);
+  appendArgument("Str", Str);
+}
+
+// Called when an OpenCL extension is either disabled or
+// enabled with a pragma.
+void PPCallbacksTracker::PragmaOpenCLExtension(
+    clang::SourceLocation NameLoc, const clang::IdentifierInfo *Name,
+    clang::SourceLocation StateLoc, unsigned State) {
+  beginCallback("PragmaOpenCLExtension");
+  appendArgument("NameLoc", NameLoc);
+  appendArgument("Name", Name);
+  appendArgument("StateLoc", StateLoc);
+  appendArgument("State", (int)State);
+}
+
+// Callback invoked when a #pragma warning directive is read.
+void PPCallbacksTracker::PragmaWarning(clang::SourceLocation Loc,
+                                       llvm::StringRef WarningSpec,
+                                       llvm::ArrayRef<int> Ids) {
+  beginCallback("PragmaWarning");
+  appendArgument("Loc", Loc);
+  appendArgument("WarningSpec", WarningSpec);
+
+  std::string Str;
+  llvm::raw_string_ostream SS(Str);
+  SS << "[";
+  for (int i = 0, e = Ids.size(); i != e; ++i) {
+    if (i)
+      SS << ", ";
+    SS << Ids[i];
+  }
+  SS << "]";
+  appendArgument("Ids", SS.str());
+}
+
+// Callback invoked when a #pragma warning(push) directive is read.
+void PPCallbacksTracker::PragmaWarningPush(clang::SourceLocation Loc,
+                                           int Level) {
+  beginCallback("PragmaWarningPush");
+  appendArgument("Loc", Loc);
+  appendArgument("Level", Level);
+}
+
+// Callback invoked when a #pragma warning(pop) directive is read.
+void PPCallbacksTracker::PragmaWarningPop(clang::SourceLocation Loc) {
+  beginCallback("PragmaWarningPop");
+  appendArgument("Loc", Loc);
+}
+
+// Called by Preprocessor::HandleMacroExpandedIdentifier when a
+// macro invocation is found.
+void
+PPCallbacksTracker::MacroExpands(const clang::Token &MacroNameTok,
+                                 const clang::MacroDefinition &MacroDefinition,
+                                 clang::SourceRange Range,
+                                 const clang::MacroArgs *Args) {
+  beginCallback("MacroExpands");
+  appendArgument("MacroNameTok", MacroNameTok);
+  appendArgument("MacroDefinition", MacroDefinition);
+  appendArgument("Range", Range);
+  appendArgument("Args", Args);
+}
+
+// Hook called whenever a macro definition is seen.
+void
+PPCallbacksTracker::MacroDefined(const clang::Token &MacroNameTok,
+                                 const clang::MacroDirective *MacroDirective) {
+  beginCallback("MacroDefined");
+  appendArgument("MacroNameTok", MacroNameTok);
+  appendArgument("MacroDirective", MacroDirective);
+}
+
+// Hook called whenever a macro #undef is seen.
+void PPCallbacksTracker::MacroUndefined(
+    const clang::Token &MacroNameTok,
+    const clang::MacroDefinition &MacroDefinition,
+    const clang::MacroDirective *Undef) {
+  beginCallback("MacroUndefined");
+  appendArgument("MacroNameTok", MacroNameTok);
+  appendArgument("MacroDefinition", MacroDefinition);
+}
+
+// Hook called whenever the 'defined' operator is seen.
+void PPCallbacksTracker::Defined(const clang::Token &MacroNameTok,
+                                 const clang::MacroDefinition &MacroDefinition,
+                                 clang::SourceRange Range) {
+  beginCallback("Defined");
+  appendArgument("MacroNameTok", MacroNameTok);
+  appendArgument("MacroDefinition", MacroDefinition);
+  appendArgument("Range", Range);
+}
+
+// Hook called when a source range is skipped.
+void PPCallbacksTracker::SourceRangeSkipped(clang::SourceRange Range) {
+  beginCallback("SourceRangeSkipped");
+  appendArgument("Range", Range);
+}
+
+// Hook called whenever an #if is seen.
+void PPCallbacksTracker::If(clang::SourceLocation Loc,
+                            clang::SourceRange ConditionRange,
+                            ConditionValueKind ConditionValue) {
+  beginCallback("If");
+  appendArgument("Loc", Loc);
+  appendArgument("ConditionRange", ConditionRange);
+  appendArgument("ConditionValue", ConditionValue, ConditionValueKindStrings);
+}
+
+// Hook called whenever an #elif is seen.
+void PPCallbacksTracker::Elif(clang::SourceLocation Loc,
+                              clang::SourceRange ConditionRange,
+                              ConditionValueKind ConditionValue,
+                              clang::SourceLocation IfLoc) {
+  beginCallback("Elif");
+  appendArgument("Loc", Loc);
+  appendArgument("ConditionRange", ConditionRange);
+  appendArgument("ConditionValue", ConditionValue, ConditionValueKindStrings);
+  appendArgument("IfLoc", IfLoc);
+}
+
+// Hook called whenever an #ifdef is seen.
+void PPCallbacksTracker::Ifdef(clang::SourceLocation Loc,
+                               const clang::Token &MacroNameTok,
+                               const clang::MacroDefinition &MacroDefinition) {
+  beginCallback("Ifdef");
+  appendArgument("Loc", Loc);
+  appendArgument("MacroNameTok", MacroNameTok);
+  appendArgument("MacroDefinition", MacroDefinition);
+}
+
+// Hook called whenever an #ifndef is seen.
+void PPCallbacksTracker::Ifndef(clang::SourceLocation Loc,
+                                const clang::Token &MacroNameTok,
+                                const clang::MacroDefinition &MacroDefinition) {
+  beginCallback("Ifndef");
+  appendArgument("Loc", Loc);
+  appendArgument("MacroNameTok", MacroNameTok);
+  appendArgument("MacroDefinition", MacroDefinition);
+}
+
+// Hook called whenever an #else is seen.
+void PPCallbacksTracker::Else(clang::SourceLocation Loc,
+                              clang::SourceLocation IfLoc) {
+  beginCallback("Else");
+  appendArgument("Loc", Loc);
+  appendArgument("IfLoc", IfLoc);
+}
+
+// Hook called whenever an #endif is seen.
+void PPCallbacksTracker::Endif(clang::SourceLocation Loc,
+                               clang::SourceLocation IfLoc) {
+  beginCallback("Endif");
+  appendArgument("Loc", Loc);
+  appendArgument("IfLoc", IfLoc);
+}
+
+// Helper functions.
+
+// Start a new callback.
+void PPCallbacksTracker::beginCallback(const char *Name) {
+  DisableTrace = Ignore.count(std::string(Name));
+  if (DisableTrace)
+    return;
+  CallbackCalls.push_back(CallbackCall(Name));
+}
+
+// Append a bool argument to the top trace item.
+void PPCallbacksTracker::appendArgument(const char *Name, bool Value) {
+  appendArgument(Name, (Value ? "true" : "false"));
+}
+
+// Append an int argument to the top trace item.
+void PPCallbacksTracker::appendArgument(const char *Name, int Value) {
+  std::string Str;
+  llvm::raw_string_ostream SS(Str);
+  SS << Value;
+  appendArgument(Name, SS.str());
+}
+
+// Append a string argument to the top trace item.
+void PPCallbacksTracker::appendArgument(const char *Name, const char *Value) {
+  if (DisableTrace)
+    return;
+  CallbackCalls.back().Arguments.push_back(Argument(Name, Value));
+}
+
+// Append a string object argument to the top trace item.
+void PPCallbacksTracker::appendArgument(const char *Name,
+                                        llvm::StringRef Value) {
+  appendArgument(Name, Value.str());
+}
+
+// Append a string object argument to the top trace item.
+void PPCallbacksTracker::appendArgument(const char *Name,
+                                        const std::string &Value) {
+  appendArgument(Name, Value.c_str());
+}
+
+// Append a token argument to the top trace item.
+void PPCallbacksTracker::appendArgument(const char *Name,
+                                        const clang::Token &Value) {
+  appendArgument(Name, PP.getSpelling(Value));
+}
+
+// Append an enum argument to the top trace item.
+void PPCallbacksTracker::appendArgument(const char *Name, int Value,
+                                        const char *const Strings[]) {
+  appendArgument(Name, Strings[Value]);
+}
+
+// Append a FileID argument to the top trace item.
+void PPCallbacksTracker::appendArgument(const char *Name, clang::FileID Value) {
+  if (Value.isInvalid()) {
+    appendArgument(Name, "(invalid)");
+    return;
+  }
+  const clang::FileEntry *FileEntry =
+      PP.getSourceManager().getFileEntryForID(Value);
+  if (!FileEntry) {
+    appendArgument(Name, "(getFileEntryForID failed)");
+    return;
+  }
+  appendFilePathArgument(Name, FileEntry->getName());
+}
+
+// Append a FileEntry argument to the top trace item.
+void PPCallbacksTracker::appendArgument(const char *Name,
+                                        const clang::FileEntry *Value) {
+  if (!Value) {
+    appendArgument(Name, "(null)");
+    return;
+  }
+  appendFilePathArgument(Name, Value->getName());
+}
+
+// Append a SourceLocation argument to the top trace item.
+void PPCallbacksTracker::appendArgument(const char *Name,
+                                        clang::SourceLocation Value) {
+  if (Value.isInvalid()) {
+    appendArgument(Name, "(invalid)");
+    return;
+  }
+  appendArgument(Name, getSourceLocationString(PP, Value).c_str());
+}
+
+// Append a SourceRange argument to the top trace item.
+void PPCallbacksTracker::appendArgument(const char *Name,
+                                        clang::SourceRange Value) {
+  if (DisableTrace)
+    return;
+  if (Value.isInvalid()) {
+    appendArgument(Name, "(invalid)");
+    return;
+  }
+  std::string Str;
+  llvm::raw_string_ostream SS(Str);
+  SS << "[" << getSourceLocationString(PP, Value.getBegin()) << ", "
+     << getSourceLocationString(PP, Value.getEnd()) << "]";
+  appendArgument(Name, SS.str());
+}
+
+// Append a CharSourceRange argument to the top trace item.
+void PPCallbacksTracker::appendArgument(const char *Name,
+                                        clang::CharSourceRange Value) {
+  if (Value.isInvalid()) {
+    appendArgument(Name, "(invalid)");
+    return;
+  }
+  appendArgument(Name, getSourceString(Value).str().c_str());
+}
+
+// Append a SourceLocation argument to the top trace item.
+void PPCallbacksTracker::appendArgument(const char *Name,
+                                        clang::ModuleIdPath Value) {
+  if (DisableTrace)
+    return;
+  std::string Str;
+  llvm::raw_string_ostream SS(Str);
+  SS << "[";
+  for (int I = 0, E = Value.size(); I != E; ++I) {
+    if (I)
+      SS << ", ";
+    SS << "{"
+       << "Name: " << Value[I].first->getName() << ", "
+       << "Loc: " << getSourceLocationString(PP, Value[I].second) << "}";
+  }
+  SS << "]";
+  appendArgument(Name, SS.str());
+}
+
+// Append an IdentifierInfo argument to the top trace item.
+void PPCallbacksTracker::appendArgument(const char *Name,
+                                        const clang::IdentifierInfo *Value) {
+  if (!Value) {
+    appendArgument(Name, "(null)");
+    return;
+  }
+  appendArgument(Name, Value->getName().str().c_str());
+}
+
+// Append a MacroDirective argument to the top trace item.
+void PPCallbacksTracker::appendArgument(const char *Name,
+                                        const clang::MacroDirective *Value) {
+  if (!Value) {
+    appendArgument(Name, "(null)");
+    return;
+  }
+  appendArgument(Name, MacroDirectiveKindStrings[Value->getKind()]);
+}
+
+// Append a MacroDefinition argument to the top trace item.
+void PPCallbacksTracker::appendArgument(const char *Name,
+                                        const clang::MacroDefinition &Value) {
+  std::string Str;
+  llvm::raw_string_ostream SS(Str);
+  SS << "[";
+  bool Any = false;
+  if (Value.getLocalDirective()) {
+    SS << "(local)";
+    Any = true;
+  }
+  for (auto *MM : Value.getModuleMacros()) {
+    if (Any) SS << ", ";
+    SS << MM->getOwningModule()->getFullModuleName();
+  }
+  SS << "]";
+  appendArgument(Name, SS.str());
+}
+
+// Append a MacroArgs argument to the top trace item.
+void PPCallbacksTracker::appendArgument(const char *Name,
+                                        const clang::MacroArgs *Value) {
+  if (!Value) {
+    appendArgument(Name, "(null)");
+    return;
+  }
+  std::string Str;
+  llvm::raw_string_ostream SS(Str);
+  SS << "[";
+
+  // Each argument is is a series of contiguous Tokens, terminated by a eof.
+  // Go through each argument printing tokens until we reach eof.
+  for (unsigned I = 0; I < Value->getNumMacroArguments(); ++I) {
+    const clang::Token *Current = Value->getUnexpArgument(I);
+    if (I)
+      SS << ", ";
+    bool First = true;
+    while (Current->isNot(clang::tok::eof)) {
+      if (!First)
+        SS << " ";
+      // We need to be careful here because the arguments might not be legal in
+      // YAML, so we use the token name for anything but identifiers and
+      // numeric literals.
+      if (Current->isAnyIdentifier() ||
+          Current->is(clang::tok::numeric_constant)) {
+        SS << PP.getSpelling(*Current);
+      } else {
+        SS << "<" << Current->getName() << ">";
+      }
+      ++Current;
+      First = false;
+    }
+  }
+  SS << "]";
+  appendArgument(Name, SS.str());
+}
+
+// Append a Module argument to the top trace item.
+void PPCallbacksTracker::appendArgument(const char *Name,
+                                        const clang::Module *Value) {
+  if (!Value) {
+    appendArgument(Name, "(null)");
+    return;
+  }
+  appendArgument(Name, Value->Name.c_str());
+}
+
+// Append a double-quoted argument to the top trace item.
+void PPCallbacksTracker::appendQuotedArgument(const char *Name,
+                                              const std::string &Value) {
+  std::string Str;
+  llvm::raw_string_ostream SS(Str);
+  SS << "\"" << Value << "\"";
+  appendArgument(Name, SS.str());
+}
+
+// Append a double-quoted file path argument to the top trace item.
+void PPCallbacksTracker::appendFilePathArgument(const char *Name,
+                                                llvm::StringRef Value) {
+  std::string Path(Value);
+  // YAML treats backslash as escape, so use forward slashes.
+  std::replace(Path.begin(), Path.end(), '\\', '/');
+  appendQuotedArgument(Name, Path);
+}
+
+// Get the raw source string of the range.
+llvm::StringRef
+PPCallbacksTracker::getSourceString(clang::CharSourceRange Range) {
+  const char *B = PP.getSourceManager().getCharacterData(Range.getBegin());
+  const char *E = PP.getSourceManager().getCharacterData(Range.getEnd());
+  return llvm::StringRef(B, E - B);
+}
diff --git a/pp-trace/PPCallbacksTracker.h b/pp-trace/PPCallbacksTracker.h
new file mode 100644 (file)
index 0000000..65c7fa1
--- /dev/null
@@ -0,0 +1,247 @@
+//===--- PPCallbacksTracker.h - Preprocessor tracking -----------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Classes and definitions for preprocessor tracking.
+///
+/// The core definition is the PPCallbacksTracker class, derived from Clang's
+/// PPCallbacks class from the Lex library, which overrides all the callbacks
+/// and collects information about each callback call, saving it in a
+/// data structure built up of CallbackCall and Argument objects, which
+/// record the preprocessor callback name and arguments in high-level string
+/// form for later inspection.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef PPTRACE_PPCALLBACKSTRACKER_H
+#define PPTRACE_PPCALLBACKSTRACKER_H
+
+#include "clang/Lex/PPCallbacks.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Basic/SourceManager.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/SmallSet.h"
+#include "llvm/ADT/StringRef.h"
+#include <string>
+#include <vector>
+
+/// \brief This class represents one callback function argument by name
+///   and value.
+class Argument {
+public:
+  Argument(llvm::StringRef Name, llvm::StringRef Value)
+      : Name(Name), Value(Value) {}
+  Argument() = default;
+
+  std::string Name;
+  std::string Value;
+};
+
+/// \brief This class represents one callback call by name and an array
+///   of arguments.
+class CallbackCall {
+public:
+  CallbackCall(llvm::StringRef Name) : Name(Name) {}
+  CallbackCall() = default;
+
+  std::string Name;
+  std::vector<Argument> Arguments;
+};
+
+/// \brief This class overrides the PPCallbacks class for tracking preprocessor
+///   activity by means of its callback functions.
+///
+/// This object is given a vector for storing the trace information, built up
+/// of CallbackCall and subordinate Argument objects for representing the
+/// callback calls and their arguments.  It's a reference so the vector can
+/// exist beyond the lifetime of this object, because it's deleted by the
+/// preprocessor automatically in its destructor.
+///
+/// This class supports a mechanism for inhibiting trace output for
+/// specific callbacks by name, for the purpose of eliminating output for
+/// callbacks of no interest that might clutter the output.
+///
+/// Following the constructor and destructor function declarations, the
+/// overidden callback functions are defined.  The remaining functions are
+/// helpers for recording the trace data, to reduce the coupling between it
+/// and the recorded data structure.
+class PPCallbacksTracker : public clang::PPCallbacks {
+public:
+  /// \brief Note that all of the arguments are references, and owned
+  /// by the caller.
+  /// \param Ignore - Set of names of callbacks to ignore.
+  /// \param CallbackCalls - Trace buffer.
+  /// \param PP - The preprocessor.  Needed for getting some argument strings.
+  PPCallbacksTracker(llvm::SmallSet<std::string, 4> &Ignore,
+                     std::vector<CallbackCall> &CallbackCalls,
+                     clang::Preprocessor &PP);
+
+  ~PPCallbacksTracker() override;
+
+  // Overidden callback functions.
+
+  void FileChanged(clang::SourceLocation Loc,
+                   clang::PPCallbacks::FileChangeReason Reason,
+                   clang::SrcMgr::CharacteristicKind FileType,
+                   clang::FileID PrevFID = clang::FileID()) override;
+  void FileSkipped(const clang::FileEntry &SkippedFile,
+                   const clang::Token &FilenameTok,
+                   clang::SrcMgr::CharacteristicKind FileType) override;
+  bool FileNotFound(llvm::StringRef FileName,
+                    llvm::SmallVectorImpl<char> &RecoveryPath) override;
+  void InclusionDirective(clang::SourceLocation HashLoc,
+                          const clang::Token &IncludeTok,
+                          llvm::StringRef FileName, bool IsAngled,
+                          clang::CharSourceRange FilenameRange,
+                          const clang::FileEntry *File,
+                          llvm::StringRef SearchPath,
+                          llvm::StringRef RelativePath,
+                          const clang::Module *Imported) override;
+  void moduleImport(clang::SourceLocation ImportLoc, clang::ModuleIdPath Path,
+                    const clang::Module *Imported) override;
+  void EndOfMainFile() override;
+  void Ident(clang::SourceLocation Loc, llvm::StringRef str) override;
+  void PragmaDirective(clang::SourceLocation Loc,
+                       clang::PragmaIntroducerKind Introducer) override;
+  void PragmaComment(clang::SourceLocation Loc,
+                     const clang::IdentifierInfo *Kind,
+                     llvm::StringRef Str) override;
+  void PragmaDetectMismatch(clang::SourceLocation Loc, llvm::StringRef Name,
+                            llvm::StringRef Value) override;
+  void PragmaDebug(clang::SourceLocation Loc,
+                   llvm::StringRef DebugType) override;
+  void PragmaMessage(clang::SourceLocation Loc, llvm::StringRef Namespace,
+                     clang::PPCallbacks::PragmaMessageKind Kind,
+                     llvm::StringRef Str) override;
+  void PragmaDiagnosticPush(clang::SourceLocation Loc,
+                            llvm::StringRef Namespace) override;
+  void PragmaDiagnosticPop(clang::SourceLocation Loc,
+                           llvm::StringRef Namespace) override;
+  void PragmaDiagnostic(clang::SourceLocation Loc, llvm::StringRef Namespace,
+                        clang::diag::Severity mapping,
+                        llvm::StringRef Str) override;
+  void PragmaOpenCLExtension(clang::SourceLocation NameLoc,
+                             const clang::IdentifierInfo *Name,
+                             clang::SourceLocation StateLoc,
+                             unsigned State) override;
+  void PragmaWarning(clang::SourceLocation Loc, llvm::StringRef WarningSpec,
+                     llvm::ArrayRef<int> Ids) override;
+  void PragmaWarningPush(clang::SourceLocation Loc, int Level) override;
+  void PragmaWarningPop(clang::SourceLocation Loc) override;
+  void MacroExpands(const clang::Token &MacroNameTok,
+                    const clang::MacroDefinition &MD, clang::SourceRange Range,
+                    const clang::MacroArgs *Args) override;
+  void MacroDefined(const clang::Token &MacroNameTok,
+                    const clang::MacroDirective *MD) override;
+  void MacroUndefined(const clang::Token &MacroNameTok,
+                      const clang::MacroDefinition &MD,
+                      const clang::MacroDirective *Undef) override;
+  void Defined(const clang::Token &MacroNameTok,
+               const clang::MacroDefinition &MD,
+               clang::SourceRange Range) override;
+  void SourceRangeSkipped(clang::SourceRange Range) override;
+  void If(clang::SourceLocation Loc, clang::SourceRange ConditionRange,
+          ConditionValueKind ConditionValue) override;
+  void Elif(clang::SourceLocation Loc, clang::SourceRange ConditionRange,
+            ConditionValueKind ConditionValue, clang::SourceLocation IfLoc) override;
+  void Ifdef(clang::SourceLocation Loc, const clang::Token &MacroNameTok,
+             const clang::MacroDefinition &MD) override;
+  void Ifndef(clang::SourceLocation Loc, const clang::Token &MacroNameTok,
+              const clang::MacroDefinition &MD) override;
+  void Else(clang::SourceLocation Loc,
+            clang::SourceLocation IfLoc) override;
+  void Endif(clang::SourceLocation Loc,
+             clang::SourceLocation IfLoc) override;
+
+  // Helper functions.
+
+  /// \brief Start a new callback.
+  void beginCallback(const char *Name);
+
+  /// \brief Append a string to the top trace item.
+  void append(const char *Str);
+
+  /// \brief Append a bool argument to the top trace item.
+  void appendArgument(const char *Name, bool Value);
+
+  /// \brief Append an int argument to the top trace item.
+  void appendArgument(const char *Name, int Value);
+
+  /// \brief Append a string argument to the top trace item.
+  void appendArgument(const char *Name, const char *Value);
+
+  /// \brief Append a string reference object argument to the top trace item.
+  void appendArgument(const char *Name, llvm::StringRef Value);
+
+  /// \brief Append a string object argument to the top trace item.
+  void appendArgument(const char *Name, const std::string &Value);
+
+  /// \brief Append a token argument to the top trace item.
+  void appendArgument(const char *Name, const clang::Token &Value);
+
+  /// \brief Append an enum argument to the top trace item.
+  void appendArgument(const char *Name, int Value, const char *const Strings[]);
+
+  /// \brief Append a FileID argument to the top trace item.
+  void appendArgument(const char *Name, clang::FileID Value);
+
+  /// \brief Append a FileEntry argument to the top trace item.
+  void appendArgument(const char *Name, const clang::FileEntry *Value);
+
+  /// \brief Append a SourceLocation argument to the top trace item.
+  void appendArgument(const char *Name, clang::SourceLocation Value);
+
+  /// \brief Append a SourceRange argument to the top trace item.
+  void appendArgument(const char *Name, clang::SourceRange Value);
+
+  /// \brief Append a CharSourceRange argument to the top trace item.
+  void appendArgument(const char *Name, clang::CharSourceRange Value);
+
+  /// \brief Append a ModuleIdPath argument to the top trace item.
+  void appendArgument(const char *Name, clang::ModuleIdPath Value);
+
+  /// \brief Append an IdentifierInfo argument to the top trace item.
+  void appendArgument(const char *Name, const clang::IdentifierInfo *Value);
+
+  /// \brief Append a MacroDirective argument to the top trace item.
+  void appendArgument(const char *Name, const clang::MacroDirective *Value);
+
+  /// \brief Append a MacroDefinition argument to the top trace item.
+  void appendArgument(const char *Name, const clang::MacroDefinition &Value);
+
+  /// \brief Append a MacroArgs argument to the top trace item.
+  void appendArgument(const char *Name, const clang::MacroArgs *Value);
+
+  /// \brief Append a Module argument to the top trace item.
+  void appendArgument(const char *Name, const clang::Module *Value);
+
+  /// \brief Append a double-quoted argument to the top trace item.
+  void appendQuotedArgument(const char *Name, const std::string &Value);
+
+  /// \brief Append a double-quoted file path argument to the top trace item.
+  void appendFilePathArgument(const char *Name, llvm::StringRef Value);
+
+  /// \brief Get the raw source string of the range.
+  llvm::StringRef getSourceString(clang::CharSourceRange Range);
+
+  /// \brief Callback trace information.
+  /// We use a reference so the trace will be preserved for the caller
+  /// after this object is destructed.
+  std::vector<CallbackCall> &CallbackCalls;
+
+  /// \brief Names of callbacks to ignore.
+  llvm::SmallSet<std::string, 4> &Ignore;
+
+  /// \brief Inhibit trace while this is set.
+  bool DisableTrace;
+
+  clang::Preprocessor &PP;
+};
+
+#endif // PPTRACE_PPCALLBACKSTRACKER_H
diff --git a/pp-trace/PPTrace.cpp b/pp-trace/PPTrace.cpp
new file mode 100644 (file)
index 0000000..0a50792
--- /dev/null
@@ -0,0 +1,233 @@
+//===--- tools/pp-trace/PPTrace.cpp - Clang preprocessor tracer -----------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements pp-trace, a tool for displaying a textual trace
+// of the Clang preprocessor activity.  It's based on a derivation of the
+// PPCallbacks class, that once registerd with Clang, receives callback calls
+// to its virtual members, and outputs the information passed to the callbacks
+// in a high-level YAML format.
+//
+// The pp-trace tool also serves as the basis for a test of the PPCallbacks
+// mechanism.
+//
+// The pp-trace tool supports the following general command line format:
+//
+//    pp-trace [pp-trace options] (source file) [compiler options]
+//
+// Basically you put the pp-trace options first, then the source file or files,
+// and then any options you want to pass to the compiler.
+//
+// These are the pp-trace options:
+//
+//    -ignore (callback list)     Don't display output for a comma-separated
+//                                list of callbacks, i.e.:
+//                                  -ignore "FileChanged,InclusionDirective"
+//
+//    -output (file)              Output trace to the given file in a YAML
+//                                format, e.g.:
+//
+//                                  ---
+//                                  - Callback: Name
+//                                    Argument1: Value1
+//                                    Argument2: Value2
+//                                  (etc.)
+//                                  ...
+//
+// Future Directions:
+//
+// 1. Add option opposite to "-ignore" that specifys a comma-separated option
+// list of callbacs.  Perhaps "-only" or "-exclusive".
+//
+//===----------------------------------------------------------------------===//
+
+#include "PPCallbacksTracker.h"
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Driver/Options.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendActions.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Tooling/CompilationDatabase.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/Option/Arg.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Option/OptTable.h"
+#include "llvm/Option/Option.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/ToolOutputFile.h"
+#include <algorithm>
+#include <fstream>
+#include <iterator>
+#include <string>
+#include <vector>
+
+using namespace clang;
+using namespace clang::driver;
+using namespace clang::driver::options;
+using namespace clang::tooling;
+using namespace llvm;
+using namespace llvm::opt;
+
+// Options:
+
+// Collect the source files.
+static cl::list<std::string> SourcePaths(cl::Positional,
+                                         cl::desc("<source0> [... <sourceN>]"),
+                                         cl::OneOrMore);
+
+// Option to specify a list or one or more callback names to ignore.
+static cl::opt<std::string> IgnoreCallbacks(
+    "ignore", cl::init(""),
+    cl::desc("Ignore callbacks, i.e. \"Callback1, Callback2...\"."));
+
+// Option to specify the trace output file name.
+static cl::opt<std::string> OutputFileName(
+    "output", cl::init(""),
+    cl::desc("Output trace to the given file name or '-' for stdout."));
+
+// Collect all other arguments, which will be passed to the front end.
+static cl::list<std::string>
+    CC1Arguments(cl::ConsumeAfter,
+                 cl::desc("<arguments to be passed to front end>..."));
+
+// Frontend action stuff:
+
+namespace {
+// Consumer is responsible for setting up the callbacks.
+class PPTraceConsumer : public ASTConsumer {
+public:
+  PPTraceConsumer(SmallSet<std::string, 4> &Ignore,
+                  std::vector<CallbackCall> &CallbackCalls, Preprocessor &PP) {
+    // PP takes ownership.
+    PP.addPPCallbacks(llvm::make_unique<PPCallbacksTracker>(Ignore,
+                                                            CallbackCalls, PP));
+  }
+};
+
+class PPTraceAction : public SyntaxOnlyAction {
+public:
+  PPTraceAction(SmallSet<std::string, 4> &Ignore,
+                std::vector<CallbackCall> &CallbackCalls)
+      : Ignore(Ignore), CallbackCalls(CallbackCalls) {}
+
+protected:
+  std::unique_ptr<clang::ASTConsumer>
+  CreateASTConsumer(CompilerInstance &CI, StringRef InFile) override {
+    return llvm::make_unique<PPTraceConsumer>(Ignore, CallbackCalls,
+                                              CI.getPreprocessor());
+  }
+
+private:
+  SmallSet<std::string, 4> &Ignore;
+  std::vector<CallbackCall> &CallbackCalls;
+};
+
+class PPTraceFrontendActionFactory : public FrontendActionFactory {
+public:
+  PPTraceFrontendActionFactory(SmallSet<std::string, 4> &Ignore,
+                               std::vector<CallbackCall> &CallbackCalls)
+      : Ignore(Ignore), CallbackCalls(CallbackCalls) {}
+
+  PPTraceAction *create() override {
+    return new PPTraceAction(Ignore, CallbackCalls);
+  }
+
+private:
+  SmallSet<std::string, 4> &Ignore;
+  std::vector<CallbackCall> &CallbackCalls;
+};
+} // namespace
+
+// Output the trace given its data structure and a stream.
+static int outputPPTrace(std::vector<CallbackCall> &CallbackCalls,
+                         llvm::raw_ostream &OS) {
+  // Mark start of document.
+  OS << "---\n";
+
+  for (std::vector<CallbackCall>::const_iterator I = CallbackCalls.begin(),
+                                                 E = CallbackCalls.end();
+       I != E; ++I) {
+    const CallbackCall &Callback = *I;
+    OS << "- Callback: " << Callback.Name << "\n";
+
+    for (auto AI = Callback.Arguments.begin(), AE = Callback.Arguments.end();
+         AI != AE; ++AI) {
+      const Argument &Arg = *AI;
+      OS << "  " << Arg.Name << ": " << Arg.Value << "\n";
+    }
+  }
+
+  // Mark end of document.
+  OS << "...\n";
+
+  return 0;
+}
+
+// Program entry point.
+int main(int Argc, const char **Argv) {
+
+  // Parse command line.
+  cl::ParseCommandLineOptions(Argc, Argv, "pp-trace.\n");
+
+  // Parse the IgnoreCallbacks list into strings.
+  SmallVector<StringRef, 32> IgnoreCallbacksStrings;
+  StringRef(IgnoreCallbacks).split(IgnoreCallbacksStrings, ",",
+                                   /*MaxSplit=*/ -1, /*KeepEmpty=*/false);
+  SmallSet<std::string, 4> Ignore;
+  for (SmallVector<StringRef, 32>::iterator I = IgnoreCallbacksStrings.begin(),
+                                            E = IgnoreCallbacksStrings.end();
+       I != E; ++I)
+    Ignore.insert(*I);
+
+  // Create the compilation database.
+  SmallString<256> PathBuf;
+  sys::fs::current_path(PathBuf);
+  std::unique_ptr<CompilationDatabase> Compilations;
+  Compilations.reset(
+      new FixedCompilationDatabase(Twine(PathBuf), CC1Arguments));
+
+  // Store the callback trace information here.
+  std::vector<CallbackCall> CallbackCalls;
+
+  // Create the tool and run the compilation.
+  ClangTool Tool(*Compilations, SourcePaths);
+  PPTraceFrontendActionFactory Factory(Ignore, CallbackCalls);
+  int HadErrors = Tool.run(&Factory);
+
+  // If we had errors, exit early.
+  if (HadErrors)
+    return HadErrors;
+
+  // Do the output.
+  if (!OutputFileName.size()) {
+    HadErrors = outputPPTrace(CallbackCalls, llvm::outs());
+  } else {
+    // Set up output file.
+    std::error_code EC;
+    llvm::tool_output_file Out(OutputFileName, EC, llvm::sys::fs::F_Text);
+    if (EC) {
+      llvm::errs() << "pp-trace: error creating " << OutputFileName << ":"
+                   << EC.message() << "\n";
+      return 1;
+    }
+
+    HadErrors = outputPPTrace(CallbackCalls, Out.os());
+
+    // Tell tool_output_file that we want to keep the file.
+    if (HadErrors == 0)
+      Out.keep();
+  }
+
+  return HadErrors;
+}
diff --git a/test/.clang-format b/test/.clang-format
new file mode 100644 (file)
index 0000000..4799b66
--- /dev/null
@@ -0,0 +1,2 @@
+BasedOnStyle: LLVM
+ColumnLimit: 0
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
new file mode 100644 (file)
index 0000000..996afec
--- /dev/null
@@ -0,0 +1,75 @@
+# Test runner infrastructure for Clang-based tools. This configures the Clang
+# test trees for use by Lit, and delegates to LLVM's lit test handlers.
+#
+# Note that currently we don't support stand-alone builds of Clang, you must
+# be building Clang from within a combined LLVM+Clang checkout..
+
+set(CLANG_TOOLS_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/..")
+set(CLANG_TOOLS_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/..")
+
+if (CMAKE_CFG_INTDIR STREQUAL ".")
+  set(LLVM_BUILD_MODE ".")
+else ()
+  set(LLVM_BUILD_MODE "%(build_mode)s")
+endif ()
+
+string(REPLACE ${CMAKE_CFG_INTDIR} ${LLVM_BUILD_MODE} CLANG_TOOLS_DIR ${LLVM_RUNTIME_OUTPUT_INTDIR})
+
+configure_lit_site_cfg(
+  ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in
+  ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg
+  )
+
+configure_lit_site_cfg(
+  ${CMAKE_CURRENT_SOURCE_DIR}/Unit/lit.site.cfg.in
+  ${CMAKE_CURRENT_BINARY_DIR}/Unit/lit.site.cfg
+  )
+
+option(CLANG_TOOLS_TEST_USE_VG "Run Clang tools' tests under Valgrind" OFF)
+if(CLANG_TOOLS_TEST_USE_VG)
+  set(CLANG_TOOLS_TEST_EXTRA_ARGS ${CLANG_TEST_EXTRA_ARGS} "--vg")
+endif()
+
+set(CLANG_TOOLS_TEST_DEPS
+  # clang-tidy tests require it.
+  clang-headers
+
+  # For the clang-tidy libclang integration test.
+  c-index-test
+  # For the clang-apply-replacements test that uses clang-rename.
+  clang-rename
+
+  # Individual tools we test.
+  clang-apply-replacements
+  clang-change-namespace
+  clangd
+  clang-include-fixer
+  clang-move
+  clang-query
+  clang-reorder-fields
+  clang-tidy
+  find-all-symbols
+  modularize
+  pp-trace
+
+  # Unit tests
+  ExtraToolsUnitTests
+  )
+
+set(llvm_utils
+  FileCheck count not
+  )
+
+foreach(t ${llvm_utils})
+  if(TARGET ${t})
+    list(APPEND CLANG_TOOLS_TEST_DEPS ${t})
+  endif()
+endforeach()
+
+add_lit_testsuite(check-clang-tools "Running the Clang extra tools' regression tests"
+  ${CMAKE_CURRENT_BINARY_DIR}
+  DEPENDS ${CLANG_TOOLS_TEST_DEPS}
+  ARGS ${CLANG_TOOLS_TEST_EXTRA_ARGS}
+  )
+
+set_target_properties(check-clang-tools PROPERTIES FOLDER "Clang extra tools' tests")
diff --git a/test/Unit/lit.cfg b/test/Unit/lit.cfg
new file mode 100644 (file)
index 0000000..ff70123
--- /dev/null
@@ -0,0 +1,54 @@
+# -*- Python -*-
+
+import platform
+
+import lit.formats
+
+config.name = "Extra Tools Unit Tests"
+config.suffixes = [] # Seems not to matter for google tests?
+
+# Test Source and Exec root dirs both point to the same directory where google
+# test binaries are built.
+extra_tools_obj_dir = getattr(config, 'extra_tools_obj_dir', None)
+if extra_tools_obj_dir is not None:
+  config.test_source_root = extra_tools_obj_dir
+  config.test_exec_root = config.test_source_root
+
+# All GoogleTests are named to have 'Tests' as their suffix. The '.' option is
+# a special value for GoogleTest indicating that it should look through the
+# entire testsuite recursively for tests (alternatively, one could provide a
+# ;-separated list of subdirectories).
+config.test_format = lit.formats.GoogleTest('.', 'Tests')
+
+# If the site-specific configuration wasn't loaded (e.g. the build system failed
+# to create it or the user is running a test file directly) try to come up with
+# sane config options.
+if config.test_exec_root is None:
+  # Look for a --param=extra_tools_unit_site_config option.
+  site_cfg = lit_config.params.get('extra_tools_unit_site_config', None)
+  if site_cfg and os.path.exists(site_cfg):
+      lit_config.load_config(config, site_cfg)
+      raise SystemExit
+
+  # FIXME: Support out-of-tree builds? See clang/test/Unit/lit.cfg if we care.
+
+shlibpath_var = ''
+if platform.system() == 'Linux':
+    shlibpath_var = 'LD_LIBRARY_PATH'
+elif platform.system() == 'Darwin':
+    shlibpath_var = 'DYLD_LIBRARY_PATH'
+elif platform.system() == 'Windows':
+    shlibpath_var = 'PATH'
+
+# Point the dynamic loader at dynamic libraries in 'lib'.
+llvm_libs_dir = getattr(config, 'llvm_libs_dir', None)
+if not llvm_libs_dir:
+    lit_config.fatal('No LLVM libs dir set!')
+shlibpath = os.path.pathsep.join((llvm_libs_dir,
+                                 config.environment.get(shlibpath_var,'')))
+
+# Win32 seeks DLLs along %PATH%.
+if sys.platform in ['win32', 'cygwin'] and os.path.isdir(config.shlibdir):
+    shlibpath = os.path.pathsep.join((config.shlibdir, shlibpath))
+
+config.environment[shlibpath_var] = shlibpath
diff --git a/test/Unit/lit.site.cfg.in b/test/Unit/lit.site.cfg.in
new file mode 100644 (file)
index 0000000..69a5cd5
--- /dev/null
@@ -0,0 +1,9 @@
+@LIT_SITE_CFG_IN_HEADER@
+
+config.extra_tools_obj_dir = "@CLANG_TOOLS_BINARY_DIR@/unittests"
+config.extra_tools_src_dir = "@CLANG_TOOLS_SOURCE_DIR@/unittests"
+config.llvm_libs_dir = "@LLVM_LIBS_DIR@"
+config.shlibdir = "@SHLIBDIR@"
+config.target_triple = "@TARGET_TRIPLE@"
+
+lit_config.load_config(config, "@CLANG_TOOLS_SOURCE_DIR@/test/Unit/lit.cfg")
diff --git a/test/change-namespace/Inputs/fake-std.h b/test/change-namespace/Inputs/fake-std.h
new file mode 100644 (file)
index 0000000..24d3f97
--- /dev/null
@@ -0,0 +1,5 @@
+namespace std {
+  class STD {};
+}
+
+using namespace std;
diff --git a/test/change-namespace/lambda-function.cpp b/test/change-namespace/lambda-function.cpp
new file mode 100644 (file)
index 0000000..452983e
--- /dev/null
@@ -0,0 +1,37 @@
+// RUN: clang-change-namespace -old_namespace "na::nb" -new_namespace "x::y" --file_pattern ".*" %s -- -std=c++11 | sed 's,// CHECK.*,,' | FileCheck %s
+
+template <class T>
+class function;
+template <class R, class... ArgTypes>
+class function<R(ArgTypes...)> {
+public:
+  template <typename Functor>
+  function(Functor f) {}
+  R operator()(ArgTypes...) const {}
+};
+
+namespace x {
+// CHECK: namespace x {
+class X {};
+}
+
+namespace na {
+namespace nb {
+// CHECK: namespace x {
+// CHECK-NEXT: namespace y {
+void f(function<void(int)> func, int param) { func(param); }
+void g() { f([](int x) {}, 1); }
+
+// x::X in function type parameter list will have translation unit context, so
+// we simply replace it with fully-qualified name.
+using TX = function<x::X(x::X)>;
+// CHECK: using TX = function<X(x::X)>;
+
+class A {};
+using TA = function<A(A)>;
+// CHECK: using TA = function<A(A)>;
+
+// CHECK: } // namespace y
+// CHECK-NEXT: } // namespace x
+}
+}
diff --git a/test/change-namespace/macro.cpp b/test/change-namespace/macro.cpp
new file mode 100644 (file)
index 0000000..ba47de6
--- /dev/null
@@ -0,0 +1,29 @@
+// RUN: cp %S/macro.cpp %T/macro.cpp
+// RUN: echo "#define USING using na::nc::X" > %T/macro.h
+//
+// RUN: clang-change-namespace -old_namespace "na::nb" -new_namespace "x::y" --file_pattern "macro.cpp" --i %T/macro.cpp --
+// RUN: FileCheck -input-file=%T/macro.cpp -check-prefix=CHECK-CC %s
+// RUN: FileCheck -input-file=%T/macro.h -check-prefix=CHECK-HEADER %s
+//
+// RUN: cp %S/macro.cpp %T/macro.cpp
+// RUN: echo "#define USING using na::nc::X" > %T/macro.h
+// RUN: clang-change-namespace -old_namespace "na::nb" -new_namespace "x::y" --file_pattern ".*" --i %T/macro.cpp --
+// RUN: FileCheck -input-file=%T/macro.cpp -check-prefix=CHECK-CC %s
+// RUN: FileCheck -input-file=%T/macro.h -check-prefix=CHECK-CHANGED-HEADER %s
+#include "macro.h"
+namespace na { namespace nc { class X{}; } }
+
+namespace na {
+namespace nb {
+USING;
+}
+}
+// CHECK-CC: namespace x {
+// CHECK-CC: namespace y {
+// CHECK-CC: USING;
+// CHECK-CC: } // namespace y
+// CHECK-CC: } // namespace x
+
+// CHECK-HEADER: #define USING using na::nc::X
+
+// CHECK-CHANGED-HEADER: #define USING using ::na::nc::X
diff --git a/test/change-namespace/simple-move.cpp b/test/change-namespace/simple-move.cpp
new file mode 100644 (file)
index 0000000..ea3c3d3
--- /dev/null
@@ -0,0 +1,10 @@
+// RUN: clang-change-namespace -old_namespace "na::nb" -new_namespace "x::y" --file_pattern ".*" %s -- | sed 's,// CHECK.*,,' | FileCheck %s
+// CHECK: namespace x {
+// CHECK-NEXT: namespace y {
+namespace na {
+namespace nb {
+class A {};
+// CHECK: } // namespace y
+// CHECK-NEXT: } // namespace x
+}
+}
diff --git a/test/change-namespace/white-list.cpp b/test/change-namespace/white-list.cpp
new file mode 100644 (file)
index 0000000..48e3a78
--- /dev/null
@@ -0,0 +1,19 @@
+// RUN: echo "^std::.*$" > %T/white-list.txt
+// RUN: clang-change-namespace -old_namespace "na::nb" -new_namespace "x::y" --file_pattern ".*" --whitelist_file %T/white-list.txt %s -- | sed 's,// CHECK.*,,' | FileCheck %s
+
+#include "Inputs/fake-std.h"
+
+// CHECK: namespace x {
+// CHECK-NEXT: namespace y {
+namespace na {
+namespace nb {
+void f() {
+  std::STD x1;
+  STD x2;
+// CHECK: {{^}}  std::STD x1;{{$}}
+// CHECK-NEXT: {{^}}  STD x2;{{$}}
+}
+// CHECK: } // namespace y
+// CHECK-NEXT: } // namespace x
+}
+}
diff --git a/test/clang-apply-replacements/ClangRenameClassReplacements.cpp b/test/clang-apply-replacements/ClangRenameClassReplacements.cpp
new file mode 100644 (file)
index 0000000..2b478bb
--- /dev/null
@@ -0,0 +1,11 @@
+// RUN: rm -rf %t
+// RUN: mkdir -p %t/fixes
+// RUN: cat %s > %t.cpp
+// RUN: clang-rename -offset=254 -new-name=Bar -export-fixes=%t/fixes/clang-rename.yaml %t.cpp --
+// RUN: clang-apply-replacements %t
+// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s
+
+class Foo {}; // CHECK: class Bar {};
+
+// Use grep -FUbo 'Foo' <file> to get the correct offset of Cla when changing
+// this file.
diff --git a/test/clang-apply-replacements/Inputs/basic/basic.h b/test/clang-apply-replacements/Inputs/basic/basic.h
new file mode 100644 (file)
index 0000000..4850968
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef BASIC_H
+#define BASIC_H
+
+
+class Parent {
+public:
+  virtual void func() {}
+};
+
+class Derived : public Parent {
+public:
+  virtual void func() {}
+  // CHECK: virtual void func() override {}
+};
+
+extern void ext(int (&)[5], const Parent &);
+
+void func(int t) {
+  int ints[5];
+  for (unsigned i = 0; i < 5; ++i) {
+    int &e = ints[i];
+    e = t;
+    // CHECK: for (auto & elem : ints) {
+    // CHECK-NEXT: elem = t;
+  }
+
+  Derived d;
+
+  ext(ints, d);
+}
+
+#endif // BASIC_H
diff --git a/test/clang-apply-replacements/Inputs/basic/file1.yaml b/test/clang-apply-replacements/Inputs/basic/file1.yaml
new file mode 100644 (file)
index 0000000..3a5375e
--- /dev/null
@@ -0,0 +1,25 @@
+---
+MainSourceFile:     source1.cpp
+Diagnostics:
+  - DiagnosticName: test-basic
+    Message: Fix
+    FilePath: $(path)/basic.h
+    FileOffset: 242
+    Replacements:
+      - FilePath:        $(path)/basic.h
+        Offset:          242
+        Length:          26
+        ReplacementText: 'auto & elem : ints'
+      - FilePath:        $(path)/basic.h
+        Offset:          276
+        Length:          22
+        ReplacementText: ''
+      - FilePath:        $(path)/basic.h
+        Offset:          298
+        Length:          1
+        ReplacementText: elem
+      - FilePath:        $(path)/../basic/basic.h
+        Offset:          148
+        Length:          0
+        ReplacementText: 'override '
+...
diff --git a/test/clang-apply-replacements/Inputs/basic/file2.yaml b/test/clang-apply-replacements/Inputs/basic/file2.yaml
new file mode 100644 (file)
index 0000000..52d2fe8
--- /dev/null
@@ -0,0 +1,13 @@
+---
+MainSourceFile:     source2.cpp
+Diagnostics:
+  - DiagnosticName: test-basic
+    Message: Fix
+    FilePath: $(path)/basic.h
+    FileOffset: 148
+    Replacements:
+      - FilePath:        $(path)/basic.h
+        Offset:          148
+        Length:          0
+        ReplacementText: 'override '
+...
diff --git a/test/clang-apply-replacements/Inputs/conflict/common.h b/test/clang-apply-replacements/Inputs/conflict/common.h
new file mode 100644 (file)
index 0000000..630a39a
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef COMMON_H
+#define COMMON_H
+
+extern void ext(int (&)[5]);
+
+void func(int t) {
+  int ints[5];
+  for (unsigned i = 0; i < 5; ++i) {
+    ints[i] = t;
+  }
+
+  int *i = 0;
+
+  ext(ints);
+}
+
+#endif // COMMON_H
diff --git a/test/clang-apply-replacements/Inputs/conflict/expected.txt b/test/clang-apply-replacements/Inputs/conflict/expected.txt
new file mode 100644 (file)
index 0000000..32dddd3
--- /dev/null
@@ -0,0 +1,11 @@
+There are conflicting changes to $(path)/common.h:
+The following changes conflict:
+  Replace 8:8-8:33 with "auto & i : ints"
+  Replace 8:8-8:33 with "int & elem : ints"
+The following changes conflict:
+  Replace 9:5-9:11 with "elem"
+  Replace 9:5-9:11 with "i"
+The following changes conflict:
+  Remove 12:3-12:14
+  Insert at 12:12 (int*)
+  Replace 12:12-12:12 with "nullptr"
diff --git a/test/clang-apply-replacements/Inputs/conflict/file1.yaml b/test/clang-apply-replacements/Inputs/conflict/file1.yaml
new file mode 100644 (file)
index 0000000..8505273
--- /dev/null
@@ -0,0 +1,21 @@
+---
+MainSourceFile: source1.cpp
+Diagnostics:
+  - DiagnosticName: test-conflict
+    Message: Fix
+    FilePath: $(path)/common.h
+    FileOffset: 106
+    Replacements:
+      - FilePath:        $(path)/common.h
+        Offset:          106
+        Length:          26
+        ReplacementText: 'auto & i : ints'
+      - FilePath:        $(path)/common.h
+        Offset:          140
+        Length:          7
+        ReplacementText: i
+      - FilePath:        $(path)/common.h
+        Offset:          160
+        Length:          12
+        ReplacementText: ''
+...
diff --git a/test/clang-apply-replacements/Inputs/conflict/file2.yaml b/test/clang-apply-replacements/Inputs/conflict/file2.yaml
new file mode 100644 (file)
index 0000000..32b35ac
--- /dev/null
@@ -0,0 +1,21 @@
+---
+MainSourceFile: source2.cpp
+Diagnostics:
+  - DiagnosticName:  test-conflict
+    Message: Fix
+    FilePath: $(path)/common.h
+    FileOffset: 106
+    Replacements:
+      - FilePath:        $(path)/common.h
+        Offset:          106
+        Length:          26
+        ReplacementText: 'int & elem : ints'
+      - FilePath:        $(path)/common.h
+        Offset:          140
+        Length:          7
+        ReplacementText: elem
+      - FilePath:        $(path)/common.h
+        Offset:          169
+        Length:          1
+        ReplacementText: nullptr
+...
diff --git a/test/clang-apply-replacements/Inputs/conflict/file3.yaml b/test/clang-apply-replacements/Inputs/conflict/file3.yaml
new file mode 100644 (file)
index 0000000..88543e0
--- /dev/null
@@ -0,0 +1,13 @@
+---
+MainSourceFile: source1.cpp
+Diagnostics:
+  - DiagnosticName:  test-conflict
+    Message: Fix
+    FilePath: $(path)/common.h
+    FileOffset: 169
+    Replacements:
+      - FilePath:        $(path)/common.h
+        Offset:          169
+        Length:          0
+        ReplacementText: "(int*)"
+...
diff --git a/test/clang-apply-replacements/Inputs/crlf/crlf.cpp b/test/clang-apply-replacements/Inputs/crlf/crlf.cpp
new file mode 100644 (file)
index 0000000..26f7996
--- /dev/null
@@ -0,0 +1,6 @@
+\r
+// This file intentionally uses a CRLF newlines!\r
+\r
+void foo() {\r
+  int *x = 0;\r
+}\r
diff --git a/test/clang-apply-replacements/Inputs/crlf/crlf.cpp.expected b/test/clang-apply-replacements/Inputs/crlf/crlf.cpp.expected
new file mode 100644 (file)
index 0000000..ad8e907
--- /dev/null
@@ -0,0 +1,6 @@
+\r
+// This file intentionally uses a CRLF newlines!\r
+\r
+void foo() {\r
+  int *x = nullptr;\r
+}\r
diff --git a/test/clang-apply-replacements/Inputs/crlf/file1.yaml b/test/clang-apply-replacements/Inputs/crlf/file1.yaml
new file mode 100644 (file)
index 0000000..06b058e
--- /dev/null
@@ -0,0 +1,13 @@
+---
+MainSourceFile:      source1.cpp
+Diagnostics:
+  - DiagnosticName:  test-crlf
+    Message: Fix
+    FilePath: $(path)/crlf.cpp
+    FileOffset: 79
+    Replacements:
+      - FilePath:        $(path)/crlf.cpp
+        Offset:          79
+        Length:          1
+        ReplacementText: nullptr
+...
diff --git a/test/clang-apply-replacements/Inputs/format/no.cpp b/test/clang-apply-replacements/Inputs/format/no.cpp
new file mode 100644 (file)
index 0000000..5bc9081
--- /dev/null
@@ -0,0 +1,6 @@
+class C {};
+
+void f() { // This comment necessary to prevent formatting as void f() { ... }
+  C *a = new C();
+  // CHECK: {{^\ \ auto\ a\ \=\ new\ C\(\);}}
+}
diff --git a/test/clang-apply-replacements/Inputs/format/no.yaml b/test/clang-apply-replacements/Inputs/format/no.yaml
new file mode 100644 (file)
index 0000000..3118cc6
--- /dev/null
@@ -0,0 +1,13 @@
+---
+MainSourceFile:  no.cpp
+Diagnostics:
+  - DiagnosticName:  test-no
+    Message: Fix
+    FilePath: $(path)/no.cpp
+    FileOffset: 94
+    Replacements:
+      - FilePath:        $(path)/no.cpp
+        Offset:          94
+        Length:          3
+        ReplacementText: 'auto '
+...
diff --git a/test/clang-apply-replacements/Inputs/format/yes.cpp b/test/clang-apply-replacements/Inputs/format/yes.cpp
new file mode 100644 (file)
index 0000000..8f600c3
--- /dev/null
@@ -0,0 +1,22 @@
+class MyType012345678901234567890123456789 {};
+
+void g(int, int*, int, int*, int, int*, int);
+
+void f() {
+  MyType012345678901234567890123456789 *a =
+      new MyType012345678901234567890123456789();
+  // CHECK: {{^\ \ auto\ a\ \=\ new\ MyType012345678901234567890123456789\(\);}}
+
+  int iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii;
+  int jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj;
+  int kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk;
+  int mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm;
+  g(iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii, 0, jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj,
+    0, kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk, 0, mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm);
+  // CHECK: g(iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii, nullptr,
+  // CHECK-NEXT: jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj, nullptr,
+  // CHECK-NEXT: kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk, nullptr,
+  // CHECK-NEXT: mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm);
+
+  delete a;
+}
diff --git a/test/clang-apply-replacements/Inputs/format/yes.yaml b/test/clang-apply-replacements/Inputs/format/yes.yaml
new file mode 100644 (file)
index 0000000..6ded4db
--- /dev/null
@@ -0,0 +1,27 @@
+# These replacements are out of order on purpose to test that they get sorted
+# so that formatting happens correctly.
+---
+MainSourceFile:  yes.cpp
+Diagnostics:
+  - DiagnosticName:  test-yes
+    Message: Fix
+    FilePath: $(path)/yes.cpp
+    FileOffset: 494
+    Replacements:
+      - FilePath:        $(path)/yes.cpp
+        Offset:          494
+        Length:          1
+        ReplacementText: nullptr
+      - FilePath:        $(path)/yes.cpp
+        Offset:          410
+        Length:          1
+        ReplacementText: nullptr
+      - FilePath:        $(path)/yes.cpp
+        Offset:          454
+        Length:          1
+        ReplacementText: nullptr
+      - FilePath:        $(path)/yes.cpp
+        Offset:          108
+        Length:          38
+        ReplacementText: 'auto '
+...
diff --git a/test/clang-apply-replacements/basic.cpp b/test/clang-apply-replacements/basic.cpp
new file mode 100644 (file)
index 0000000..4f19a96
--- /dev/null
@@ -0,0 +1,17 @@
+// RUN: mkdir -p %T/Inputs/basic
+// RUN: grep -Ev "// *[A-Z-]+:" %S/Inputs/basic/basic.h > %T/Inputs/basic/basic.h
+// RUN: sed "s#\$(path)#%/T/Inputs/basic#" %S/Inputs/basic/file1.yaml > %T/Inputs/basic/file1.yaml
+// RUN: sed "s#\$(path)#%/T/Inputs/basic#" %S/Inputs/basic/file2.yaml > %T/Inputs/basic/file2.yaml
+// RUN: clang-apply-replacements %T/Inputs/basic
+// RUN: FileCheck -input-file=%T/Inputs/basic/basic.h %S/Inputs/basic/basic.h
+//
+// Check that the yaml files are *not* deleted after running clang-apply-replacements without remove-change-desc-files.
+// RUN: ls -1 %T/Inputs/basic | FileCheck %s --check-prefix=YAML
+//
+// Check that the yaml files *are* deleted after running clang-apply-replacements with remove-change-desc-files.
+// RUN: grep -Ev "// *[A-Z-]+:" %S/Inputs/basic/basic.h > %T/Inputs/basic/basic.h
+// RUN: clang-apply-replacements -remove-change-desc-files %T/Inputs/basic
+// RUN: ls -1 %T/Inputs/basic | FileCheck %s --check-prefix=NO_YAML
+//
+// YAML: {{^file.\.yaml$}}
+// NO_YAML-NOT: {{^file.\.yaml$}}
diff --git a/test/clang-apply-replacements/conflict.cpp b/test/clang-apply-replacements/conflict.cpp
new file mode 100644 (file)
index 0000000..c1f2342
--- /dev/null
@@ -0,0 +1,17 @@
+// RUN: mkdir -p %T/Inputs/conflict
+// RUN: sed "s#\$(path)#%/S/Inputs/conflict#" %S/Inputs/conflict/file1.yaml > %T/Inputs/conflict/file1.yaml
+// RUN: sed "s#\$(path)#%/S/Inputs/conflict#" %S/Inputs/conflict/file2.yaml > %T/Inputs/conflict/file2.yaml
+// RUN: sed "s#\$(path)#%/S/Inputs/conflict#" %S/Inputs/conflict/file3.yaml > %T/Inputs/conflict/file3.yaml
+// RUN: sed "s#\$(path)#%/S/Inputs/conflict#" %S/Inputs/conflict/expected.txt > %T/Inputs/conflict/expected.txt
+// RUN: not clang-apply-replacements %T/Inputs/conflict > %T/Inputs/conflict/output.txt 2>&1
+// RUN: diff -b %T/Inputs/conflict/output.txt %T/Inputs/conflict/expected.txt
+//
+// Check that the yaml files are *not* deleted after running clang-apply-replacements without remove-change-desc-files even when there is a failure.
+// RUN: ls -1 %T/Inputs/conflict | FileCheck %s --check-prefix=YAML
+//
+// Check that the yaml files *are* deleted after running clang-apply-replacements with remove-change-desc-files even when there is a failure.
+// RUN: not clang-apply-replacements %T/Inputs/conflict -remove-change-desc-files > %T/Inputs/conflict/output.txt 2>&1
+// RUN: ls -1 %T/Inputs/conflict | FileCheck %s --check-prefix=NO_YAML
+//
+// YAML: {{^file.\.yaml$}}
+// NO_YAML-NOT: {{^file.\.yaml$}}
diff --git a/test/clang-apply-replacements/crlf.cpp b/test/clang-apply-replacements/crlf.cpp
new file mode 100644 (file)
index 0000000..8a8fd46
--- /dev/null
@@ -0,0 +1,5 @@
+// RUN: mkdir -p %T/Inputs/crlf
+// RUN: cp %S/Inputs/crlf/crlf.cpp %T/Inputs/crlf/crlf.cpp
+// RUN: sed "s#\$(path)#%/T/Inputs/crlf#" %S/Inputs/crlf/file1.yaml > %T/Inputs/crlf/file1.yaml
+// RUN: clang-apply-replacements %T/Inputs/crlf
+// RUN: diff %T/Inputs/crlf/crlf.cpp %S/Inputs/crlf/crlf.cpp.expected
diff --git a/test/clang-apply-replacements/format.cpp b/test/clang-apply-replacements/format.cpp
new file mode 100644 (file)
index 0000000..7de320d
--- /dev/null
@@ -0,0 +1,15 @@
+// RUN: mkdir -p %T/Inputs/format
+//
+// yes.cpp requires formatting after replacements are applied. no.cpp does not.
+// The presence of no.cpp ensures that files that don't need formatting still
+// have their new state written to disk after applying replacements.
+//
+// RUN: grep -Ev "// *[A-Z-]+:" %S/Inputs/format/yes.cpp > %T/Inputs/format/yes.cpp
+// RUN: grep -Ev "// *[A-Z-]+:" %S/Inputs/format/no.cpp > %T/Inputs/format/no.cpp
+// RUN: sed "s#\$(path)#%/T/Inputs/format#" %S/Inputs/format/yes.yaml > %T/Inputs/format/yes.yaml
+// RUN: sed "s#\$(path)#%/T/Inputs/format#" %S/Inputs/format/no.yaml > %T/Inputs/format/no.yaml
+// RUN: clang-apply-replacements -format %T/Inputs/format
+// RUN: FileCheck --strict-whitespace -input-file=%T/Inputs/format/yes.cpp %S/Inputs/format/yes.cpp
+// RUN: FileCheck --strict-whitespace -input-file=%T/Inputs/format/no.cpp %S/Inputs/format/no.cpp
+//
+// RUN not clang-apply-replacements -format=blah %T/Inputs/format
diff --git a/test/clang-move/Inputs/database_template.json b/test/clang-move/Inputs/database_template.json
new file mode 100644 (file)
index 0000000..ceb499c
--- /dev/null
@@ -0,0 +1,7 @@
+[
+{
+  "directory": "$test_dir/build",
+  "command": "clang++ -o test.o -I../include $test_dir/src/test.cpp",
+  "file": "$test_dir/src/test.cpp"
+}
+]
diff --git a/test/clang-move/Inputs/enum.h b/test/clang-move/Inputs/enum.h
new file mode 100644 (file)
index 0000000..9ec3926
--- /dev/null
@@ -0,0 +1,9 @@
+namespace a {
+enum E1 { Green, Red };
+
+enum class E2 { Yellow };
+
+class C {
+  enum E3 { Blue };
+};
+} // namespace a
diff --git a/test/clang-move/Inputs/function_test.cpp b/test/clang-move/Inputs/function_test.cpp
new file mode 100644 (file)
index 0000000..5e86f59
--- /dev/null
@@ -0,0 +1,5 @@
+#include "function_test.h"
+
+void f() {}
+
+void A::f() {}
diff --git a/test/clang-move/Inputs/function_test.h b/test/clang-move/Inputs/function_test.h
new file mode 100644 (file)
index 0000000..810f803
--- /dev/null
@@ -0,0 +1,14 @@
+void f();
+
+inline int g() { return 0; }
+
+template<typename T>
+void h(T t) {}
+
+template<>
+void h(int t) {}
+
+class A {
+ public:
+  void f();
+};
diff --git a/test/clang-move/Inputs/helper_decls_test.cpp b/test/clang-move/Inputs/helper_decls_test.cpp
new file mode 100644 (file)
index 0000000..32d2a23
--- /dev/null
@@ -0,0 +1,97 @@
+#include "helper_decls_test.h"
+
+namespace {
+class HelperC1 {
+public:
+  static int I;
+};
+
+int HelperC1::I = 0;
+
+class HelperC2 {};
+
+class HelperC3 {
+ public:
+  static int I;
+};
+
+int HelperC3::I = 0;
+
+void HelperFun1() {}
+
+void HelperFun2() { HelperFun1(); }
+
+const int K1 = 1;
+} // namespace
+
+static const int K2 = 2;
+static void HelperFun3() { K2; }
+
+namespace a {
+
+static const int K3 = 3;
+static const int K4 = HelperC3::I;
+static const int K5 = 5;
+static const int K6 = 6;
+
+static void HelperFun4() {}
+static void HelperFun6() {}
+
+void Class1::f() { HelperFun2(); }
+
+void Class2::f() {
+  HelperFun1();
+  HelperFun3();
+}
+
+void Class3::f() { HelperC1::I; }
+
+void Class4::f() { HelperC2 c2; }
+
+void Class5::f() {
+  int Result = K1 + K2 + K3;
+  HelperFun4();
+}
+
+int Class6::f() {
+  int R = K4;
+  return R;
+}
+
+int Class7::f() {
+  int R = K6;
+  return R;
+}
+
+int Class7::g() {
+  HelperFun6();
+  return 1;
+}
+
+static int HelperFun5() {
+  int R = K5;
+  return R;
+}
+
+void Fun1() { HelperFun5(); }
+
+} // namespace a
+
+namespace b {
+namespace {
+void HelperFun7();
+
+class HelperC4;
+} // namespace
+
+void Fun3() {
+  HelperFun7();
+  HelperC4 *t;
+}
+
+namespace {
+void HelperFun7() {}
+
+class HelperC4 {};
+} // namespace
+} // namespace b
diff --git a/test/clang-move/Inputs/helper_decls_test.h b/test/clang-move/Inputs/helper_decls_test.h
new file mode 100644 (file)
index 0000000..d8927c4
--- /dev/null
@@ -0,0 +1,39 @@
+namespace a {
+class Class1 {
+  void f();
+};
+
+class Class2 {
+  void f();
+};
+
+class Class3 {
+  void f();
+};
+
+class Class4 {
+  void f();
+};
+
+class Class5 {
+  void f();
+};
+
+class Class6 {
+  int f();
+};
+
+class Class7 {
+  int f();
+  int g();
+};
+
+void Fun1();
+
+inline void Fun2() {}
+
+} // namespace a
+
+namespace b {
+void Fun3();
+} // namespace b
diff --git a/test/clang-move/Inputs/macro_helper_test.cpp b/test/clang-move/Inputs/macro_helper_test.cpp
new file mode 100644 (file)
index 0000000..c2c1e1e
--- /dev/null
@@ -0,0 +1,13 @@
+#include "macro_helper_test.h"
+
+#define DEFINE(name) \
+  namespace ns { \
+  static const bool t1 = false; \
+  bool t2_##name = t1; \
+  bool t3_##name = t1; \
+  } \
+  using ns::t2_##name;
+
+DEFINE(test)
+
+void f1() {}
diff --git a/test/clang-move/Inputs/macro_helper_test.h b/test/clang-move/Inputs/macro_helper_test.h
new file mode 100644 (file)
index 0000000..0ddfaae
--- /dev/null
@@ -0,0 +1,2 @@
+class A {};
+void f1();
diff --git a/test/clang-move/Inputs/multiple_class_test.cpp b/test/clang-move/Inputs/multiple_class_test.cpp
new file mode 100644 (file)
index 0000000..5eb4bf3
--- /dev/null
@@ -0,0 +1,52 @@
+#include "multiple_class_test.h"
+
+using a::Move1;
+using namespace a;
+using A = a::Move1;
+static int g = 0;
+
+namespace a {
+int Move1::f() {
+  return 0;
+}
+} // namespace a
+
+namespace {
+using a::Move1;
+using namespace a;
+static int k = 0;
+} // namespace
+
+namespace b {
+using a::Move1;
+using namespace a;
+using T = a::Move1;
+int Move2::f() {
+  return 0;
+}
+} // namespace b
+
+namespace c {
+int Move3::f() {
+  using a::Move1;
+  using namespace b;
+  return 0;
+}
+
+int Move4::f() {
+  return k;
+}
+
+int EnclosingMove5::a = 1;
+
+int EnclosingMove5::Nested::f() {
+  return g;
+}
+
+int EnclosingMove5::Nested::b = 1;
+
+int NoMove::f() {
+  static int F = 0;
+  return g;
+}
+} // namespace c
diff --git a/test/clang-move/Inputs/multiple_class_test.h b/test/clang-move/Inputs/multiple_class_test.h
new file mode 100644 (file)
index 0000000..113a261
--- /dev/null
@@ -0,0 +1,39 @@
+namespace a {
+class Move1 {
+public:
+  int f();
+};
+} // namespace a
+
+namespace b {
+class Move2 {
+public:
+  int f();
+};
+} // namespace b
+
+namespace c {
+class Move3 {
+public:
+  int f();
+};
+
+class Move4 {
+public:
+  int f();
+};
+
+class EnclosingMove5 {
+public:
+  class Nested {
+    int f();
+    static int b;
+  };
+  static int a;
+};
+
+class NoMove {
+public:
+  int f();
+};
+} // namespace c
diff --git a/test/clang-move/Inputs/template_class_test.cpp b/test/clang-move/Inputs/template_class_test.cpp
new file mode 100644 (file)
index 0000000..8ad2dcb
--- /dev/null
@@ -0,0 +1,13 @@
+#include "template_class_test.h"
+
+template <typename T>
+void A<T>::g() {}
+
+template <typename T>
+template <typename U>
+void A<T>::k() {}
+
+template <typename T>
+int A<T>::c = 2;
+
+void B::f() {}
diff --git a/test/clang-move/Inputs/template_class_test.h b/test/clang-move/Inputs/template_class_test.h
new file mode 100644 (file)
index 0000000..d42a158
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef TEMPLATE_CLASS_TEST_H // comment 1
+#define TEMPLATE_CLASS_TEST_H
+
+template <typename T>
+class A {
+ public:
+  void f();
+  void g();
+  template <typename U> void h();
+  template <typename U> void k();
+  static int b;
+  static int c;
+};
+
+template <typename T>
+void A<T>::f() {}
+
+template <typename T>
+template <typename U>
+void A<T>::h() {}
+
+template <typename T>
+int A<T>::b = 2;
+
+class B {
+ public:
+  void f();
+};
+
+#endif // TEMPLATE_CLASS_TEST_H
diff --git a/test/clang-move/Inputs/test.cpp b/test/clang-move/Inputs/test.cpp
new file mode 100644 (file)
index 0000000..fa777f0
--- /dev/null
@@ -0,0 +1,11 @@
+#include "test.h"
+#include "test2.h"
+
+namespace a {
+int Foo::f() {
+  return 0;
+}
+int Foo::f2(int a, int b) {
+  return a + b;
+}
+} // namespace a
diff --git a/test/clang-move/Inputs/test.h b/test/clang-move/Inputs/test.h
new file mode 100644 (file)
index 0000000..8006c34
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef TEST_H // comment 1
+#define TEST_H
+namespace a {
+class Foo {
+public:
+  int f();
+  int f2(int a, int b);
+};
+} // namespace a
+#endif // TEST_H
diff --git a/test/clang-move/Inputs/type_alias.h b/test/clang-move/Inputs/type_alias.h
new file mode 100644 (file)
index 0000000..7a900f6
--- /dev/null
@@ -0,0 +1,11 @@
+typedef int Int1;
+using Int2 = int;
+
+template<class T>
+struct A {};
+
+template <class T> using B = A<T>;
+
+class C {
+  typedef int Int3;
+};
diff --git a/test/clang-move/Inputs/var_test.cpp b/test/clang-move/Inputs/var_test.cpp
new file mode 100644 (file)
index 0000000..9023abc
--- /dev/null
@@ -0,0 +1,6 @@
+#include "var_test.h"
+
+namespace a{
+int kGlobalInt = 1;
+const char *const kGlobalStr = "Hello";
+}
diff --git a/test/clang-move/Inputs/var_test.h b/test/clang-move/Inputs/var_test.h
new file mode 100644 (file)
index 0000000..1bfd54c
--- /dev/null
@@ -0,0 +1,11 @@
+namespace a {
+extern int kGlobalInt;
+extern const char *const kGlobalStr;
+}
+
+int kEvilInt = 2;
+
+inline void f1() {
+  int kGlobalInt = 3;
+  const char *const kGlobalStr = "Hello2";
+}
diff --git a/test/clang-move/move-class.cpp b/test/clang-move/move-class.cpp
new file mode 100644 (file)
index 0000000..a30cb4d
--- /dev/null
@@ -0,0 +1,43 @@
+// RUN: mkdir -p %T/clang-move/build
+// RUN: mkdir -p %T/clang-move/include
+// RUN: mkdir -p %T/clang-move/src
+// RUN: sed 's|$test_dir|%/T/clang-move|g' %S/Inputs/database_template.json > %T/clang-move/compile_commands.json
+// RUN: cp %S/Inputs/test.h  %T/clang-move/include
+// RUN: cp %S/Inputs/test.cpp %T/clang-move/src
+// RUN: touch %T/clang-move/include/test2.h
+// RUN: cd %T/clang-move/build
+// RUN: clang-move -names="a::Foo" -new_cc=%T/clang-move/new_test.cpp -new_header=%T/clang-move/new_test.h -old_cc=../src/test.cpp -old_header=../include/test.h %T/clang-move/src/test.cpp
+// RUN: FileCheck -input-file=%T/clang-move/new_test.cpp -check-prefix=CHECK-NEW-TEST-CPP %s
+// RUN: FileCheck -input-file=%T/clang-move/new_test.h -check-prefix=CHECK-NEW-TEST-H %s
+// RUN: FileCheck -input-file=%T/clang-move/src/test.cpp -check-prefix=CHECK-OLD-TEST-EMPTY -allow-empty %s
+// RUN: FileCheck -input-file=%T/clang-move/include/test.h -check-prefix=CHECK-OLD-TEST-EMPTY -allow-empty %s
+//
+// RUN: cp %S/Inputs/test.h  %T/clang-move/include
+// RUN: cp %S/Inputs/test.cpp %T/clang-move/src
+// RUN: cd %T/clang-move/build
+// RUN: clang-move -names="a::Foo" -new_cc=%T/clang-move/new_test.cpp -new_header=%T/clang-move/new_test.h -old_cc=%T/clang-move/src/test.cpp -old_header=%T/clang-move/include/test.h %T/clang-move/src/test.cpp
+// RUN: FileCheck -input-file=%T/clang-move/new_test.cpp -check-prefix=CHECK-NEW-TEST-CPP %s
+// RUN: FileCheck -input-file=%T/clang-move/new_test.h -check-prefix=CHECK-NEW-TEST-H %s
+// RUN: FileCheck -input-file=%T/clang-move/src/test.cpp -check-prefix=CHECK-OLD-TEST-EMPTY -allow-empty %s
+// RUN: FileCheck -input-file=%T/clang-move/include/test.h -check-prefix=CHECK-OLD-TEST-EMPTY -allow-empty %s
+//
+//
+// CHECK-NEW-TEST-H: #ifndef TEST_H // comment 1
+// CHECK-NEW-TEST-H: #define TEST_H
+// CHECK-NEW-TEST-H: namespace a {
+// CHECK-NEW-TEST-H: class Foo {
+// CHECK-NEW-TEST-H: public:
+// CHECK-NEW-TEST-H:   int f();
+// CHECK-NEW-TEST-H:   int f2(int a, int b);
+// CHECK-NEW-TEST-H: };
+// CHECK-NEW-TEST-H: } // namespace a
+// CHECK-NEW-TEST-H: #endif // TEST_H
+//
+// CHECK-NEW-TEST-CPP: #include "{{.*}}new_test.h"
+// CHECK-NEW-TEST-CPP: #include "test2.h"
+// CHECK-NEW-TEST-CPP: namespace a {
+// CHECK-NEW-TEST-CPP: int Foo::f() { return 0; }
+// CHECK-NEW-TEST-CPP: int Foo::f2(int a, int b) { return a + b; }
+// CHECK-NEW-TEST-CPP: } // namespace a
+//
+// CHECK-OLD-TEST-EMPTY: {{^}}{{$}}
diff --git a/test/clang-move/move-enum-decl.cpp b/test/clang-move/move-enum-decl.cpp
new file mode 100644 (file)
index 0000000..42f6f99
--- /dev/null
@@ -0,0 +1,44 @@
+// RUN: mkdir -p %T/move-enum
+// RUN: cp %S/Inputs/enum.h  %T/move-enum/enum.h
+// RUN: echo '#include "enum.h"' > %T/move-enum/enum.cpp
+// RUN: cd %T/move-enum
+//
+// -----------------------------------------------------------------------------
+// Test moving enum declarations.
+// -----------------------------------------------------------------------------
+// RUN: clang-move -names="a::E1" -new_cc=%T/move-enum/new_test.cpp -new_header=%T/move-enum/new_test.h -old_cc=%T/move-enum/enum.cpp -old_header=%T/move-enum/enum.h %T/move-enum/enum.cpp -- -std=c++11
+// RUN: FileCheck -input-file=%T/move-enum/new_test.h -check-prefix=CHECK-NEW-TEST-H-CASE1 %s
+// RUN: FileCheck -input-file=%T/move-enum/enum.h -check-prefix=CHECK-OLD-TEST-H-CASE1 %s
+//
+// CHECK-NEW-TEST-H-CASE1: namespace a {
+// CHECK-NEW-TEST-H-CASE1-NEXT: enum E1 { Green, Red };
+// CHECK-NEW-TEST-H-CASE1-NEXT: }
+
+// CHECK-OLD-TEST-H-CASE1-NOT: enum E1 { Green, Red };
+
+
+// -----------------------------------------------------------------------------
+// Test moving scoped enum declarations.
+// -----------------------------------------------------------------------------
+// RUN: cp %S/Inputs/enum.h  %T/move-enum/enum.h
+// RUN: echo '#include "enum.h"' > %T/move-enum/enum.cpp
+// RUN: clang-move -names="a::E2" -new_cc=%T/move-enum/new_test.cpp -new_header=%T/move-enum/new_test.h -old_cc=%T/move-enum/enum.cpp -old_header=%T/move-enum/enum.h %T/move-enum/enum.cpp -- -std=c++11
+// RUN: FileCheck -input-file=%T/move-enum/new_test.h -check-prefix=CHECK-NEW-TEST-H-CASE2 %s
+// RUN: FileCheck -input-file=%T/move-enum/enum.h -check-prefix=CHECK-OLD-TEST-H-CASE2 %s
+
+// CHECK-NEW-TEST-H-CASE2: namespace a {
+// CHECK-NEW-TEST-H-CASE2-NEXT: enum class E2 { Yellow };
+// CHECK-NEW-TEST-H-CASE2-NEXT: }
+
+// CHECK-OLD-TEST-H-CASE2-NOT: enum class E2 { Yellow };
+
+
+// -----------------------------------------------------------------------------
+// Test not moving class-insided enum declarations.
+// -----------------------------------------------------------------------------
+// RUN: cp %S/Inputs/enum.h  %T/move-enum/enum.h
+// RUN: echo '#include "enum.h"' > %T/move-enum/enum.cpp
+// RUN: clang-move -names="a::C::E3" -new_cc=%T/move-enum/new_test.cpp -new_header=%T/move-enum/new_test.h -old_cc=%T/move-enum/enum.cpp -old_header=%T/move-enum/enum.h %T/move-enum/enum.cpp -- -std=c++11
+// RUN: FileCheck -input-file=%T/move-enum/new_test.h -allow-empty -check-prefix=CHECK-EMPTY %s
+
+// CHECK-EMPTY: {{^}}{{$}}
diff --git a/test/clang-move/move-function.cpp b/test/clang-move/move-function.cpp
new file mode 100644 (file)
index 0000000..ceda2d5
--- /dev/null
@@ -0,0 +1,67 @@
+// RUN: mkdir -p %T/move-function
+// RUN: cp %S/Inputs/function_test*  %T/move-function
+// RUN: cd %T/move-function
+// RUN: clang-move -names="g" -new_header=%T/move-function/new_function_test.h -old_header=../move-function/function_test.h %T/move-function/function_test.cpp --
+// RUN: FileCheck -input-file=%T/move-function/new_function_test.h -check-prefix=CHECK-NEW-TEST-H-CASE1 %s
+//
+// CHECK-NEW-TEST-H-CASE1: #ifndef {{.*}}NEW_FUNCTION_TEST_H
+// CHECK-NEW-TEST-H-CASE1: #define {{.*}}NEW_FUNCTION_TEST_H
+// CHECK-NEW-TEST-H-CASE1: {{[[:space:]]+}}
+// CHECK-NEW-TEST-H-CASE1: inline int g() { return 0; }
+// CHECK-NEW-TEST-H-CASE1: {{[[:space:]]+}}
+// CHECK-NEW-TEST-H-CASE1: #endif // {{.*}}NEW_FUNCTION_TEST_H
+//
+// RUN: cp %S/Inputs/function_test*  %T/move-function
+// RUN: clang-move -names="h" -new_header=%T/move-function/new_function_test.h -old_header=../move-function/function_test.h %T/move-function/function_test.cpp --
+// RUN: FileCheck -input-file=%T/move-function/new_function_test.h -check-prefix=CHECK-NEW-TEST-H-CASE2 %s
+//
+// CHECK-NEW-TEST-H-CASE2: #ifndef {{.*}}NEW_FUNCTION_TEST_H
+// CHECK-NEW-TEST-H-CASE2: #define {{.*}}NEW_FUNCTION_TEST_H
+// CHECK-NEW-TEST-H-CASE2: {{[[:space:]]+}}
+// CHECK-NEW-TEST-H-CASE2: template <typename T> void h(T t) {}
+// CHECK-NEW-TEST-H-CASE2: {{[[:space:]]+}}
+// CHECK-NEW-TEST-H-CASE2: template <> void h(int t) {}
+// CHECK-NEW-TEST-H-CASE2: {{[[:space:]]+}}
+// CHECK-NEW-TEST-H-CASE2: #endif // {{.*}}NEW_FUNCTION_TEST_H
+//
+// RUN: cp %S/Inputs/function_test*  %T/move-function
+// RUN: clang-move -names="f" -new_header=%T/move-function/new_function_test.h -new_cc=%T/move-function/new_function_test.cpp -old_header=../move-function/function_test.h -old_cc=../move-function/function_test.cpp %T/move-function/function_test.cpp --
+// RUN: FileCheck -input-file=%T/move-function/new_function_test.h -check-prefix=CHECK-NEW-TEST-H-CASE3 %s
+// RUN: FileCheck -input-file=%T/move-function/new_function_test.cpp -check-prefix=CHECK-NEW-TEST-CPP-CASE3 %s
+//
+// CHECK-NEW-TEST-H-CASE3: #ifndef {{.*}}NEW_FUNCTION_TEST_H
+// CHECK-NEW-TEST-H-CASE3: #define {{.*}}NEW_FUNCTION_TEST_H
+// CHECK-NEW-TEST-H-CASE3: {{[[:space:]]+}}
+// CHECK-NEW-TEST-H-CASE3: void f();
+// CHECK-NEW-TEST-H-CASE3: {{[[:space:]]+}}
+// CHECK-NEW-TEST-H-CASE3: #endif // {{.*}}NEW_FUNCTION_TEST_H
+// CHECK-NEW-TEST-CPP-CASE3: #include "{{.*}}new_function_test.h"
+// CHECK-NEW-TEST-CPP-CASE3: {{[[:space:]]+}}
+// CHECK-NEW-TEST-CPP-CASE3: void f() {}
+//
+// RUN: cp %S/Inputs/function_test*  %T/move-function
+// RUN: clang-move -names="A::f" -new_header=%T/move-function/new_function_test.h -new_cc=%T/move-function/new_function_test.cpp -old_header=../move-function/function_test.h -old_cc=../move-function/function_test.cpp %T/move-function/function_test.cpp -dump_result -- | FileCheck %s -check-prefix=CHECK-EMPTY
+//
+// CHECK-EMPTY: [{{[[:space:]]*}}]
+//
+// RUN: cp %S/Inputs/function_test*  %T/move-function
+// RUN: clang-move -names="f,A" -new_header=%T/move-function/new_function_test.h -new_cc=%T/move-function/new_function_test.cpp -old_header=../move-function/function_test.h -old_cc=../move-function/function_test.cpp %T/move-function/function_test.cpp --
+// RUN: FileCheck -input-file=%T/move-function/new_function_test.h -check-prefix=CHECK-NEW-TEST-H-CASE4 %s
+// RUN: FileCheck -input-file=%T/move-function/new_function_test.cpp -check-prefix=CHECK-NEW-TEST-CPP-CASE4 %s
+
+// CHECK-NEW-TEST-H-CASE4: #ifndef {{.*}}NEW_FUNCTION_TEST_H
+// CHECK-NEW-TEST-H-CASE4: #define {{.*}}NEW_FUNCTION_TEST_H
+// CHECK-NEW-TEST-H-CASE4: {{[[:space:]]+}}
+// CHECK-NEW-TEST-H-CASE4: void f();
+// CHECK-NEW-TEST-H-CASE4: {{[[:space:]]+}}
+// CHECK-NEW-TEST-H-CASE4: class A {
+// CHECK-NEW-TEST-H-CASE4: public:
+// CHECK-NEW-TEST-H-CASE4:   void f();
+// CHECK-NEW-TEST-H-CASE4: };
+// CHECK-NEW-TEST-H-CASE4: {{[[:space:]]+}}
+// CHECK-NEW-TEST-H-CASE4: #endif // {{.*}}NEW_FUNCTION_TEST_H
+// CHECK-NEW-TEST-CPP-CASE4: #include "{{.*}}new_function_test.h"
+// CHECK-NEW-TEST-CPP-CASE4: {{[[:space:]]+}}
+// CHECK-NEW-TEST-CPP-CASE4: void f() {}
+// CHECK-NEW-TEST-CPP-CASE4: {{[[:space:]]+}}
+// CHECK-NEW-TEST-CPP-CASE4: void A::f() {}
diff --git a/test/clang-move/move-multiple-classes.cpp b/test/clang-move/move-multiple-classes.cpp
new file mode 100644 (file)
index 0000000..821d567
--- /dev/null
@@ -0,0 +1,104 @@
+// RUN: mkdir -p %T/move-multiple-classes
+// RUN: cp %S/Inputs/multiple_class_test*  %T/move-multiple-classes/
+// RUN: cd %T/move-multiple-classes
+// RUN: clang-move -names="c::EnclosingMove5::Nested" -new_cc=%T/move-multiple-classes/new_multiple_class_test.cpp -new_header=%T/move-multiple-classes/new_multiple_class_test.h -old_cc=%T/move-multiple-classes/multiple_class_test.cpp -old_header=../move-multiple-classes/multiple_class_test.h -dump_result %T/move-multiple-classes/multiple_class_test.cpp -- -std=c++11| FileCheck %s -check-prefix=CHECK-EMPTY
+// RUN: clang-move -names="a::Move1, b::Move2,c::Move3,c::Move4,c::EnclosingMove5" -new_cc=%T/move-multiple-classes/new_multiple_class_test.cpp -new_header=%T/move-multiple-classes/new_multiple_class_test.h -old_cc=%T/move-multiple-classes/multiple_class_test.cpp -old_header=../move-multiple-classes/multiple_class_test.h %T/move-multiple-classes/multiple_class_test.cpp -- -std=c++11
+// RUN: FileCheck -input-file=%T/move-multiple-classes/new_multiple_class_test.cpp -check-prefix=CHECK-NEW-TEST-CPP %s
+// RUN: FileCheck -input-file=%T/move-multiple-classes/new_multiple_class_test.h -check-prefix=CHECK-NEW-TEST-H %s
+// RUN: FileCheck -input-file=%T/move-multiple-classes/multiple_class_test.cpp -check-prefix=CHECK-OLD-TEST-CPP %s
+// RUN: FileCheck -input-file=%T/move-multiple-classes/multiple_class_test.h -check-prefix=CHECK-OLD-TEST-H %s
+//
+// CHECK-EMPTY: [{{[[:space:]]*}}]
+//
+// CHECK-OLD-TEST-H: namespace c {
+// CHECK-OLD-TEST-H: class NoMove {
+// CHECK-OLD-TEST-H: public:
+// CHECK-OLD-TEST-H:   int f();
+// CHECK-OLD-TEST-H: };
+// CHECK-OLD-TEST-H: } // namespace c
+
+// CHECK-OLD-TEST-CPP: #include "{{.*}}multiple_class_test.h"
+// CHECK-OLD-TEST-CPP: using a::Move1;
+// CHECK-OLD-TEST-CPP: using namespace a;
+// CHECK-OLD-TEST-CPP: using A = a::Move1;
+// CHECK-OLD-TEST-CPP: static int g = 0;
+// CHECK-OLD-TEST-CPP: namespace {
+// CHECK-OLD-TEST-CPP: using a::Move1;
+// CHECK-OLD-TEST-CPP: using namespace a;
+// CHECK-OLD-TEST-CPP: } // namespace
+// CHECK-OLD-TEST-CPP: namespace b {
+// CHECK-OLD-TEST-CPP: using a::Move1;
+// CHECK-OLD-TEST-CPP: using namespace a;
+// CHECK-OLD-TEST-CPP: using T = a::Move1;
+// CHECK-OLD-TEST-CPP: } // namespace b
+// CHECK-OLD-TEST-CPP: namespace c {
+// CHECK-OLD-TEST-CPP: int NoMove::f() {
+// CHECK-OLD-TEST-CPP:   static int F = 0;
+// CHECK-OLD-TEST-CPP:   return g;
+// CHECK-OLD-TEST-CPP: }
+// CHECK-OLD-TEST-CPP: } // namespace c
+
+// CHECK-NEW-TEST-H: #ifndef {{.*}}NEW_MULTIPLE_CLASS_TEST_H
+// CHECK-NEW-TEST-H: #define {{.*}}NEW_MULTIPLE_CLASS_TEST_H
+// CHECK-NEW-TEST-H: namespace a {
+// CHECK-NEW-TEST-H: class Move1 {
+// CHECK-NEW-TEST-H: public:
+// CHECK-NEW-TEST-H:   int f();
+// CHECK-NEW-TEST-H: };
+// CHECK-NEW-TEST-H: } // namespace a
+// CHECK-NEW-TEST-H: namespace b {
+// CHECK-NEW-TEST-H: class Move2 {
+// CHECK-NEW-TEST-H: public:
+// CHECK-NEW-TEST-H:   int f();
+// CHECK-NEW-TEST-H: };
+// CHECK-NEW-TEST-H: } // namespace b
+// CHECK-NEW-TEST-H: namespace c {
+// CHECK-NEW-TEST-H: class Move3 {
+// CHECK-NEW-TEST-H: public:
+// CHECK-NEW-TEST-H:   int f();
+// CHECK-NEW-TEST-H: };
+// CHECK-NEW-TEST-H: class Move4 {
+// CHECK-NEW-TEST-H: public:
+// CHECK-NEW-TEST-H:   int f();
+// CHECK-NEW-TEST-H: };
+// CHECK-NEW-TEST-H: class EnclosingMove5 {
+// CHECK-NEW-TEST-H: public:
+// CHECK-NEW-TEST-H:   class Nested {
+// CHECK-NEW-TEST-H:     int f();
+// CHECK-NEW-TEST-H:     static int b;
+// CHECK-NEW-TEST-H:   };
+// CHECK-NEW-TEST-H:   static int a;
+// CHECK-NEW-TEST-H: };
+// CHECK-NEW-TEST-H: } // namespace c
+// CHECK-NEW-TEST-H: #endif // {{.*}}NEW_MULTIPLE_CLASS_TEST_H
+
+// CHECK-NEW-TEST-CPP: #include "{{.*}}new_multiple_class_test.h"
+// CHECK-NEW-TEST-CPP: using a::Move1;
+// CHECK-NEW-TEST-CPP: using namespace a;
+// CHECK-NEW-TEST-CPP: using A = a::Move1;
+// CHECK-NEW-TEST-CPP: static int g = 0;
+// CHECK-NEW-TEST-CPP: namespace a {
+// CHECK-NEW-TEST-CPP: int Move1::f() { return 0; }
+// CHECK-NEW-TEST-CPP: } // namespace a
+// CHECK-NEW-TEST-CPP: namespace {
+// CHECK-NEW-TEST-CPP: using a::Move1;
+// CHECK-NEW-TEST-CPP: using namespace a;
+// CHECK-NEW-TEST-CPP: static int k = 0;
+// CHECK-NEW-TEST-CPP: } // namespace
+// CHECK-NEW-TEST-CPP: namespace b {
+// CHECK-NEW-TEST-CPP: using a::Move1;
+// CHECK-NEW-TEST-CPP: using namespace a;
+// CHECK-NEW-TEST-CPP: using T = a::Move1;
+// CHECK-NEW-TEST-CPP: int Move2::f() { return 0; }
+// CHECK-NEW-TEST-CPP: } // namespace b
+// CHECK-NEW-TEST-CPP: namespace c {
+// CHECK-NEW-TEST-CPP: int Move3::f() {
+// CHECK-NEW-TEST-CPP:   using a::Move1;
+// CHECK-NEW-TEST-CPP:   using namespace b;
+// CHECK-NEW-TEST-CPP:   return 0;
+// CHECK-NEW-TEST-CPP: }
+// CHECK-NEW-TEST-CPP: int Move4::f() { return k; }
+// CHECK-NEW-TEST-CPP: int EnclosingMove5::a = 1;
+// CHECK-NEW-TEST-CPP: int EnclosingMove5::Nested::f() { return g; }
+// CHECK-NEW-TEST-CPP: int EnclosingMove5::Nested::b = 1;
+// CHECK-NEW-TEST-CPP: } // namespace c
diff --git a/test/clang-move/move-template-class.cpp b/test/clang-move/move-template-class.cpp
new file mode 100644 (file)
index 0000000..1a6a60b
--- /dev/null
@@ -0,0 +1,86 @@
+// RUN: mkdir -p %T/move-template-class
+// RUN: cp %S/Inputs/template_class_test*  %T/move-template-class
+// RUN: cd %T/move-template-class
+// RUN: clang-move -names="A,B" -new_cc=%T/move-template-class/new_template_class_test.cpp -new_header=%T/move-template-class/new_template_class_test.h -old_cc=%T/move-template-class/template_class_test.cpp -old_header=../move-template-class/template_class_test.h %T/move-template-class/template_class_test.cpp --
+// RUN: FileCheck -input-file=%T/move-template-class/template_class_test.cpp -check-prefix=CHECK-OLD-TEST-EMPTY -allow-empty %s
+// RUN: FileCheck -input-file=%T/move-template-class/template_class_test.h -check-prefix=CHECK-OLD-TEST-EMPTY -allow-empty %s
+// RUN: FileCheck -input-file=%T/move-template-class/new_template_class_test.cpp -check-prefix=CHECK-NEW-TEST-CPP-CASE1 %s
+// RUN: FileCheck -input-file=%T/move-template-class/new_template_class_test.h -check-prefix=CHECK-NEW-TEST-H-CASE1 %s
+//
+// RUN: cp %S/Inputs/template_class_test*  %T/move-template-class
+// RUN: clang-move -names="A" -new_cc=%T/move-template-class/new_template_class_test.cpp -new_header=%T/move-template-class/new_template_class_test.h -old_cc=%T/move-template-class/template_class_test.cpp -old_header=../move-template-class/template_class_test.h %T/move-template-class/template_class_test.cpp --
+// RUN: FileCheck -input-file=%T/move-template-class/template_class_test.h -check-prefix=CHECK-OLD-TEST-H-CASE2 %s
+// RUN: FileCheck -input-file=%T/move-template-class/template_class_test.cpp -check-prefix=CHECK-OLD-TEST-CPP-CASE2 %s
+// RUN: FileCheck -input-file=%T/move-template-class/new_template_class_test.h -check-prefix=CHECK-NEW-TEST-H-CASE2 %s
+// RUN: FileCheck -input-file=%T/move-template-class/new_template_class_test.cpp -check-prefix=CHECK-NEW-TEST-CPP-CASE2 %s
+//
+//
+// CHECK-OLD-TEST-EMPTY: {{^}}{{$}}
+//
+// CHECK-NEW-TEST-H-CASE1: #ifndef TEMPLATE_CLASS_TEST_H // comment 1
+// CHECK-NEW-TEST-H-CASE1: #define TEMPLATE_CLASS_TEST_H
+// CHECK-NEW-TEST-H-CASE1: template <typename T>
+// CHECK-NEW-TEST-H-CASE1: class A {
+// CHECK-NEW-TEST-H-CASE1:  public:
+// CHECK-NEW-TEST-H-CASE1:   void f();
+// CHECK-NEW-TEST-H-CASE1:   void g();
+// CHECK-NEW-TEST-H-CASE1:   template <typename U> void h();
+// CHECK-NEW-TEST-H-CASE1:   template <typename U> void k();
+// CHECK-NEW-TEST-H-CASE1:   static int b;
+// CHECK-NEW-TEST-H-CASE1:   static int c;
+// CHECK-NEW-TEST-H-CASE1: };
+// CHECK-NEW-TEST-H-CASE1: template <typename T>
+// CHECK-NEW-TEST-H-CASE1: void A<T>::f() {}
+// CHECK-NEW-TEST-H-CASE1: template <typename T>
+// CHECK-NEW-TEST-H-CASE1: template <typename U>
+// CHECK-NEW-TEST-H-CASE1: void A<T>::h() {}
+// CHECK-NEW-TEST-H-CASE1: template <typename T>
+// CHECK-NEW-TEST-H-CASE1: int A<T>::b = 2;
+// CHECK-NEW-TEST-H-CASE1: class B {
+// CHECK-NEW-TEST-H-CASE1:  public:
+// CHECK-NEW-TEST-H-CASE1:   void f();
+// CHECK-NEW-TEST-H-CASE1: };
+// CHECK-NEW-TEST-H-CASE1: #endif // TEMPLATE_CLASS_TEST_H
+//
+// CHECK-NEW-TEST-CPP-CASE1: #include "{{.*}}new_template_class_test.h"
+// CHECK-NEW-TEST-CPP-CASE1: template <typename T>
+// CHECK-NEW-TEST-CPP-CASE1: void A<T>::g() {}
+// CHECK-NEW-TEST-CPP-CASE1: template <typename T>
+// CHECK-NEW-TEST-CPP-CASE1: template <typename U>
+// CHECK-NEW-TEST-CPP-CASE1: void A<T>::k() {}
+// CHECK-NEW-TEST-CPP-CASE1: template <typename T>
+// CHECK-NEW-TEST-CPP-CASE1: int A<T>::c = 2;
+// CHECK-NEW-TEST-CPP-CASE1: void B::f() {}
+//
+// CHECK-OLD-TEST-H-CASE2: #ifndef TEMPLATE_CLASS_TEST_H // comment 1
+// CHECK-OLD-TEST-H-CASE2: #define TEMPLATE_CLASS_TEST_H
+// CHECK-OLD-TEST-H-CASE2: class B {
+// CHECK-OLD-TEST-H-CASE2:  public:
+// CHECK-OLD-TEST-H-CASE2:   void f();
+// CHECK-OLD-TEST-H-CASE2: };
+// CHECK-OLD-TEST-H-CASE2: #endif // TEMPLATE_CLASS_TEST_H
+//
+// CHECK-OLD-TEST-CPP-CASE2: #include "template_class_test.h"
+// CHECK-OLD-TEST-CPP-CASE2:  void B::f() {}
+//
+// CHECK-NEW-TEST-H-CASE2: #ifndef {{.*}}NEW_TEMPLATE_CLASS_TEST_H
+// CHECK-NEW-TEST-H-CASE2: #define {{.*}}NEW_TEMPLATE_CLASS_TEST_H
+// CHECK-NEW-TEST-H-CASE2: template <typename T>
+// CHECK-NEW-TEST-H-CASE2: class A {
+// CHECK-NEW-TEST-H-CASE2:  public:
+// CHECK-NEW-TEST-H-CASE2:   void f();
+// CHECK-NEW-TEST-H-CASE2:   void g();
+// CHECK-NEW-TEST-H-CASE2:   template <typename U> void h();
+// CHECK-NEW-TEST-H-CASE2:   template <typename U> void k();
+// CHECK-NEW-TEST-H-CASE2:   static int b;
+// CHECK-NEW-TEST-H-CASE2:   static int c;
+// CHECK-NEW-TEST-H-CASE2: };
+// CHECK-NEW-TEST-H-CASE2: template <typename T> void A<T>::f() {}
+// CHECK-NEW-TEST-H-CASE2: template <typename T> template <typename U> void A<T>::h() {}
+// CHECK-NEW-TEST-H-CASE2: template <typename T> int A<T>::b = 2;
+// CHECK-NEW-TEST-H-CASE2: #endif // {{.*}}NEW_TEMPLATE_CLASS_TEST_H
+//
+// CHECK-NEW-TEST-CPP-CASE2: #include "{{.*}}new_template_class_test.h"
+// CHECK-NEW-TEST-CPP-CASE2: template <typename T> void A<T>::g() {}
+// CHECK-NEW-TEST-CPP-CASE2: template <typename T> template <typename U> void A<T>::k() {}
+// CHECK-NEW-TEST-CPP-CASE2: template <typename T> int A<T>::c = 2;
diff --git a/test/clang-move/move-type-alias.cpp b/test/clang-move/move-type-alias.cpp
new file mode 100644 (file)
index 0000000..ab70237
--- /dev/null
@@ -0,0 +1,52 @@
+// RUN: mkdir -p %T/move-type-alias
+// RUN: cp %S/Inputs/type_alias.h  %T/move-type-alias/type_alias.h
+// RUN: echo '#include "type_alias.h"' > %T/move-type-alias/type_alias.cpp
+// RUN: cd %T/move-type-alias
+//
+// -----------------------------------------------------------------------------
+// Test moving typedef declarations.
+// -----------------------------------------------------------------------------
+// RUN: clang-move -names="Int1" -new_cc=%T/move-type-alias/new_test.cpp -new_header=%T/move-type-alias/new_test.h -old_cc=%T/move-type-alias/type_alias.cpp -old_header=%T/move-type-alias/type_alias.h %T/move-type-alias/type_alias.cpp -- -std=c++11
+// RUN: FileCheck -input-file=%T/move-type-alias/new_test.h -check-prefix=CHECK-NEW-TEST-H-CASE1 %s
+// RUN: FileCheck -input-file=%T/move-type-alias/type_alias.h -check-prefix=CHECK-OLD-TEST-H-CASE1 %s
+
+// CHECK-NEW-TEST-H-CASE1: typedef int Int1;
+
+// CHECK-OLD-TEST-H-CASE1-NOT: typedef int Int1;
+
+
+// -----------------------------------------------------------------------------
+// Test moving type alias declarations.
+// -----------------------------------------------------------------------------
+// RUN: cp %S/Inputs/type_alias.h  %T/move-type-alias/type_alias.h
+// RUN: echo '#include "type_alias.h"' > %T/move-type-alias/type_alias.cpp
+// RUN: clang-move -names="Int2" -new_cc=%T/move-type-alias/new_test.cpp -new_header=%T/move-type-alias/new_test.h -old_cc=%T/move-type-alias/type_alias.cpp -old_header=%T/move-type-alias/type_alias.h %T/move-type-alias/type_alias.cpp -- -std=c++11
+// RUN: FileCheck -input-file=%T/move-type-alias/new_test.h -check-prefix=CHECK-NEW-TEST-H-CASE2 %s
+// RUN: FileCheck -input-file=%T/move-type-alias/type_alias.h -check-prefix=CHECK-OLD-TEST-H-CASE2 %s
+
+// CHECK-NEW-TEST-H-CASE2: using Int2 = int;
+
+// CHECK-OLD-TEST-H-CASE2-NOT: using Int2 = int;
+
+
+// -----------------------------------------------------------------------------
+// Test moving template type alias declarations.
+// -----------------------------------------------------------------------------
+// RUN: cp %S/Inputs/type_alias.h  %T/move-type-alias/type_alias.h
+// RUN: echo '#include "type_alias.h"' > %T/move-type-alias/type_alias.cpp
+// RUN: clang-move -names="B" -new_cc=%T/move-type-alias/new_test.cpp -new_header=%T/move-type-alias/new_test.h -old_cc=%T/move-type-alias/type_alias.cpp -old_header=%T/move-type-alias/type_alias.h %T/move-type-alias/type_alias.cpp -- -std=c++11
+// RUN: FileCheck -input-file=%T/move-type-alias/new_test.h -check-prefix=CHECK-OLD-TEST-H-CASE3 %s
+
+// CHECK-NEW-TEST-H-CASE3: template<class T> using B = A<T>;
+// CHECK-OLD-TEST-H-CASE3-NOT: template<class T> using B = A<T>;
+
+
+// -----------------------------------------------------------------------------
+// Test not moving class-insided typedef declarations.
+// -----------------------------------------------------------------------------
+// RUN: cp %S/Inputs/type_alias.h  %T/move-type-alias/type_alias.h
+// RUN: echo '#include "type_alias.h"' > %T/move-type-alias/type_alias.cpp
+// RUN: clang-move -names="C::Int3" -new_cc=%T/move-type-alias/new_test.cpp -new_header=%T/move-type-alias/new_test.h -old_cc=%T/move-type-alias/type_alias.cpp -old_header=%T/move-type-alias/type_alias.h %T/move-type-alias/type_alias.cpp -- -std=c++11
+// RUN: FileCheck -input-file=%T/move-type-alias/new_test.h -allow-empty -check-prefix=CHECK-EMPTY %s
+
+// CHECK-EMPTY: {{^}}{{$}}
diff --git a/test/clang-move/move-used-helper-decls.cpp b/test/clang-move/move-used-helper-decls.cpp
new file mode 100644 (file)
index 0000000..45bd4be
--- /dev/null
@@ -0,0 +1,438 @@
+// RUN: mkdir -p %T/used-helper-decls
+// RUN: cp %S/Inputs/helper_decls_test*  %T/used-helper-decls/
+// RUN: cd %T/used-helper-decls
+
+// ----------------------------------------------------------------------------
+// Test moving used helper function and its transively used functions.
+// ----------------------------------------------------------------------------
+// RUN: clang-move -names="a::Class1" -new_cc=%T/used-helper-decls/new_helper_decls_test.cpp -new_header=%T/used-helper-decls/new_helper_decls_test.h -old_cc=%T/used-helper-decls/helper_decls_test.cpp -old_header=../used-helper-decls/helper_decls_test.h %T/used-helper-decls/helper_decls_test.cpp -- -std=c++11
+// RUN: FileCheck -input-file=%T/used-helper-decls/new_helper_decls_test.cpp -check-prefix=CHECK-NEW-CLASS1-CPP %s
+// RUN: FileCheck -input-file=%T/used-helper-decls/helper_decls_test.cpp -check-prefix=CHECK-OLD-CLASS1-CPP %s
+
+// CHECK-NEW-CLASS1-CPP: #include "{{.*}}new_helper_decls_test.h"
+// CHECK-NEW-CLASS1-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CLASS1-CPP-NEXT: namespace {
+// CHECK-NEW-CLASS1-CPP-NEXT: void HelperFun1() {}
+// CHECK-NEW-CLASS1-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CLASS1-CPP-NEXT: void HelperFun2() { HelperFun1(); }
+// CHECK-NEW-CLASS1-CPP-NEXT: } // namespace
+// CHECK-NEW-CLASS1-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CLASS1-CPP-NEXT: namespace a {
+// CHECK-NEW-CLASS1-CPP-NEXT: void Class1::f() { HelperFun2(); }
+// CHECK-NEW-CLASS1-CPP-NEXT: } // namespace a
+//
+// CHECK-OLD-CLASS1-CPP: void HelperFun1() {}
+// CHECK-OLD-CLASS1-CPP-NOT: void HelperFun2() { HelperFun1(); }
+// CHECK-OLD-CLASS1-CPP-NOT: void Class1::f() { HelperFun2(); }
+// CHECK-OLD-CLASS1-CPP: void Class2::f() {
+// CHECK-OLD-CLASS1-CPP:   HelperFun1();
+
+
+// ----------------------------------------------------------------------------
+// Test moving used helper function and its transively used static variables.
+// ----------------------------------------------------------------------------
+// RUN: cp %S/Inputs/helper_decls_test*  %T/used-helper-decls/
+// RUN: clang-move -names="a::Class2" -new_cc=%T/used-helper-decls/new_helper_decls_test.cpp -new_header=%T/used-helper-decls/new_helper_decls_test.h -old_cc=%T/used-helper-decls/helper_decls_test.cpp -old_header=../used-helper-decls/helper_decls_test.h %T/used-helper-decls/helper_decls_test.cpp -- -std=c++11
+// RUN: FileCheck -input-file=%T/used-helper-decls/new_helper_decls_test.cpp -check-prefix=CHECK-NEW-CLASS2-CPP %s
+// RUN: FileCheck -input-file=%T/used-helper-decls/helper_decls_test.cpp -check-prefix=CHECK-OLD-CLASS2-CPP %s
+
+// CHECK-NEW-CLASS2-CPP: #include "{{.*}}new_helper_decls_test.h"
+// CHECK-NEW-CLASS2-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CLASS2-CPP-NEXT: namespace {
+// CHECK-NEW-CLASS2-CPP-NEXT: void HelperFun1() {}
+// CHECK-NEW-CLASS2-CPP-NEXT: } // namespace
+// CHECK-NEW-CLASS2-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CLASS2-CPP-NEXT: static const int K2 = 2;
+// CHECK-NEW-CLASS2-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CLASS2-CPP-NEXT: static void HelperFun3() { K2; }
+// CHECK-NEW-CLASS2-CPP-NEXT: namespace a {
+// CHECK-NEW-CLASS2-CPP-NEXT: void Class2::f() {
+// CHECK-NEW-CLASS2-CPP-NEXT:   HelperFun1();
+// CHECK-NEW-CLASS2-CPP-NEXT:   HelperFun3();
+// CHECK-NEW-CLASS2-CPP-NEXT: }
+// CHECK-NEW-CLASS2-CPP-NEXT: } // namespace a
+
+// CHECK-OLD-CLASS2-CPP: void HelperFun1() {}
+// CHECK-OLD-CLASS2-CPP: void HelperFun2() { HelperFun1(); }
+// CHECK-OLD-CLASS2-CPP: const int K1 = 1;
+// CHECK-OLD-CLASS2-CPP: static const int K2 = 2;
+// CHECK-OLD-CLASS2-CPP-NOT: static void HelperFun3() { K2; }
+// CHECK-OLD-CLASS2-CPP-NOT: void Class2::f() {
+// CHECK-OLD-CLASS2-CPP-NOT:   HelperFun1();
+// CHECK-OLD-CLASS2-CPP-NOT:   HelperFun3();
+// CHECK-OLD-CLASS2-CPP: void Class5::f() {
+// CHECK-OLD-CLASS2-CPP-NEXT: int Result = K1 + K2 + K3;
+
+
+// ----------------------------------------------------------------------------
+// Test using a static member variable of a helper class.
+// ----------------------------------------------------------------------------
+// RUN: cp %S/Inputs/helper_decls_test*  %T/used-helper-decls/
+// RUN: clang-move -names="a::Class3" -new_cc=%T/used-helper-decls/new_helper_decls_test.cpp -new_header=%T/used-helper-decls/new_helper_decls_test.h -old_cc=%T/used-helper-decls/helper_decls_test.cpp -old_header=../used-helper-decls/helper_decls_test.h %T/used-helper-decls/helper_decls_test.cpp -- -std=c++11
+// RUN: FileCheck -input-file=%T/used-helper-decls/new_helper_decls_test.cpp -check-prefix=CHECK-NEW-CLASS3-CPP %s
+// RUN: FileCheck -input-file=%T/used-helper-decls/helper_decls_test.cpp -check-prefix=CHECK-OLD-CLASS3-CPP %s
+
+// CHECK-NEW-CLASS3-CPP: #include "{{.*}}new_helper_decls_test.h"
+// CHECK-NEW-CLASS3-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CLASS3-CPP-NEXT: namespace {
+// CHECK-NEW-CLASS3-CPP-NEXT: class HelperC1 {
+// CHECK-NEW-CLASS3-CPP-NEXT: public:
+// CHECK-NEW-CLASS3-CPP-NEXT:   static int I;
+// CHECK-NEW-CLASS3-CPP-NEXT: };
+// CHECK-NEW-CLASS3-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CLASS3-CPP-NEXT: int HelperC1::I = 0;
+// CHECK-NEW-CLASS3-CPP-NEXT: } // namespace
+// CHECK-NEW-CLASS3-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CLASS3-CPP-NEXT: namespace a {
+// CHECK-NEW-CLASS3-CPP-NEXT: void Class3::f() { HelperC1::I; }
+// CHECK-NEW-CLASS3-CPP-NEXT: } // namespace a
+
+// CHECK-OLD-CLASS3-CPP: namespace {
+// CHECK-OLD-CLASS3-CPP-NOT: class HelperC1 {
+// CHECK-OLD-CLASS3-CPP-NOT: public:
+// CHECK-OLD-CLASS3-CPP-NOT:   static int I;
+// CHECK-OLD-CLASS3-CPP-NOT: };
+// CHECK-OLD-CLASS3-CPP-NOT: int HelperC1::I = 0;
+// CHECK-OLD-CLASS3-CPP: class HelperC2 {};
+
+
+// ----------------------------------------------------------------------------
+// Test moving helper classes.
+// ----------------------------------------------------------------------------
+// RUN: cp %S/Inputs/helper_decls_test*  %T/used-helper-decls/
+// RUN: clang-move -names="a::Class4" -new_cc=%T/used-helper-decls/new_helper_decls_test.cpp -new_header=%T/used-helper-decls/new_helper_decls_test.h -old_cc=%T/used-helper-decls/helper_decls_test.cpp -old_header=../used-helper-decls/helper_decls_test.h %T/used-helper-decls/helper_decls_test.cpp -- -std=c++11
+// RUN: FileCheck -input-file=%T/used-helper-decls/new_helper_decls_test.cpp -check-prefix=CHECK-NEW-CLASS4-CPP %s
+// RUN: FileCheck -input-file=%T/used-helper-decls/helper_decls_test.cpp -check-prefix=CHECK-OLD-CLASS4-CPP %s
+
+// CHECK-NEW-CLASS4-CPP: #include "{{.*}}new_helper_decls_test.h"
+// CHECK-NEW-CLASS4-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CLASS4-CPP-NEXT: namespace {
+// CHECK-NEW-CLASS4-CPP-NEXT: class HelperC2 {};
+// CHECK-NEW-CLASS4-CPP-NEXT: } // namespace
+// CHECK-NEW-CLASS4-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CLASS4-CPP-NEXT: namespace a {
+// CHECK-NEW-CLASS4-CPP-NEXT: void Class4::f() { HelperC2 c2; }
+// CHECK-NEW-CLASS4-CPP-NEXT: } // namespace a
+
+// CHECK-OLD-CLASS4-CPP-NOT: class HelperC2 {};
+
+
+// ----------------------------------------------------------------------------
+// Test moving helper variables and helper functions together.
+// ----------------------------------------------------------------------------
+// RUN: cp %S/Inputs/helper_decls_test*  %T/used-helper-decls/
+// RUN: clang-move -names="a::Class5" -new_cc=%T/used-helper-decls/new_helper_decls_test.cpp -new_header=%T/used-helper-decls/new_helper_decls_test.h -old_cc=%T/used-helper-decls/helper_decls_test.cpp -old_header=../used-helper-decls/helper_decls_test.h %T/used-helper-decls/helper_decls_test.cpp -- -std=c++11
+// RUN: FileCheck -input-file=%T/used-helper-decls/new_helper_decls_test.cpp -check-prefix=CHECK-NEW-CLASS5-CPP %s
+// RUN: FileCheck -input-file=%T/used-helper-decls/helper_decls_test.cpp -check-prefix=CHECK-OLD-CLASS5-CPP %s
+
+// CHECK-NEW-CLASS5-CPP: #include "{{.*}}new_helper_decls_test.h"
+// CHECK-NEW-CLASS5-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CLASS5-CPP-NEXT: namespace {
+// CHECK-NEW-CLASS5-CPP-NEXT: const int K1 = 1;
+// CHECK-NEW-CLASS5-CPP-NEXT: } // namespace
+// CHECK-NEW-CLASS5-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CLASS5-CPP-NEXT: static const int K2 = 2;
+// CHECK-NEW-CLASS5-CPP-NEXT: namespace a {
+// CHECK-NEW-CLASS5-CPP-NEXT: static const int K3 = 3;
+// CHECK-NEW-CLASS5-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CLASS5-CPP-NEXT: static void HelperFun4() {}
+// CHECK-NEW-CLASS5-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CLASS5-CPP-NEXT: void Class5::f() {
+// CHECK-NEW-CLASS5-CPP-NEXT:   int Result = K1 + K2 + K3;
+// CHECK-NEW-CLASS5-CPP-NEXT:   HelperFun4();
+// CHECK-NEW-CLASS5-CPP-NEXT: }
+// CHECK-NEW-CLASS5-CPP-NEXT: } // namespace a
+
+// CHECK-OLD-CLASS5-CPP-NOT: const int K1 = 1;
+// CHECK-OLD-CLASS5-CPP: static const int K2 = 2;
+// CHECK-OLD-CLASS5-CPP: static void HelperFun3() { K2; }
+// CHECK-OLD-CLASS5-CPP: static const int K4 = HelperC3::I;
+// CHECK-OLD-CLASS5-CPP-NOT: void Class5::f() {
+
+
+// ----------------------------------------------------------------------------
+// Test moving helper variables and their transively used helper classes.
+// ----------------------------------------------------------------------------
+// RUN: cp %S/Inputs/helper_decls_test*  %T/used-helper-decls/
+// RUN: clang-move -names="a::Class6" -new_cc=%T/used-helper-decls/new_helper_decls_test.cpp -new_header=%T/used-helper-decls/new_helper_decls_test.h -old_cc=%T/used-helper-decls/helper_decls_test.cpp -old_header=../used-helper-decls/helper_decls_test.h %T/used-helper-decls/helper_decls_test.cpp -- -std=c++11
+// RUN: FileCheck -input-file=%T/used-helper-decls/new_helper_decls_test.cpp -check-prefix=CHECK-NEW-CLASS6-CPP %s
+// RUN: FileCheck -input-file=%T/used-helper-decls/helper_decls_test.cpp -check-prefix=CHECK-OLD-CLASS6-CPP %s
+
+// CHECK-NEW-CLASS6-CPP: #include "{{.*}}new_helper_decls_test.h"
+// CHECK-NEW-CLASS6-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CLASS6-CPP-NEXT: namespace {
+// CHECK-NEW-CLASS6-CPP-NEXT: class HelperC3 {
+// CHECK-NEW-CLASS6-CPP-NEXT: public:
+// CHECK-NEW-CLASS6-CPP-NEXT:   static int I;
+// CHECK-NEW-CLASS6-CPP-NEXT: };
+// CHECK-NEW-CLASS6-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CLASS6-CPP-NEXT: int HelperC3::I = 0;
+// CHECK-NEW-CLASS6-CPP-NEXT: } // namespace
+// CHECK-NEW-CLASS6-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CLASS6-CPP-NEXT: namespace a {
+// CHECK-NEW-CLASS6-CPP-NEXT: static const int K4 = HelperC3::I;
+// CHECK-NEW-CLASS6-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CLASS6-CPP-NEXT: int Class6::f() {
+// CHECK-NEW-CLASS6-CPP-NEXT:   int R = K4;
+// CHECK-NEW-CLASS6-CPP-NEXT:   return R;
+// CHECK-NEW-CLASS6-CPP-NEXT: }
+// CHECK-NEW-CLASS6-CPP-NEXT: } // namespace a
+
+// CHECK-OLD-CLASS6-CPP-NOT: class HelperC3 {
+// CHECK-OLD-CLASS6-CPP-NOT: int HelperC3::I = 0;
+// CHECK-OLD-CLASS6-CPP-NOT: static const int K4 = HelperC3::I;
+
+
+// ----------------------------------------------------------------------------
+// Test moving classes where its methods use helpers.
+// ----------------------------------------------------------------------------
+// RUN: cp %S/Inputs/helper_decls_test*  %T/used-helper-decls/
+// RUN: clang-move -names="a::Class7" -new_cc=%T/used-helper-decls/new_helper_decls_test.cpp -new_header=%T/used-helper-decls/new_helper_decls_test.h -old_cc=%T/used-helper-decls/helper_decls_test.cpp -old_header=../used-helper-decls/helper_decls_test.h %T/used-helper-decls/helper_decls_test.cpp -- -std=c++11
+// RUN: FileCheck -input-file=%T/used-helper-decls/new_helper_decls_test.cpp -check-prefix=CHECK-NEW-CLASS7-CPP %s
+// RUN: FileCheck -input-file=%T/used-helper-decls/helper_decls_test.cpp -check-prefix=CHECK-OLD-CLASS7-CPP %s
+
+// CHECK-NEW-CLASS7-CPP: #include "{{.*}}new_helper_decls_test.h"
+// CHECK-NEW-CLASS7-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CLASS7-CPP-NEXT: namespace a {
+// CHECK-NEW-CLASS7-CPP-NEXT: static const int K6 = 6;
+// CHECK-NEW-CLASS7-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CLASS7-CPP-NEXT: static void HelperFun6() {}
+// CHECK-NEW-CLASS7-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CLASS7-CPP-NEXT: int Class7::f() {
+// CHECK-NEW-CLASS7-CPP-NEXT:   int R = K6;
+// CHECK-NEW-CLASS7-CPP-NEXT:   return R;
+// CHECK-NEW-CLASS7-CPP-NEXT: }
+// CHECK-NEW-CLASS7-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CLASS7-CPP-NEXT: int Class7::g() {
+// CHECK-NEW-CLASS7-CPP-NEXT:   HelperFun6();
+// CHECK-NEW-CLASS7-CPP-NEXT:   return 1;
+// CHECK-NEW-CLASS7-CPP-NEXT: }
+// CHECK-NEW-CLASS7-CPP-NEXT: } // namespace a
+//
+// CHECK-OLD-CLASS7-CPP-NOT: static const int K6 = 6;
+// CHECK-OLD-CLASS7-CPP-NOT: static void HelperFun6() {}
+// CHECK-OLD-CLASS7-CPP-NOT: int Class7::f() {
+// CHECK-OLD-CLASS7-CPP-NOT: int Class7::g() {
+
+
+// ----------------------------------------------------------------------------
+// Test moving helper function and its transively used helper variables.
+// ----------------------------------------------------------------------------
+// RUN: cp %S/Inputs/helper_decls_test*  %T/used-helper-decls/
+// RUN: clang-move -names="a::Fun1" -new_cc=%T/used-helper-decls/new_helper_decls_test.cpp -new_header=%T/used-helper-decls/new_helper_decls_test.h -old_cc=%T/used-helper-decls/helper_decls_test.cpp -old_header=../used-helper-decls/helper_decls_test.h %T/used-helper-decls/helper_decls_test.cpp -- -std=c++11
+// RUN: FileCheck -input-file=%T/used-helper-decls/new_helper_decls_test.cpp -check-prefix=CHECK-NEW-FUN1-CPP %s
+// RUN: FileCheck -input-file=%T/used-helper-decls/helper_decls_test.cpp -check-prefix=CHECK-OLD-FUN1-CPP %s
+
+// CHECK-NEW-FUN1-CPP: #include "{{.*}}new_helper_decls_test.h"
+// CHECK-NEW-FUN1-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-FUN1-CPP-NEXT: namespace a {
+// CHECK-NEW-FUN1-CPP-NEXT: static const int K5 = 5;
+// CHECK-NEW-FUN1-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-FUN1-CPP-NEXT: static int HelperFun5() {
+// CHECK-NEW-FUN1-CPP-NEXT:   int R = K5;
+// CHECK-NEW-FUN1-CPP-NEXT:   return R;
+// CHECK-NEW-FUN1-CPP-NEXT: }
+// CHECK-NEW-FUN1-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-FUN1-CPP-NEXT: void Fun1() { HelperFun5(); }
+// CHECK-NEW-FUN1-CPP-NEXT: } // namespace a
+
+// CHECK-OLD-FUN1-CPP-NOT: static const int K5 = 5;
+// CHECK-OLD-FUN1-CPP-NOT: static int HelperFun5() {
+// CHECK-OLD-FUN1-CPP-NOT: void Fun1() { HelperFun5(); }
+
+
+// ----------------------------------------------------------------------------
+// Test no moving helpers when moving inline functions in header.
+// ----------------------------------------------------------------------------
+// RUN: cp %S/Inputs/helper_decls_test*  %T/used-helper-decls/
+// RUN: clang-move -names="a::Fun2" -new_cc=%T/used-helper-decls/new_helper_decls_test.cpp -new_header=%T/used-helper-decls/new_helper_decls_test.h -old_cc=%T/used-helper-decls/helper_decls_test.cpp -old_header=../used-helper-decls/helper_decls_test.h %T/used-helper-decls/helper_decls_test.cpp -- -std=c++11
+// RUN: FileCheck -input-file=%T/used-helper-decls/new_helper_decls_test.cpp -check-prefix=CHECK-NEW-FUN2-CPP %s
+// RUN: FileCheck -input-file=%T/used-helper-decls/new_helper_decls_test.h -check-prefix=CHECK-NEW-FUN2-H %s
+// RUN: FileCheck -input-file=%T/used-helper-decls/helper_decls_test.h -check-prefix=CHECK-OLD-FUN2-H %s
+
+// CHECK-NEW-FUN2-H: namespace a {
+// CHECK-NEW-FUN2-H-NEXT: inline void Fun2() {}
+// CHECK-NEW-FUN2-H-NEXT: } // namespace a
+
+// CHECK-NEW-FUN2-CPP: #include "{{.*}}new_helper_decls_test.h"
+// CHECK-NEW-FUN2-CPP-SAME: {{[[:space:]]}}
+
+// CHECK-OLD-FUN2-H-NOT: inline void Fun2() {}
+
+// ----------------------------------------------------------------------------
+// Test moving used helper function and its transively used functions.
+// ----------------------------------------------------------------------------
+// RUN: cp %S/Inputs/helper_decls_test*  %T/used-helper-decls/
+// RUN: clang-move -names="b::Fun3" -new_cc=%T/used-helper-decls/new_helper_decls_test.cpp -new_header=%T/used-helper-decls/new_helper_decls_test.h -old_cc=%T/used-helper-decls/helper_decls_test.cpp -old_header=../used-helper-decls/helper_decls_test.h %T/used-helper-decls/helper_decls_test.cpp -- -std=c++11
+// RUN: FileCheck -input-file=%T/used-helper-decls/new_helper_decls_test.cpp -check-prefix=CHECK-NEW-FUN3-CPP %s
+// RUN: FileCheck -input-file=%T/used-helper-decls/helper_decls_test.cpp -check-prefix=CHECK-OLD-FUN3-CPP %s
+
+// CHECK-NEW-FUN3-CPP: #include "{{.*}}new_helper_decls_test.h"
+// CHECK-NEW-FUN3-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-FUN3-CPP-NEXT: namespace b {
+// CHECK-NEW-FUN3-CPP-NEXT: namespace {
+// CHECK-NEW-FUN3-CPP-NEXT: void HelperFun7();
+// CHECK-NEW-FUN3-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-FUN3-CPP-NEXT: class HelperC4;
+// CHECK-NEW-FUN3-CPP-NEXT: } // namespace
+// CHECK-NEW-FUN3-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-FUN3-CPP-NEXT: void Fun3() {
+// CHECK-NEW-FUN3-CPP-NEXT:   HelperFun7();
+// CHECK-NEW-FUN3-CPP-NEXT:   HelperC4 *t;
+// CHECK-NEW-FUN3-CPP-NEXT: }
+// CHECK-NEW-FUN3-CPP-NEXT: namespace {
+// CHECK-NEW-FUN3-CPP-NEXT: void HelperFun7() {}
+// CHECK-NEW-FUN3-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-FUN3-CPP-NEXT: class HelperC4 {};
+// CHECK-NEW-FUN3-CPP-NEXT: } // namespace
+// CHECK-NEW-FUN3-CPP-NEXT: } // namespace b
+//
+// CHECK-OLD-FUN3-CPP-NOT: void HelperFun7();
+// CHECK-OLD-FUN3-CPP-NOT: void HelperFun7() {}
+// CHECK-OLD-FUN3-CPP-NOT: void Fun3() { HelperFun7(); }
+
+// ----------------------------------------------------------------------------
+// Test moving all symbols in headers.
+// ----------------------------------------------------------------------------
+// RUN: cp %S/Inputs/helper_decls_test*  %T/used-helper-decls/
+// RUN: clang-move -names="a::Class1, a::Class2, a::Class3, a::Class4, a::Class5, a::Class5, a::Class6, a::Class7, a::Fun1, a::Fun2, b::Fun3" -new_cc=%T/used-helper-decls/new_helper_decls_test.cpp -new_header=%T/used-helper-decls/new_helper_decls_test.h -old_cc=%T/used-helper-decls/helper_decls_test.cpp -old_header=../used-helper-decls/helper_decls_test.h %T/used-helper-decls/helper_decls_test.cpp -- -std=c++11
+// RUN: FileCheck -input-file=%T/used-helper-decls/new_helper_decls_test.h -check-prefix=CHECK-NEW-H %s
+// RUN: FileCheck -input-file=%T/used-helper-decls/new_helper_decls_test.cpp -check-prefix=CHECK-NEW-CPP %s
+// RUN: FileCheck -input-file=%T/used-helper-decls/helper_decls_test.h -allow-empty -check-prefix=CHECK-EMPTY %s
+// RUN: FileCheck -input-file=%T/used-helper-decls/helper_decls_test.cpp -allow-empty -check-prefix=CHECK-EMPTY %s
+
+
+// CHECK-NEW-H: namespace a {
+// CHECK-NEW-H-NEXT: class Class1 {
+// CHECK-NEW-H-NEXT:   void f();
+// CHECK-NEW-H-NEXT: };
+// CHECK-NEW-H-SAME: {{[[:space:]]}}
+// CHECK-NEW-H-NEXT: class Class2 {
+// CHECK-NEW-H-NEXT:   void f();
+// CHECK-NEW-H-NEXT: };
+// CHECK-NEW-H-SAME: {{[[:space:]]}}
+// CHECK-NEW-H-NEXT: class Class3 {
+// CHECK-NEW-H-NEXT:   void f();
+// CHECK-NEW-H-NEXT: };
+// CHECK-NEW-H-SAME: {{[[:space:]]}}
+// CHECK-NEW-H-NEXT: class Class4 {
+// CHECK-NEW-H-NEXT:   void f();
+// CHECK-NEW-H-NEXT: };
+// CHECK-NEW-H-SAME: {{[[:space:]]}}
+// CHECK-NEW-H-NEXT: class Class5 {
+// CHECK-NEW-H-NEXT:   void f();
+// CHECK-NEW-H-NEXT: };
+// CHECK-NEW-H-SAME: {{[[:space:]]}}
+// CHECK-NEW-H-NEXT: class Class6 {
+// CHECK-NEW-H-NEXT:   int f();
+// CHECK-NEW-H-NEXT: };
+// CHECK-NEW-H-SAME: {{[[:space:]]}}
+// CHECK-NEW-H-NEXT: class Class7 {
+// CHECK-NEW-H-NEXT:   int f();
+// CHECK-NEW-H-NEXT:   int g();
+// CHECK-NEW-H-NEXT: };
+// CHECK-NEW-H-SAME: {{[[:space:]]}}
+// CHECK-NEW-H-NEXT: void Fun1();
+// CHECK-NEW-H-SAME: {{[[:space:]]}}
+// CHECK-NEW-H-NEXT: inline void Fun2() {}
+// CHECK-NEW-H-SAME: {{[[:space:]]}}
+// CHECK-NEW-H-NEXT: } // namespace a
+
+
+// CHECK-NEW-CPP: namespace {
+// CHECK-NEW-CPP-NEXT: class HelperC1 {
+// CHECK-NEW-CPP-NEXT: public:
+// CHECK-NEW-CPP-NEXT:   static int I;
+// CHECK-NEW-CPP-NEXT: };
+// CHECK-NEW-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CPP-NEXT: int HelperC1::I = 0;
+// CHECK-NEW-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CPP-NEXT: class HelperC2 {};
+// CHECK-NEW-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CPP-NEXT: class HelperC3 {
+// CHECK-NEW-CPP-NEXT:  public:
+// CHECK-NEW-CPP-NEXT:   static int I;
+// CHECK-NEW-CPP-NEXT: };
+// CHECK-NEW-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CPP-NEXT: int HelperC3::I = 0;
+// CHECK-NEW-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CPP-NEXT: void HelperFun1() {}
+// CHECK-NEW-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CPP-NEXT: void HelperFun2() { HelperFun1(); }
+// CHECK-NEW-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CPP-NEXT: const int K1 = 1;
+// CHECK-NEW-CPP-NEXT: } // namespace
+// CHECK-NEW-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CPP-NEXT: static const int K2 = 2;
+// CHECK-NEW-CPP-NEXT: static void HelperFun3() { K2; }
+// CHECK-NEW-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CPP-NEXT: namespace a {
+// CHECK-NEW-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CPP-NEXT: static const int K3 = 3;
+// CHECK-NEW-CPP-NEXT: static const int K4 = HelperC3::I;
+// CHECK-NEW-CPP-NEXT: static const int K5 = 5;
+// CHECK-NEW-CPP-NEXT: static const int K6 = 6;
+// CHECK-NEW-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CPP-NEXT: static void HelperFun4() {}
+// CHECK-NEW-CPP-NEXT: static void HelperFun6() {}
+// CHECK-NEW-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CPP-NEXT: void Class1::f() { HelperFun2(); }
+// CHECK-NEW-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CPP-NEXT: void Class2::f() {
+// CHECK-NEW-CPP-NEXT:   HelperFun1();
+// CHECK-NEW-CPP-NEXT:   HelperFun3();
+// CHECK-NEW-CPP-NEXT: }
+// CHECK-NEW-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CPP-NEXT: void Class3::f() { HelperC1::I; }
+// CHECK-NEW-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CPP-NEXT: void Class4::f() { HelperC2 c2; }
+// CHECK-NEW-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CPP-NEXT: void Class5::f() {
+// CHECK-NEW-CPP-NEXT:   int Result = K1 + K2 + K3;
+// CHECK-NEW-CPP-NEXT:   HelperFun4();
+// CHECK-NEW-CPP-NEXT: }
+// CHECK-NEW-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CPP-NEXT: int Class6::f() {
+// CHECK-NEW-CPP-NEXT:   int R = K4;
+// CHECK-NEW-CPP-NEXT:   return R;
+// CHECK-NEW-CPP-NEXT: }
+// CHECK-NEW-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CPP-NEXT: int Class7::f() {
+// CHECK-NEW-CPP-NEXT:   int R = K6;
+// CHECK-NEW-CPP-NEXT:   return R;
+// CHECK-NEW-CPP-NEXT: }
+// CHECK-NEW-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CPP-NEXT: int Class7::g() {
+// CHECK-NEW-CPP-NEXT:   HelperFun6();
+// CHECK-NEW-CPP-NEXT:   return 1;
+// CHECK-NEW-CPP-NEXT: }
+// CHECK-NEW-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CPP-NEXT: static int HelperFun5() {
+// CHECK-NEW-CPP-NEXT:   int R = K5;
+// CHECK-NEW-CPP-NEXT:   return R;
+// CHECK-NEW-CPP-NEXT: }
+// CHECK-NEW-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CPP-NEXT: void Fun1() { HelperFun5(); }
+// CHECK-NEW-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CPP-NEXT: } // namespace a
+// CHECK-NEW-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CPP-NEXT: namespace b {
+// CHECK-NEW-CPP-NEXT: namespace {
+// CHECK-NEW-CPP-NEXT: void HelperFun7();
+// CHECK-NEW-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CPP-NEXT: class HelperC4;
+// CHECK-NEW-CPP-NEXT: } // namespace
+// CHECK-NEW-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CPP-NEXT: void Fun3() {
+// CHECK-NEW-CPP-NEXT:   HelperFun7();
+// CHECK-NEW-CPP-NEXT:   HelperC4 *t;
+// CHECK-NEW-CPP-NEXT: }
+// CHECK-NEW-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CPP-NEXT: namespace {
+// CHECK-NEW-CPP-NEXT: void HelperFun7() {}
+// CHECK-NEW-CPP-SAME: {{[[:space:]]}}
+// CHECK-NEW-CPP-NEXT: class HelperC4 {};
+// CHECK-NEW-CPP-NEXT: } // namespace
+// CHECK-NEW-CPP-NEXT: } // namespace b
+
+// CHECK-EMPTY: {{^}}{{$}}
diff --git a/test/clang-move/move-var.cpp b/test/clang-move/move-var.cpp
new file mode 100644 (file)
index 0000000..4a3554c
--- /dev/null
@@ -0,0 +1,46 @@
+// RUN: mkdir -p %T/move-var
+// RUN: cp %S/Inputs/var_test*  %T/move-var
+// RUN: cd %T/move-var
+// RUN: clang-move -names="a::kGlobalInt" -new_header=%T/move-var/new_var_test.h -old_header=../move-var/var_test.h -old_cc=../move-var/var_test.cpp -new_cc=%T/move-var/new_var_test.cpp %T/move-var/var_test.cpp --
+// RUN: FileCheck -input-file=%T/move-var/var_test.h -check-prefix=CHECK-OLD-VAR-H-CASE1 %s
+// RUN: FileCheck -input-file=%T/move-var/var_test.cpp -check-prefix=CHECK-OLD-VAR-CPP-CASE1 %s
+// RUN: FileCheck -input-file=%T/move-var/new_var_test.h -check-prefix=CHECK-NEW-VAR-H-CASE1 %s
+// RUN: FileCheck -input-file=%T/move-var/new_var_test.cpp -check-prefix=CHECK-NEW-VAR-CPP-CASE1 %s
+
+// CHECK-OLD-VAR-H-CASE1-NOT: extern int kGlobalInt;
+// CHECK-OLD-VAR-H-CASE1: int kGlobalInt = 3;
+
+// CHECK-OLD-VAR-CPP-CASE1-NOT: int kGlobalInt = 1;
+
+// CHECK-NEW-VAR-H-CASE1: extern int kGlobalInt;
+// CHECK-NEW-VAR-H-CASE1-NOT: int kGlobalInt = 3;
+
+// CHECK-NEW-VAR-CPP-CASE1: int kGlobalInt = 1;
+
+
+// RUN: cp %S/Inputs/var_test*  %T/move-var
+// RUN: clang-move -names="a::kGlobalStr" -new_header=%T/move-var/new_var_test.h -old_header=../move-var/var_test.h -old_cc=../move-var/var_test.cpp -new_cc=%T/move-var/new_var_test.cpp %T/move-var/var_test.cpp --
+// RUN: FileCheck -input-file=%T/move-var/var_test.h -check-prefix=CHECK-OLD-VAR-H-CASE2 %s
+// RUN: FileCheck -input-file=%T/move-var/var_test.cpp -check-prefix=CHECK-OLD-VAR-CPP-CASE2 %s
+// RUN: FileCheck -input-file=%T/move-var/new_var_test.h -check-prefix=CHECK-NEW-VAR-H-CASE2 %s
+// RUN: FileCheck -input-file=%T/move-var/new_var_test.cpp -check-prefix=CHECK-NEW-VAR-CPP-CASE2 %s
+
+// CHECK-OLD-VAR-H-CASE2-NOT: extern const char *const kGlobalStr;
+// CHECK-OLD-VAR-H-CASE2: const char *const kGlobalStr = "Hello2";
+
+// CHECK-OLD-VAR-CPP-CASE2-NOT: const char *const kGlobalStr = "Hello";
+
+// CHECK-NEW-VAR-H-CASE2: extern const char *const kGlobalStr;
+// CHECK-NEW-VAR-H-CASE2-NOT: const char *const kGlobalStr = "Hello2";
+
+// CHECK-NEW-VAR-CPP-CASE2: const char *const kGlobalStr = "Hello";
+
+
+// RUN: cp %S/Inputs/var_test*  %T/move-var
+// RUN: clang-move -names="kEvilInt" -new_header=%T/move-var/new_var_test.h -old_header=../move-var/var_test.h -old_cc=../move-var/var_test.cpp -new_cc=%T/move-var/new_var_test.cpp %T/move-var/var_test.cpp --
+// RUN: FileCheck -input-file=%T/move-var/var_test.h -check-prefix=CHECK-OLD-VAR-H-CASE3 %s
+// RUN: FileCheck -input-file=%T/move-var/new_var_test.h -check-prefix=CHECK-NEW-VAR-H-CASE3 %s
+
+// CHECK-OLD-VAR-H-CASE3-NOT: int kEvilInt = 2;
+
+// CHECK-NEW-VAR-H-CASE3: int kEvilInt = 2;
diff --git a/test/clang-move/no-move-macro-helpers.cpp b/test/clang-move/no-move-macro-helpers.cpp
new file mode 100644 (file)
index 0000000..d0db5a9
--- /dev/null
@@ -0,0 +1,43 @@
+// RUN: mkdir -p %T/no-move-macro-helper
+// RUN: cp %S/Inputs/macro_helper_test.h  %T/no-move-macro-helper/macro_helper_test.h
+// RUN: cp %S/Inputs/macro_helper_test.cpp %T/no-move-macro-helper/macro_helper_test.cpp
+// RUN: cd %T/no-move-macro-helper
+//
+// -----------------------------------------------------------------------------
+// Test no moving helpers in macro.
+// -----------------------------------------------------------------------------
+// RUN: clang-move -names="A" -new_cc=%T/no-move-macro-helper/new_test.cpp -new_header=%T/no-move-macro-helper/new_test.h -old_cc=%T/no-move-macro-helper/macro_helper_test.cpp -old_header=%T/no-move-macro-helper/macro_helper_test.h %T/no-move-macro-helper/macro_helper_test.cpp -- -std=c++11
+// RUN: FileCheck -input-file=%T/no-move-macro-helper/new_test.h -check-prefix=CHECK-NEW-TEST-CASE1-H %s
+// RUN: FileCheck -input-file=%T/no-move-macro-helper/new_test.cpp -check-prefix=CHECK-NEW-TEST-CASE1-CPP %s
+// RUN: FileCheck -input-file=%T/no-move-macro-helper/macro_helper_test.h -check-prefix=CHECK-OLD-TEST-CASE1-H %s
+// RUN: FileCheck -input-file=%T/no-move-macro-helper/macro_helper_test.cpp -check-prefix=CHECK-OLD-TEST-CASE1-CPP %s
+
+// CHECK-NEW-TEST-CASE1-H: class A {};
+
+// CHECK-OLD-TEST-CASE1-H-NOT: class A {};
+
+// CHECK-OLD-TEST-CASE1-CPP: DEFINE(test)
+
+// CHECK-NEW-TEST-CASE1-CPP-NOT: DEFINE(test)
+
+
+// -----------------------------------------------------------------------------
+// Test moving all.
+// -----------------------------------------------------------------------------
+// RUN: cp %S/Inputs/macro_helper_test.h  %T/no-move-macro-helper/macro_helper_test.h
+// RUN: cp %S/Inputs/macro_helper_test.cpp %T/no-move-macro-helper/macro_helper_test.cpp
+// RUN: clang-move -names="A, f1" -new_cc=%T/no-move-macro-helper/new_test.cpp -new_header=%T/no-move-macro-helper/new_test.h -old_cc=%T/no-move-macro-helper/macro_helper_test.cpp -old_header=%T/no-move-macro-helper/macro_helper_test.h %T/no-move-macro-helper/macro_helper_test.cpp -- -std=c++11
+//
+// RUN: FileCheck -input-file=%T/no-move-macro-helper/new_test.h -check-prefix=CHECK-NEW-TEST-CASE2-H %s
+// RUN: FileCheck -input-file=%T/no-move-macro-helper/new_test.cpp -check-prefix=CHECK-NEW-TEST-CASE2-CPP %s
+// RUN: FileCheck -input-file=%T/no-move-macro-helper/macro_helper_test.h -allow-empty -check-prefix=CHECK-EMPTY %s
+// RUN: FileCheck -input-file=%T/no-move-macro-helper/macro_helper_test.cpp -allow-empty -check-prefix=CHECK-EMPTY %s
+
+// CHECK-NEW-TEST-CASE2-H: class A {};
+// CHECK-NEW-TEST-CASE2-H-NEXT:void f1();
+
+
+// CHECK-NEW-TEST-CASE2-CPP: DEFINE(test)
+// CHECK-NEW-TEST-CASE2-CPP: void f1() {}
+
+// CHECK-EMPTY: {{^}}{{$}}
diff --git a/test/clang-query/Inputs/foo.script b/test/clang-query/Inputs/foo.script
new file mode 100644 (file)
index 0000000..3bd1f0e
--- /dev/null
@@ -0,0 +1,2 @@
+foo
+bar
diff --git a/test/clang-query/errors.c b/test/clang-query/errors.c
new file mode 100644 (file)
index 0000000..bbb7421
--- /dev/null
@@ -0,0 +1,10 @@
+// RUN: not clang-query -c foo -c bar %s -- | FileCheck %s
+// RUN: not clang-query -f %S/Inputs/foo.script %s -- | FileCheck %s
+// RUN: not clang-query -f %S/Inputs/nonexistent.script %s -- 2>&1 | FileCheck --check-prefix=CHECK-NONEXISTENT %s
+// RUN: not clang-query -c foo -f foo %s -- 2>&1 | FileCheck --check-prefix=CHECK-BOTH %s
+
+// CHECK: unknown command: foo
+// CHECK-NOT: unknown command: bar
+
+// CHECK-NONEXISTENT: cannot open {{.*}}nonexistent.script
+// CHECK-BOTH: cannot specify both -c and -f
diff --git a/test/clang-query/function-decl.c b/test/clang-query/function-decl.c
new file mode 100644 (file)
index 0000000..f35cba0
--- /dev/null
@@ -0,0 +1,4 @@
+// RUN: clang-query -c "match functionDecl()" %s -- | FileCheck %s
+
+// CHECK: function-decl.c:4:1: note: "root" binds here
+void foo(void) {}
diff --git a/test/clang-reorder-fields/AggregatePartialInitialization.cpp b/test/clang-reorder-fields/AggregatePartialInitialization.cpp
new file mode 100644 (file)
index 0000000..9d09c81
--- /dev/null
@@ -0,0 +1,14 @@
+// RUN: clang-reorder-fields -record-name Foo -fields-order z,y,x %s -- | FileCheck %s
+
+// The order of fields should not change.
+class Foo {
+public:
+  int x;  // CHECK:       {{^  int x;}}
+  int y;  // CHECK-NEXT:  {{^  int y;}}
+  int z;  // CHECK-NEXT:  {{^  int z;}}
+};
+
+int main() {
+  Foo foo = { 0, 1 }; // CHECK: {{^  Foo foo = { 0, 1 };}}
+  return 0;
+}
diff --git a/test/clang-reorder-fields/CStructAmbiguousName.cpp b/test/clang-reorder-fields/CStructAmbiguousName.cpp
new file mode 100644 (file)
index 0000000..e1e6645
--- /dev/null
@@ -0,0 +1,18 @@
+// RUN: clang-reorder-fields -record-name ::Foo -fields-order y,x %s -- | FileCheck %s
+
+struct Foo {
+  int x;    // CHECK:      {{^  double y;}}
+  double y; // CHECK-NEXT: {{^  int x;}}
+};
+
+namespace bar {
+struct Foo {
+  int x;    // CHECK:      {{^  int x;}}
+  double y; // CHECK-NEXT: {{^  double y;}}
+};
+} // end namespace bar
+
+int main() {
+  bar::Foo foo = { 1, 1.7 }; // CHECK: {{^  bar::Foo foo = { 1, 1.7 };}}
+  return 0;
+}
diff --git a/test/clang-reorder-fields/CStructFieldsOrder.cpp b/test/clang-reorder-fields/CStructFieldsOrder.cpp
new file mode 100644 (file)
index 0000000..2ed3578
--- /dev/null
@@ -0,0 +1,16 @@
+// RUN: clang-reorder-fields -record-name ::bar::Foo -fields-order z,w,y,x %s -- | FileCheck %s
+
+namespace bar {
+struct Foo {
+  const int* x; // CHECK:      {{^  double z;}}
+  int y;        // CHECK-NEXT: {{^  int w;}}
+  double z;     // CHECK-NEXT: {{^  int y;}}
+  int w;        // CHECK-NEXT: {{^  const int\* x}}
+};
+} // end namespace bar
+
+int main() {
+  const int x = 13;
+  bar::Foo foo = { &x, 0, 1.29, 17 }; // CHECK: {{^  bar::Foo foo = { 1.29, 17, 0, &x };}} 
+  return 0;
+}
diff --git a/test/clang-reorder-fields/ClassDifferentFieldsAccesses.cpp b/test/clang-reorder-fields/ClassDifferentFieldsAccesses.cpp
new file mode 100644 (file)
index 0000000..dd0a555
--- /dev/null
@@ -0,0 +1,16 @@
+// RUN: clang-reorder-fields -record-name Foo -fields-order z,y,x %s -- | FileCheck %s
+
+// The order of fields should not change.
+class Foo {
+public:
+  int x;  // CHECK:       {{^  int x;}}
+
+private:
+  int y;  // CHECK:       {{^  int y;}}
+  int z;  // CHECK-NEXT:  {{^  int z;}}
+};
+
+int main() {
+  Foo foo;
+  return 0;
+}
diff --git a/test/clang-reorder-fields/ClassMixedInitialization.cpp b/test/clang-reorder-fields/ClassMixedInitialization.cpp
new file mode 100644 (file)
index 0000000..888649a
--- /dev/null
@@ -0,0 +1,24 @@
+// RUN: clang-reorder-fields -record-name Foo -fields-order e,x,pi,s2,s1 %s -- -std=c++11 | FileCheck %s
+
+class Foo {
+public:
+  Foo();
+
+private:
+  int x;              // CHECK:      {{^  double e = 2.71;}}
+  const char *s1;     // CHECK-NEXT: {{^  int x;}}
+  const char *s2;     // CHECK-NEXT: {{^  double pi = 3.14;}}
+  double pi = 3.14;   // CHECK-NEXT: {{^  const char \*s2;}}
+  double e = 2.71;    // CHECK-NEXT: {{^  const char \*s1;}}
+};
+
+Foo::Foo():
+  x(12),      // CHECK:      {{^  x\(12\)}},
+  s1("abc"),  // CHECK-NEXT: {{^  s2\("def"\)}},
+  s2("def")   // CHECK-NEXT: {{^  s1\("abc"\)}}
+{}
+
+int main() {
+  Foo foo;
+  return 0;
+}
diff --git a/test/clang-reorder-fields/ClassSimpleCtor.cpp b/test/clang-reorder-fields/ClassSimpleCtor.cpp
new file mode 100644 (file)
index 0000000..6dc2722
--- /dev/null
@@ -0,0 +1,24 @@
+// RUN: clang-reorder-fields -record-name Foo -fields-order s1,x,z,s2 %s -- | FileCheck %s
+
+class Foo {
+public:
+  Foo();
+
+private:
+  int x;              // CHECK:      {{^  const char \*s1;}}
+  const char *s1;     // CHECK-NEXT: {{^  int x;}}
+  const char *s2;     // CHECK-NEXT: {{^  double z;}}
+  double z;           // CHECK-NEXT: {{^  const char \*s2;}}
+};
+
+Foo::Foo():
+  x(12),      // CHECK:      {{^  s1\("abc"\),}}
+  s1("abc"),  // CHECK-NEXT: {{^  x\(12\),}}
+  s2("def"),  // CHECK-NEXT: {{^  z\(3.14\),}}
+  z(3.14)     // CHECK-NEXT: {{^  s2\("def"\)}}
+{}
+
+int main() {
+  Foo foo;
+  return 0;
+}
diff --git a/test/clang-tidy/Inputs/Headers/a.h b/test/clang-tidy/Inputs/Headers/a.h
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/test/clang-tidy/Inputs/Headers/b.h b/test/clang-tidy/Inputs/Headers/b.h
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/test/clang-tidy/Inputs/Headers/clang-c/c.h b/test/clang-tidy/Inputs/Headers/clang-c/c.h
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/test/clang-tidy/Inputs/Headers/clang/b.h b/test/clang-tidy/Inputs/Headers/clang/b.h
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/test/clang-tidy/Inputs/Headers/cross-file-a.h b/test/clang-tidy/Inputs/Headers/cross-file-a.h
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/test/clang-tidy/Inputs/Headers/cross-file-b.h b/test/clang-tidy/Inputs/Headers/cross-file-b.h
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/test/clang-tidy/Inputs/Headers/cross-file-c.h b/test/clang-tidy/Inputs/Headers/cross-file-c.h
new file mode 100644 (file)
index 0000000..342dd34
--- /dev/null
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+// The line number of the following include should match the location of the
+// corresponding comment in llvm-include-order.cpp
+#include "cross-file-b.h"
diff --git a/test/clang-tidy/Inputs/Headers/gtest/foo.h b/test/clang-tidy/Inputs/Headers/gtest/foo.h
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/test/clang-tidy/Inputs/Headers/i.h b/test/clang-tidy/Inputs/Headers/i.h
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/test/clang-tidy/Inputs/Headers/j.h b/test/clang-tidy/Inputs/Headers/j.h
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/test/clang-tidy/Inputs/Headers/llvm-c/d.h b/test/clang-tidy/Inputs/Headers/llvm-c/d.h
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/test/clang-tidy/Inputs/Headers/llvm/a.h b/test/clang-tidy/Inputs/Headers/llvm/a.h
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/test/clang-tidy/Inputs/Headers/s.h b/test/clang-tidy/Inputs/Headers/s.h
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/test/clang-tidy/Inputs/Headers/system-header-simulation.h b/test/clang-tidy/Inputs/Headers/system-header-simulation.h
new file mode 100644 (file)
index 0000000..0df5305
--- /dev/null
@@ -0,0 +1,24 @@
+#pragma clang system_header
+
+namespace std {
+
+template<class T, T v>
+struct integral_constant {
+    static constexpr T value = v;
+    typedef T value_type;
+    typedef integral_constant type;
+    constexpr operator value_type() const noexcept { return value; }
+};
+
+template <bool B>
+using bool_constant = integral_constant<bool, B>;
+using true_type = bool_constant<true>;
+using false_type = bool_constant<false>;
+
+template<class T>
+struct is_error_code_enum : false_type {};
+
+template<class T>
+void swap(T &a, T &b);
+}
+
diff --git a/test/clang-tidy/Inputs/compilation-database/template.json b/test/clang-tidy/Inputs/compilation-database/template.json
new file mode 100644 (file)
index 0000000..74275d9
--- /dev/null
@@ -0,0 +1,32 @@
+[
+{
+  "directory": "test_dir/a",
+  "command": "clang++ -o test.o test_dir/a/a.cpp",
+  "file": "test_dir/a/a.cpp"
+},
+{
+  "directory": "test_dir/a",
+  "command": "clang++ -o test.o test_dir/a/b.cpp",
+  "file": "test_dir/a/b.cpp"
+},
+{
+  "directory": "test_dir/",
+  "command": "clang++ -o test.o test_dir/b/b.cpp",
+  "file": "test_dir/b/b.cpp"
+},
+{
+  "directory": "test_dir/b",
+  "command": "clang++ -o test.o ../b/c.cpp",
+  "file": "test_dir/b/c.cpp"
+},
+{
+  "directory": "test_dir/b",
+  "command": "clang++ -I../include -o test.o ../b/d.cpp",
+  "file": "test_dir/b/d.cpp"
+},
+{
+  "directory": "test_dir/",
+  "command": "clang++ -o test.o test_dir/b/not-exist.cpp",
+  "file": "test_dir/b/not-exist.cpp"
+}
+]
diff --git a/test/clang-tidy/Inputs/config-files/.clang-tidy b/test/clang-tidy/Inputs/config-files/.clang-tidy
new file mode 100644 (file)
index 0000000..942169f
--- /dev/null
@@ -0,0 +1,2 @@
+Checks: 'from-parent'
+HeaderFilterRegex: 'parent'
diff --git a/test/clang-tidy/Inputs/config-files/1/.clang-tidy b/test/clang-tidy/Inputs/config-files/1/.clang-tidy
new file mode 100644 (file)
index 0000000..800fd4e
--- /dev/null
@@ -0,0 +1,2 @@
+Checks: 'from-child1'
+HeaderFilterRegex: 'child1'
diff --git a/test/clang-tidy/Inputs/explain-config/.clang-tidy b/test/clang-tidy/Inputs/explain-config/.clang-tidy
new file mode 100644 (file)
index 0000000..4fbd09c
--- /dev/null
@@ -0,0 +1 @@
+Checks: '-*,modernize-use-nullptr'
diff --git a/test/clang-tidy/Inputs/file-filter/header1.h b/test/clang-tidy/Inputs/file-filter/header1.h
new file mode 100644 (file)
index 0000000..ba9e3d1
--- /dev/null
@@ -0,0 +1 @@
+class A1 { A1(int); };
diff --git a/test/clang-tidy/Inputs/file-filter/header2.h b/test/clang-tidy/Inputs/file-filter/header2.h
new file mode 100644 (file)
index 0000000..09bfeab
--- /dev/null
@@ -0,0 +1 @@
+class A2 { A2(int); };
diff --git a/test/clang-tidy/Inputs/file-filter/system/system-header.h b/test/clang-tidy/Inputs/file-filter/system/system-header.h
new file mode 100644 (file)
index 0000000..98482c4
--- /dev/null
@@ -0,0 +1 @@
+class A0 { A0(int); };
diff --git a/test/clang-tidy/Inputs/google-namespaces.h b/test/clang-tidy/Inputs/google-namespaces.h
new file mode 100644 (file)
index 0000000..f4d87f5
--- /dev/null
@@ -0,0 +1,7 @@
+namespace {
+int x;
+}
+
+namespace spaaaace {
+class core;
+}
diff --git a/test/clang-tidy/Inputs/line-filter/header1.h b/test/clang-tidy/Inputs/line-filter/header1.h
new file mode 100644 (file)
index 0000000..35d59ce
--- /dev/null
@@ -0,0 +1,3 @@
+class A1 { A1(int); };
+class B1 { B1(int); };
+class C1 { C1(int); };
diff --git a/test/clang-tidy/Inputs/line-filter/header2.h b/test/clang-tidy/Inputs/line-filter/header2.h
new file mode 100644 (file)
index 0000000..bb12598
--- /dev/null
@@ -0,0 +1,3 @@
+class A2 { A2(int); };
+class B2 { B2(int); };
+class C2 { C2(int); };
diff --git a/test/clang-tidy/Inputs/line-filter/header3.h b/test/clang-tidy/Inputs/line-filter/header3.h
new file mode 100644 (file)
index 0000000..b5198ca
--- /dev/null
@@ -0,0 +1 @@
+class A3 { A3(int); };
diff --git a/test/clang-tidy/Inputs/modernize-deprecated-headers/assert.h b/test/clang-tidy/Inputs/modernize-deprecated-headers/assert.h
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/test/clang-tidy/Inputs/modernize-deprecated-headers/complex.h b/test/clang-tidy/Inputs/modernize-deprecated-headers/complex.h
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/test/clang-tidy/Inputs/modernize-deprecated-headers/ctype.h b/test/clang-tidy/Inputs/modernize-deprecated-headers/ctype.h
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/test/clang-tidy/Inputs/modernize-deprecated-headers/errno.h b/test/clang-tidy/Inputs/modernize-deprecated-headers/errno.h
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/test/clang-tidy/Inputs/modernize-deprecated-headers/fenv.h b/test/clang-tidy/Inputs/modernize-deprecated-headers/fenv.h
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/test/clang-tidy/Inputs/modernize-deprecated-headers/float.h b/test/clang-tidy/Inputs/modernize-deprecated-headers/float.h
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/test/clang-tidy/Inputs/modernize-deprecated-headers/inttypes.h b/test/clang-tidy/Inputs/modernize-deprecated-headers/inttypes.h
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/test/clang-tidy/Inputs/modernize-deprecated-headers/iso646.h b/test/clang-tidy/Inputs/modernize-deprecated-headers/iso646.h
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/test/clang-tidy/Inputs/modernize-deprecated-headers/limits.h b/test/clang-tidy/Inputs/modernize-deprecated-headers/limits.h
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/test/clang-tidy/Inputs/modernize-deprecated-headers/locale.h b/test/clang-tidy/Inputs/modernize-deprecated-headers/locale.h
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/test/clang-tidy/Inputs/modernize-deprecated-headers/math.h b/test/clang-tidy/Inputs/modernize-deprecated-headers/math.h
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/test/clang-tidy/Inputs/modernize-deprecated-headers/setjmp.h b/test/clang-tidy/Inputs/modernize-deprecated-headers/setjmp.h
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/test/clang-tidy/Inputs/modernize-deprecated-headers/signal.h b/test/clang-tidy/Inputs/modernize-deprecated-headers/signal.h
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/test/clang-tidy/Inputs/modernize-deprecated-headers/stdalign.h b/test/clang-tidy/Inputs/modernize-deprecated-headers/stdalign.h
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/test/clang-tidy/Inputs/modernize-deprecated-headers/stdarg.h b/test/clang-tidy/Inputs/modernize-deprecated-headers/stdarg.h
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/test/clang-tidy/Inputs/modernize-deprecated-headers/stdbool.h b/test/clang-tidy/Inputs/modernize-deprecated-headers/stdbool.h
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/test/clang-tidy/Inputs/modernize-deprecated-headers/stddef.h b/test/clang-tidy/Inputs/modernize-deprecated-headers/stddef.h
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/test/clang-tidy/Inputs/modernize-deprecated-headers/stdint.h b/test/clang-tidy/Inputs/modernize-deprecated-headers/stdint.h
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/test/clang-tidy/Inputs/modernize-deprecated-headers/stdio.h b/test/clang-tidy/Inputs/modernize-deprecated-headers/stdio.h
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/test/clang-tidy/Inputs/modernize-deprecated-headers/stdlib.h b/test/clang-tidy/Inputs/modernize-deprecated-headers/stdlib.h
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/test/clang-tidy/Inputs/modernize-deprecated-headers/string.h b/test/clang-tidy/Inputs/modernize-deprecated-headers/string.h
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/test/clang-tidy/Inputs/modernize-deprecated-headers/tgmath.h b/test/clang-tidy/Inputs/modernize-deprecated-headers/tgmath.h
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/test/clang-tidy/Inputs/modernize-deprecated-headers/time.h b/test/clang-tidy/Inputs/modernize-deprecated-headers/time.h
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/test/clang-tidy/Inputs/modernize-deprecated-headers/uchar.h b/test/clang-tidy/Inputs/modernize-deprecated-headers/uchar.h
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/test/clang-tidy/Inputs/modernize-deprecated-headers/wchar.h b/test/clang-tidy/Inputs/modernize-deprecated-headers/wchar.h
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/test/clang-tidy/Inputs/modernize-deprecated-headers/wctype.h b/test/clang-tidy/Inputs/modernize-deprecated-headers/wctype.h
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/test/clang-tidy/Inputs/modernize-loop-convert/structures.h b/test/clang-tidy/Inputs/modernize-loop-convert/structures.h
new file mode 100644 (file)
index 0000000..02d440c
--- /dev/null
@@ -0,0 +1,206 @@
+#ifndef STRUCTURES_H
+#define STRUCTURES_H
+
+extern "C" {
+extern int printf(const char *restrict, ...);
+}
+
+struct Val {int X; void g(); };
+
+struct MutableVal {
+  void constFun(int) const;
+  void nonConstFun(int, int);
+  void constFun(MutableVal &) const;
+  void constParamFun(const MutableVal &) const;
+  void nonConstParamFun(const MutableVal &);
+  int X;
+};
+
+struct NonTriviallyCopyable {
+  NonTriviallyCopyable() = default;
+  // Define this constructor to make this class non-trivially copyable.
+  NonTriviallyCopyable(const NonTriviallyCopyable& Ntc);
+  int X;
+};
+
+struct TriviallyCopyableButBig {
+  int X;
+  char Array[16];
+};
+
+struct S {
+  typedef MutableVal *iterator;
+  typedef const MutableVal *const_iterator;
+  const_iterator begin() const;
+  const_iterator end() const;
+  const_iterator cbegin() const;
+  const_iterator cend() const;
+  iterator begin();
+  iterator end();
+};
+
+struct T {
+  struct iterator {
+    int& operator*();
+    const int& operator*()const;
+    iterator& operator ++();
+    bool operator!=(const iterator &other);
+    void insert(int);
+    int X;
+  };
+  iterator begin();
+  iterator end();
+};
+
+struct U {
+  struct iterator {
+    Val& operator*();
+    const Val& operator*()const;
+    iterator& operator ++();
+    bool operator!=(const iterator &other);
+    Val *operator->();
+  };
+  iterator begin();
+  iterator end();
+  int X;
+};
+
+struct X {
+  S Ss;
+  T Tt;
+  U Uu;
+  S getS();
+};
+
+template<typename ElemType>
+class dependent {
+ public:
+  dependent<ElemType>();
+  struct iterator_base {
+    const ElemType& operator*()const;
+    iterator_base& operator ++();
+    bool operator!=(const iterator_base &other) const;
+    const ElemType *operator->() const;
+  };
+
+  struct iterator : iterator_base {
+    ElemType& operator*();
+    iterator& operator ++();
+    ElemType *operator->();
+  };
+
+  typedef iterator_base const_iterator;
+  const_iterator begin() const;
+  const_iterator end() const;
+  iterator begin();
+  iterator end();
+  unsigned size() const;
+  ElemType & operator[](unsigned);
+  const ElemType & operator[](unsigned) const;
+  ElemType & at(unsigned);
+  ElemType & at(unsigned, unsigned);
+  const ElemType & at(unsigned) const;
+
+  // Intentionally evil.
+  dependent<ElemType> operator*();
+
+  void foo();
+  void constFoo() const;
+};
+
+template<typename First, typename Second>
+class doublyDependent{
+ public:
+  struct Value {
+    First first;
+    Second second;
+  };
+
+  struct iterator_base {
+    const Value& operator*()const;
+    iterator_base& operator ++();
+    bool operator!=(const iterator_base &other) const;
+    const Value *operator->() const;
+  };
+
+  struct iterator : iterator_base {
+    Value& operator*();
+    Value& operator ++();
+    Value *operator->();
+  };
+
+  typedef iterator_base const_iterator;
+  const_iterator begin() const;
+  const_iterator end() const;
+  iterator begin();
+  iterator end();
+};
+
+template<typename Contained>
+class transparent {
+ public:
+  Contained *at();
+  Contained *operator->();
+  Contained operator*();
+};
+
+template<typename IteratorType>
+struct Nested {
+  typedef IteratorType* iterator;
+  typedef const IteratorType* const_iterator;
+  IteratorType *operator->();
+  IteratorType operator*();
+  iterator begin();
+  iterator end();
+  const_iterator begin() const;
+  const_iterator end() const;
+};
+
+// Like llvm::SmallPtrSet, the iterator has a dereference operator that returns
+// by value instead of by reference.
+template <typename T>
+struct PtrSet {
+  struct iterator {
+    bool operator!=(const iterator &other) const;
+    const T operator*();
+    iterator &operator++();
+  };
+  iterator begin() const;
+  iterator end() const;
+};
+
+template <typename T>
+struct TypedefDerefContainer {
+  struct iterator {
+    typedef T &deref_type;
+    bool operator!=(const iterator &other) const;
+    deref_type operator*();
+    iterator &operator++();
+  };
+  iterator begin() const;
+  iterator end() const;
+};
+
+template <typename T>
+struct RValueDerefContainer {
+  struct iterator {
+    typedef T &&deref_type;
+    bool operator!=(const iterator &other) const;
+    deref_type operator*();
+    iterator &operator++();
+  };
+  iterator begin() const;
+  iterator end() const;
+};
+
+namespace Macros {
+
+struct MacroStruct {
+  int Arr[10];
+};
+static MacroStruct *MacroSt;
+#define CONT MacroSt->
+
+} // namespace Macros
+
+#endif  // STRUCTURES_H
diff --git a/test/clang-tidy/Inputs/modernize-pass-by-value/header-with-fix.h b/test/clang-tidy/Inputs/modernize-pass-by-value/header-with-fix.h
new file mode 100644 (file)
index 0000000..62609e2
--- /dev/null
@@ -0,0 +1,8 @@
+struct S {
+  S(S&&);
+  S(const S&);
+};
+struct Foo {
+  Foo(const S &s);
+  S s;
+};
diff --git a/test/clang-tidy/Inputs/modernize-pass-by-value/header.h b/test/clang-tidy/Inputs/modernize-pass-by-value/header.h
new file mode 100644 (file)
index 0000000..c2103cb
--- /dev/null
@@ -0,0 +1,10 @@
+class ThreadId {
+public:
+  ThreadId(const ThreadId &) {}
+  ThreadId(ThreadId &&) {}
+};
+
+struct A {
+  A(const ThreadId &tid) : threadid(tid) {}
+  ThreadId threadid;
+};
diff --git a/test/clang-tidy/Inputs/modernize-replace-auto-ptr/memory.h b/test/clang-tidy/Inputs/modernize-replace-auto-ptr/memory.h
new file mode 100644 (file)
index 0000000..bc476ce
--- /dev/null
@@ -0,0 +1,45 @@
+#ifndef INPUTS_MEMORY_H
+#define INPUTS_MEMORY_H
+
+namespace std {
+
+inline namespace _1 {
+
+template <class Y> struct auto_ptr_ref {
+  Y *y_;
+};
+
+template <class X> class auto_ptr {
+public:
+  typedef X element_type;
+  explicit auto_ptr(X *p = 0) throw() {}
+  auto_ptr(auto_ptr &) throw() {}
+  template <class Y> auto_ptr(auto_ptr<Y> &) throw() {}
+  auto_ptr &operator=(auto_ptr &) throw() { return *this; }
+  template <class Y> auto_ptr &operator=(auto_ptr<Y> &) throw() {
+    return *this;
+  }
+  auto_ptr &operator=(auto_ptr_ref<X> r) throw() { return *this; }
+  ~auto_ptr() throw() {}
+  auto_ptr(auto_ptr_ref<X> r) throw() : x_(r.y_) {}
+  template <class Y> operator auto_ptr_ref<Y>() throw() {
+    auto_ptr_ref<Y> r;
+    r.y_ = x_;
+    return r;
+  }
+  template <class Y> operator auto_ptr<Y>() throw() { return auto_ptr<Y>(x_); }
+
+private:
+  X *x_;
+};
+
+template <> class auto_ptr<void> {
+public:
+  typedef void element_type;
+};
+
+} // namespace _1
+
+} // end namespace std
+
+#endif // INPUTS_MEMORY_H
diff --git a/test/clang-tidy/Inputs/modernize-smart-ptr/shared_ptr.h b/test/clang-tidy/Inputs/modernize-smart-ptr/shared_ptr.h
new file mode 100644 (file)
index 0000000..0f4f2a9
--- /dev/null
@@ -0,0 +1,24 @@
+namespace std {
+
+template <typename type>
+class shared_ptr {
+public:
+  shared_ptr();
+  shared_ptr(type *ptr);
+  shared_ptr(const shared_ptr<type> &t) {}
+  shared_ptr(shared_ptr<type> &&t) {}
+  ~shared_ptr();
+  type &operator*() { return *ptr; }
+  type *operator->() { return ptr; }
+  type *release();
+  void reset();
+  void reset(type *pt);
+  shared_ptr &operator=(shared_ptr &&);
+  template <typename T>
+  shared_ptr &operator=(shared_ptr<T> &&);
+
+private:
+  type *ptr;
+};
+
+}  // namespace std
diff --git a/test/clang-tidy/Inputs/modernize-smart-ptr/unique_ptr.h b/test/clang-tidy/Inputs/modernize-smart-ptr/unique_ptr.h
new file mode 100644 (file)
index 0000000..4fc3da1
--- /dev/null
@@ -0,0 +1,28 @@
+namespace std {
+
+template <typename T>
+class default_delete {};
+
+template <typename type, typename Deleter = std::default_delete<type>>
+class unique_ptr {
+public:
+  unique_ptr();
+  unique_ptr(type *ptr);
+  unique_ptr(const unique_ptr<type> &t) = delete;
+  unique_ptr(unique_ptr<type> &&t);
+  ~unique_ptr();
+  type &operator*() { return *ptr; }
+  type *operator->() { return ptr; }
+  type *release();
+  void reset();
+  void reset(type *pt);
+  void reset(type pt);
+  unique_ptr &operator=(unique_ptr &&);
+  template <typename T>
+  unique_ptr &operator=(unique_ptr<T> &&);
+
+private:
+  type *ptr;
+};
+
+}  // namespace std
diff --git a/test/clang-tidy/Inputs/modernize-use-auto/containers.h b/test/clang-tidy/Inputs/modernize-use-auto/containers.h
new file mode 100644 (file)
index 0000000..c99b7a4
--- /dev/null
@@ -0,0 +1,253 @@
+#ifndef CONTAINERS_H
+#define CONTAINERS_H
+
+namespace std {
+
+template <typename T>
+class iterator {
+public:
+  iterator() {}
+  iterator(const iterator<T> &iter) : ptr(iter.ptr) {}
+
+  typedef T value_type;
+  typedef T *pointer;
+  typedef T &reference;
+
+  reference operator*() const { return *ptr; }
+  pointer operator->() const { return ptr; }
+  iterator &operator++() {
+    ++ptr;
+    return *this;
+  }
+  iterator &operator--() {
+    --ptr;
+    return *this;
+  }
+  iterator operator++(int) {
+    iterator res(*this);
+    ++ptr;
+    return res;
+  }
+  iterator operator--(int) {
+    iterator res(*this);
+    --ptr;
+    return res;
+  }
+  bool operator!=(const iterator<T> &iter) const {
+    return ptr != iter.operator->();
+  }
+
+private:
+  T *ptr;
+};
+
+template <class Iterator>
+class const_iterator {
+public:
+  const_iterator() {}
+  const_iterator(const Iterator &iter) : iter(iter) {}
+  const_iterator(const const_iterator<Iterator> &citer) : iter(citer.iter) {}
+
+  typedef const typename Iterator::value_type value_type;
+  typedef const typename Iterator::pointer pointer;
+  typedef const typename Iterator::reference reference;
+
+  reference operator*() const { return *iter; }
+  pointer operator->() const { return iter.operator->(); }
+
+  const_iterator &operator++() { return ++iter; }
+  const_iterator &operator--() { return --iter; }
+  const_iterator operator++(int) { return iter--; }
+  const_iterator operator--(int) { return iter--; }
+
+  bool operator!=(const Iterator &it) const {
+    return iter->operator->() != it.operator->();
+  }
+  bool operator!=(const const_iterator<Iterator> &it) const {
+    return iter.operator->() != it.operator->();
+  }
+
+private:
+  Iterator iter;
+};
+
+template <class Iterator>
+class forward_iterable {
+public:
+  forward_iterable() {}
+  typedef Iterator iterator;
+  typedef const_iterator<Iterator> const_iterator;
+
+  iterator begin() { return _begin; }
+  iterator end() { return _end; }
+
+  const_iterator begin() const { return _begin; }
+  const_iterator end() const { return _end; }
+
+  const_iterator cbegin() const { return _begin; }
+  const_iterator cend() const { return _end; }
+
+private:
+  iterator _begin, _end;
+};
+
+template <class Iterator>
+class reverse_iterator {
+public:
+  reverse_iterator() {}
+  reverse_iterator(const Iterator &iter) : iter(iter) {}
+  reverse_iterator(const reverse_iterator<Iterator> &rit) : iter(rit.iter) {}
+
+  typedef typename Iterator::value_type value_type;
+  typedef typename Iterator::pointer pointer;
+  typedef typename Iterator::reference reference;
+
+  reference operator*() { return *iter; }
+  pointer operator->() { return iter.operator->(); }
+
+  reverse_iterator &operator++() { return --iter; }
+  reverse_iterator &operator--() { return ++iter; }
+  reverse_iterator operator++(int) { return iter--; }
+  reverse_iterator operator--(int) { return iter++; }
+
+private:
+  Iterator iter;
+};
+
+template <class Iterator>
+class backward_iterable {
+public:
+  backward_iterable() {}
+
+  typedef reverse_iterator<Iterator> reverse_iterator;
+  typedef const_iterator<reverse_iterator> const_reverse_iterator;
+
+  reverse_iterator rbegin() { return _rbegin; }
+  reverse_iterator rend() { return _rend; }
+
+  const_reverse_iterator rbegin() const { return _rbegin; }
+  const_reverse_iterator rend() const { return _rend; }
+
+  const_reverse_iterator crbegin() const { return _rbegin; }
+  const_reverse_iterator crend() const { return _rend; }
+
+private:
+  reverse_iterator _rbegin, _rend;
+};
+
+template <class Iterator>
+class bidirectional_iterable : public forward_iterable<Iterator>,
+                               public backward_iterable<Iterator> {};
+
+template <typename A, typename B>
+struct pair {
+  pair(A f, B s) : first(f), second(s) {}
+  A first;
+  B second;
+};
+
+class string {
+public:
+  string() {}
+  string(const char *) {}
+};
+
+template <typename T, int n>
+class array : public backward_iterable<iterator<T>> {
+public:
+  array() {}
+
+  typedef T *iterator;
+  typedef const T *const_iterator;
+
+  iterator begin() { return &v[0]; }
+  iterator end() { return &v[n - 1]; }
+
+  const_iterator begin() const { return &v[0]; }
+  const_iterator end() const { return &v[n - 1]; }
+
+  const_iterator cbegin() const { return &v[0]; }
+  const_iterator cend() const { return &v[n - 1]; }
+
+private:
+  T v[n];
+};
+
+template <typename T>
+class deque : public bidirectional_iterable<iterator<T>> {
+public:
+  deque() {}
+};
+
+template <typename T>
+class list : public bidirectional_iterable<iterator<T>> {
+public:
+  list() {}
+};
+
+template <typename T>
+class forward_list : public forward_iterable<iterator<T>> {
+public:
+  forward_list() {}
+};
+
+template <typename T>
+class vector : public bidirectional_iterable<iterator<T>> {
+public:
+  vector() {}
+};
+
+template <typename T>
+class set : public bidirectional_iterable<iterator<T>> {
+public:
+  set() {}
+};
+
+template <typename T>
+class multiset : public bidirectional_iterable<iterator<T>> {
+public:
+  multiset() {}
+};
+
+template <typename key, typename value>
+class map : public bidirectional_iterable<iterator<pair<key, value>>> {
+public:
+  map() {}
+
+  iterator<pair<key, value>> find(const key &) {}
+  const_iterator<iterator<pair<key, value>>> find(const key &) const {}
+};
+
+template <typename key, typename value>
+class multimap : public bidirectional_iterable<iterator<pair<key, value>>> {
+public:
+  multimap() {}
+};
+
+template <typename T>
+class unordered_set : public forward_iterable<iterator<T>> {
+public:
+  unordered_set() {}
+};
+
+template <typename T>
+class unordered_multiset : public forward_iterable<iterator<T>> {
+public:
+  unordered_multiset() {}
+};
+
+template <typename key, typename value>
+class unordered_map : public forward_iterable<iterator<pair<key, value>>> {
+public:
+  unordered_map() {}
+};
+
+template <typename key, typename value>
+class unordered_multimap : public forward_iterable<iterator<pair<key, value>>> {
+public:
+  unordered_multimap() {}
+};
+
+} // namespace std
+
+#endif // CONTAINERS_H
diff --git a/test/clang-tidy/Inputs/mpi-type-mismatch/mpimock.h b/test/clang-tidy/Inputs/mpi-type-mismatch/mpimock.h
new file mode 100644 (file)
index 0000000..cb90028
--- /dev/null
@@ -0,0 +1,64 @@
+// This Message Passing Interface mock header is used, to mock typedefs,
+// constants and functions, required for integration tests being part of
+// clang-tidy MPI checks.
+
+#ifndef MPIMOCK_H
+#define MPIMOCK_H
+
+#define NULL 0
+
+// These typedefs are used to mock MPI types, fixed width integer types and the
+// templated C++ complex number type.
+typedef int MPI_Datatype;
+typedef int MPI_Comm;
+typedef int MPI_Request;
+typedef int MPI_Status;
+typedef int MPI_Op;
+typedef int int8_t;
+typedef int uint8_t;
+typedef int uint16_t;
+typedef int int64_t;
+namespace std { template<class T> struct complex { T real; T imag; }; }
+
+// These defines are used to mock MPI constants.
+#define MPI_DATATYPE_NULL 0
+#define MPI_CHAR 0
+#define MPI_BYTE 0
+#define MPI_SHORT 0
+#define MPI_INT 0
+#define MPI_LONG 0
+#define MPI_LONG_DOUBLE 0
+#define MPI_UNSIGNED 0
+#define MPI_INT8_T 0
+#define MPI_UINT8_T 0
+#define MPI_UINT16_T 0
+#define MPI_C_FLOAT_COMPLEX 0
+#define MPI_C_LONG_DOUBLE_COMPLEX 0
+#define MPI_FLOAT 0
+#define MPI_DOUBLE 0
+#define MPI_CXX_BOOL 0
+#define MPI_CXX_FLOAT_COMPLEX 0
+#define MPI_CXX_DOUBLE_COMPLEX 0
+#define MPI_CXX_LONG_DOUBLE_COMPLEX 0
+#define MPI_IN_PLACE 0
+#define MPI_COMM_WORLD 0
+#define MPI_STATUS_IGNORE 0
+#define MPI_STATUSES_IGNORE 0
+#define MPI_SUM 0
+
+// These declarations are used to mock MPI functions.
+int MPI_Comm_size(MPI_Comm, int *);
+int MPI_Comm_rank(MPI_Comm, int *);
+int MPI_Send(const void *, int, MPI_Datatype, int, int, MPI_Comm);
+int MPI_Recv(void *, int, MPI_Datatype, int, int, MPI_Comm, MPI_Status *);
+int MPI_Isend(const void *, int, MPI_Datatype, int, int, MPI_Comm,
+    MPI_Request *);
+int MPI_Irecv(void *, int, MPI_Datatype, int, int, MPI_Comm, MPI_Request *);
+int MPI_Wait(MPI_Request *, MPI_Status *);
+int MPI_Waitall(int, MPI_Request[], MPI_Status[]);
+int MPI_Reduce(const void *, void *, int, MPI_Datatype, MPI_Op, int, MPI_Comm);
+int MPI_Ireduce(const void *, void *, int, MPI_Datatype, MPI_Op, int, MPI_Comm,
+    MPI_Request *);
+int MPI_Bcast(void *, int count, MPI_Datatype, int, MPI_Comm);
+
+#endif  // end of include guard: MPIMOCK_H
diff --git a/test/clang-tidy/Inputs/nolint/trigger_warning.h b/test/clang-tidy/Inputs/nolint/trigger_warning.h
new file mode 100644 (file)
index 0000000..e8ebed2
--- /dev/null
@@ -0,0 +1,5 @@
+void A1(const int &In, int &Out) {
+  if (In > 123) // NOLINT
+    Out = 123;
+}
+
diff --git a/test/clang-tidy/Inputs/overlapping/o.h b/test/clang-tidy/Inputs/overlapping/o.h
new file mode 100644 (file)
index 0000000..bfa7a0a
--- /dev/null
@@ -0,0 +1,9 @@
+// run: clang-tidy -checks=-*,llvm-include-order -header-filter=.* %s \
+// run:   -- -isystem %S/Inputs/Headers -I %S/Inputs/overlapping | \
+// run:   not grep "note: this fix will not be applied because it overlaps with another fix"
+
+#include "b.h"
+#include "a.h"
+
+// The comments above are there to match the offset of the #include with the
+// offset of the #includes in the .cpp file.
diff --git a/test/clang-tidy/Inputs/performance-unnecessary-value-param/header-fixed.h b/test/clang-tidy/Inputs/performance-unnecessary-value-param/header-fixed.h
new file mode 100644 (file)
index 0000000..f3e9e72
--- /dev/null
@@ -0,0 +1,15 @@
+// struct ABC is expensive to copy and should be
+// passed as a const referece.
+struct ABC {
+  ABC(const ABC&);
+  int get(int) const;
+};
+
+
+int f1(int n,              const ABC& v1,   const ABC& v2); // line 9
+
+int f1(int n, ABC v1); // line 11
+
+
+
+int f2(        int n,       const ABC& v2); // line 15
diff --git a/test/clang-tidy/Inputs/performance-unnecessary-value-param/header.h b/test/clang-tidy/Inputs/performance-unnecessary-value-param/header.h
new file mode 100644 (file)
index 0000000..3f55c79
--- /dev/null
@@ -0,0 +1,15 @@
+// struct ABC is expensive to copy and should be
+// passed as a const referece.
+struct ABC {
+  ABC(const ABC&);
+  int get(int) const;
+};
+
+
+int f1(int n,              ABC v1,   ABC v2); // line 9
+
+int f1(int n, ABC v1); // line 11
+
+
+
+int f2(        int n,       ABC v2); // line 15
diff --git a/test/clang-tidy/Inputs/readability-identifier-naming/system/system-header.h b/test/clang-tidy/Inputs/readability-identifier-naming/system/system-header.h
new file mode 100644 (file)
index 0000000..8a394a9
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef SYSTEM_HEADER_H
+#define SYSTEM_HEADER_H
+
+#define SYSTEM_MACRO(m) int m = 0
+
+namespace SYSTEM_NS {
+class structure {
+  int member;
+};
+
+float global;
+
+void function() {
+}
+}
+
+#endif // SYSTEM_HEADER_H
diff --git a/test/clang-tidy/Inputs/readability-identifier-naming/user-header.h b/test/clang-tidy/Inputs/readability-identifier-naming/user-header.h
new file mode 100644 (file)
index 0000000..f03982c
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef USER_HEADER_H
+#define USER_HEADER_H
+
+#define USER_MACRO(m) int m = 0
+
+namespace USER_NS {
+class object {
+  int member;
+};
+
+float global;
+
+void function() {
+}
+}
+
+#endif // USER_HEADER_H
diff --git a/test/clang-tidy/Inputs/unused-using-decls.h b/test/clang-tidy/Inputs/unused-using-decls.h
new file mode 100644 (file)
index 0000000..8245cb1
--- /dev/null
@@ -0,0 +1,11 @@
+class MyClass {
+public:
+  template <template <typename> class S, typename T>
+  S<T> *func1(T *a) {
+    return new S<T>();
+  }
+  template <typename T, T (*S)()>
+  void func2(T a) {
+    S();
+  }
+};
diff --git a/test/clang-tidy/android-cloexec-creat.cpp b/test/clang-tidy/android-cloexec-creat.cpp
new file mode 100644 (file)
index 0000000..df57b10
--- /dev/null
@@ -0,0 +1,35 @@
+// RUN: %check_clang_tidy %s android-cloexec-creat %t
+
+typedef int mode_t;
+
+extern "C" int creat(const char *path, mode_t, ...);
+extern "C" int create(const char *path, mode_t, ...);
+
+void f() {
+  creat("filename", 0);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: prefer open() to creat() because open() allows O_CLOEXEC [android-cloexec-creat]
+  // CHECK-FIXES: open ("filename", O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0);
+  create("filename", 0);
+  // CHECK-MESSAGES-NOT: warning:
+  mode_t mode = 0755;
+  creat("filename", mode);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning:
+  // CHECK-FIXES: open ("filename", O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, mode);
+}
+
+namespace i {
+int creat(const char *path, mode_t, ...);
+void g() {
+  creat("filename", 0);
+  // CHECK-MESSAGES-NOT: warning:
+}
+} // namespace i
+
+class C {
+public:
+  int creat(const char *path, mode_t, ...);
+  void h() {
+    creat("filename", 0);
+    // CHECK-MESSAGES-NOT: warning:
+  }
+};
diff --git a/test/clang-tidy/android-cloexec-fopen.cpp b/test/clang-tidy/android-cloexec-fopen.cpp
new file mode 100644 (file)
index 0000000..6b6c655
--- /dev/null
@@ -0,0 +1,57 @@
+// RUN: %check_clang_tidy %s android-cloexec-fopen %t
+
+#define FILE_OPEN_RO "r"
+
+typedef int FILE;
+
+extern "C" FILE *fopen(const char *filename, const char *mode, ...);
+extern "C" FILE *open(const char *filename, const char *mode, ...);
+
+void f() {
+  fopen("filename", "r");
+  // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: use 'fopen' mode 'e' to set O_CLOEXEC [android-cloexec-fopen]
+  // CHECK-FIXES: fopen("filename", "re");
+
+  fopen("filename", FILE_OPEN_RO);
+  // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: use 'fopen' mode 'e'
+  // CHECK-FIXES: fopen("filename", FILE_OPEN_RO "e");
+
+  fopen("filename", "er");
+  // CHECK-MESSAGES-NOT: warning:
+  fopen("filename", "re");
+  // CHECK-MESSAGES-NOT: warning:
+  fopen("filename", "e");
+  // CHECK-MESSAGES-NOT: warning:
+  open("filename", "e");
+  // CHECK-MESSAGES-NOT: warning:
+
+  char *str = "r";
+  fopen("filename", str);
+  // CHECK-MESSAGES-NOT: warning:
+  str = "re";
+  fopen("filename", str);
+  // CHECK-MESSAGES-NOT: warning:
+  char arr[2] = "r";
+  fopen("filename", arr);
+  // CHECK-MESSAGES-NOT: warning:
+  char arr2[3] = "re";
+  fopen("filename", arr2);
+  // CHECK-MESSAGES-NOT: warning:
+}
+
+namespace i {
+int *fopen(const char *filename, const char *mode, ...);
+void g() {
+  fopen("filename", "e");
+  // CHECK-MESSAGES-NOT: warning:
+}
+} // namespace i
+
+class C {
+public:
+  int *fopen(const char *filename, const char *mode, ...);
+  void h() {
+    fopen("filename", "e");
+    // CHECK-MESSAGES-NOT: warning:
+  }
+};
diff --git a/test/clang-tidy/android-cloexec-open.cpp b/test/clang-tidy/android-cloexec-open.cpp
new file mode 100644 (file)
index 0000000..4ef1f40
--- /dev/null
@@ -0,0 +1,180 @@
+// RUN: %check_clang_tidy %s android-cloexec-open %t
+
+#define O_RDWR 1
+#define O_EXCL 2
+#define __O_CLOEXEC 3
+#define O_CLOEXEC __O_CLOEXEC
+#define TEMP_FAILURE_RETRY(exp) \
+  ({                            \
+    int _rc;                    \
+    do {                        \
+      _rc = (exp);              \
+    } while (_rc == -1);        \
+  })
+
+extern "C" int open(const char *fn, int flags, ...);
+extern "C" int open64(const char *fn, int flags, ...);
+extern "C" int openat(int dirfd, const char *pathname, int flags, ...);
+
+void a() {
+  open("filename", O_RDWR);
+  // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: 'open' should use O_CLOEXEC where possible [android-cloexec-open]
+  // CHECK-FIXES: O_RDWR | O_CLOEXEC
+  TEMP_FAILURE_RETRY(open("filename", O_RDWR));
+  // CHECK-MESSAGES: :[[@LINE-1]]:45: warning: 'open' should use O_CLOEXEC where
+  // CHECK-FIXES: O_RDWR | O_CLOEXEC
+  open("filename", O_RDWR | O_EXCL);
+  // CHECK-MESSAGES: :[[@LINE-1]]:35: warning: 'open' should use O_CLOEXEC where
+  // CHECK-FIXES: O_RDWR | O_EXCL | O_CLOEXEC
+  TEMP_FAILURE_RETRY(open("filename", O_RDWR | O_EXCL));
+  // CHECK-MESSAGES: :[[@LINE-1]]:54: warning: 'open' should use O_CLOEXEC where
+  // CHECK-FIXES: O_RDWR | O_EXCL | O_CLOEXEC
+}
+
+void b() {
+  open64("filename", O_RDWR);
+  // CHECK-MESSAGES: :[[@LINE-1]]:28: warning: 'open64' should use O_CLOEXEC where possible [android-cloexec-open]
+  // CHECK-FIXES: O_RDWR | O_CLOEXEC
+  TEMP_FAILURE_RETRY(open64("filename", O_RDWR));
+  // CHECK-MESSAGES: :[[@LINE-1]]:47: warning: 'open64' should use O_CLOEXEC where
+  // CHECK-FIXES: O_RDWR | O_CLOEXEC
+  open64("filename", O_RDWR | O_EXCL);
+  // CHECK-MESSAGES: :[[@LINE-1]]:37: warning: 'open64' should use O_CLOEXEC where
+  // CHECK-FIXES: O_RDWR | O_EXCL | O_CLOEXEC
+  TEMP_FAILURE_RETRY(open64("filename", O_RDWR | O_EXCL));
+  // CHECK-MESSAGES: :[[@LINE-1]]:56: warning: 'open64' should use O_CLOEXEC where
+  // CHECK-FIXES: O_RDWR | O_EXCL | O_CLOEXEC
+}
+
+void c() {
+  openat(0, "filename", O_RDWR);
+  // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: 'openat' should use O_CLOEXEC where possible [android-cloexec-open]
+  // CHECK-FIXES: O_RDWR | O_CLOEXEC
+  TEMP_FAILURE_RETRY(openat(0, "filename", O_RDWR));
+  // CHECK-MESSAGES: :[[@LINE-1]]:50: warning: 'openat' should use O_CLOEXEC where
+  // CHECK-FIXES: O_RDWR | O_CLOEXEC
+  openat(0, "filename", O_RDWR | O_EXCL);
+  // CHECK-MESSAGES: :[[@LINE-1]]:40: warning: 'openat' should use O_CLOEXEC where
+  // CHECK-FIXES: O_RDWR | O_EXCL | O_CLOEXEC
+  TEMP_FAILURE_RETRY(openat(0, "filename", O_RDWR | O_EXCL));
+  // CHECK-MESSAGES: :[[@LINE-1]]:59: warning: 'openat' should use O_CLOEXEC where
+  // CHECK-FIXES: O_RDWR | O_EXCL | O_CLOEXEC
+}
+
+void f() {
+  open("filename", 3);
+  // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: 'open' should use O_CLOEXEC where possible [android-cloexec-open]
+  // CHECK-FIXES: 3 | O_CLOEXEC
+  TEMP_FAILURE_RETRY(open("filename", 3));
+  // CHECK-MESSAGES: :[[@LINE-1]]:40: warning: 'open' should use O_CLOEXEC where
+  // CHECK-FIXES: 3 | O_CLOEXEC
+  open64("filename", 3);
+  // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: 'open64' should use O_CLOEXEC where possible [android-cloexec-open]
+  // CHECK-FIXES: 3 | O_CLOEXEC
+  TEMP_FAILURE_RETRY(open64("filename", 3));
+  // CHECK-MESSAGES: :[[@LINE-1]]:42: warning: 'open64' should use O_CLOEXEC where
+  // CHECK-FIXES: 3 | O_CLOEXEC
+  openat(0, "filename", 3);
+  // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: 'openat' should use O_CLOEXEC where possible [android-cloexec-open]
+  // CHECK-FIXES: 3 | O_CLOEXEC
+  TEMP_FAILURE_RETRY(openat(0, "filename", 3));
+  // CHECK-MESSAGES: :[[@LINE-1]]:45: warning: 'openat' should use O_CLOEXEC where
+  // CHECK-FIXES: 3 | O_CLOEXEC
+
+  int flag = 3;
+  open("filename", flag);
+  // CHECK-MESSAGES-NOT: warning:
+  TEMP_FAILURE_RETRY(open("filename", flag));
+  // CHECK-MESSAGES-NOT: warning:
+  open64("filename", flag);
+  // CHECK-MESSAGES-NOT: warning:
+  TEMP_FAILURE_RETRY(open64("filename", flag));
+  // CHECK-MESSAGES-NOT: warning:
+  openat(0, "filename", flag);
+  // CHECK-MESSAGES-NOT: warning:
+  TEMP_FAILURE_RETRY(openat(0, "filename", flag));
+  // CHECK-MESSAGES-NOT: warning:
+}
+
+namespace i {
+int open(const char *pathname, int flags, ...);
+int open64(const char *pathname, int flags, ...);
+int openat(int dirfd, const char *pathname, int flags, ...);
+
+void d() {
+  open("filename", O_RDWR);
+  // CHECK-MESSAGES-NOT: warning:
+  TEMP_FAILURE_RETRY(open("filename", O_RDWR));
+  // CHECK-MESSAGES-NOT: warning:
+  open64("filename", O_RDWR);
+  // CHECK-MESSAGES-NOT: warning:
+  TEMP_FAILURE_RETRY(open64("filename", O_RDWR));
+  // CHECK-MESSAGES-NOT: warning:
+  openat(0, "filename", O_RDWR);
+  // CHECK-MESSAGES-NOT: warning:
+  TEMP_FAILURE_RETRY(openat(0, "filename", O_RDWR));
+  // CHECK-MESSAGES-NOT: warning:
+}
+
+} // namespace i
+
+void e() {
+  open("filename", O_CLOEXEC);
+  // CHECK-MESSAGES-NOT: warning:
+  TEMP_FAILURE_RETRY(open("filename", O_CLOEXEC));
+  // CHECK-MESSAGES-NOT: warning:
+  open("filename", O_RDWR | O_CLOEXEC);
+  // CHECK-MESSAGES-NOT: warning:
+  TEMP_FAILURE_RETRY(open("filename", O_RDWR | O_CLOEXEC));
+  // CHECK-MESSAGES-NOT: warning:
+  open("filename", O_RDWR | O_CLOEXEC | O_EXCL);
+  // CHECK-MESSAGES-NOT: warning:
+  TEMP_FAILURE_RETRY(open("filename", O_RDWR | O_CLOEXEC | O_EXCL));
+  // CHECK-MESSAGES-NOT: warning:
+  open64("filename", O_CLOEXEC);
+  // CHECK-MESSAGES-NOT: warning:
+  TEMP_FAILURE_RETRY(open64("filename", O_CLOEXEC));
+  // CHECK-MESSAGES-NOT: warning:
+  open64("filename", O_RDWR | O_CLOEXEC);
+  // CHECK-MESSAGES-NOT: warning:
+  TEMP_FAILURE_RETRY(open64("filename", O_RDWR | O_CLOEXEC));
+  // CHECK-MESSAGES-NOT: warning:
+  open64("filename", O_RDWR | O_CLOEXEC | O_EXCL);
+  // CHECK-MESSAGES-NOT: warning:
+  TEMP_FAILURE_RETRY(open64("filename", O_RDWR | O_CLOEXEC | O_EXCL));
+  // CHECK-MESSAGES-NOT: warning:
+  openat(0, "filename", O_CLOEXEC);
+  // CHECK-MESSAGES-NOT: warning:
+  TEMP_FAILURE_RETRY(openat(0, "filename", O_CLOEXEC));
+  // CHECK-MESSAGES-NOT: warning:
+  openat(0, "filename", O_RDWR | O_CLOEXEC);
+  // CHECK-MESSAGES-NOT: warning:
+  TEMP_FAILURE_RETRY(openat(0, "filename", O_RDWR | O_CLOEXEC));
+  // CHECK-MESSAGES-NOT: warning:
+  openat(0, "filename", O_RDWR | O_CLOEXEC | O_EXCL);
+  // CHECK-MESSAGES-NOT: warning:
+  TEMP_FAILURE_RETRY(openat(0, "filename", O_RDWR | O_CLOEXEC | O_EXCL));
+  // CHECK-MESSAGES-NOT: warning:
+}
+
+class G {
+public:
+  int open(const char *pathname, int flags, ...);
+  int open64(const char *pathname, int flags, ...);
+  int openat(int dirfd, const char *pathname, int flags, ...);
+
+  void h() {
+    open("filename", O_RDWR);
+    // CHECK-MESSAGES-NOT: warning:
+    TEMP_FAILURE_RETRY(open("filename", O_RDWR));
+    // CHECK-MESSAGES-NOT: warning:
+    open64("filename", O_RDWR);
+    // CHECK-MESSAGES-NOT: warning:
+    TEMP_FAILURE_RETRY(open64("filename", O_RDWR));
+    // CHECK-MESSAGES-NOT: warning:
+    openat(0, "filename", O_RDWR);
+    // CHECK-MESSAGES-NOT: warning:
+    TEMP_FAILURE_RETRY(openat(0, "filename", O_RDWR));
+    // CHECK-MESSAGES-NOT: warning:
+  }
+};
diff --git a/test/clang-tidy/android-cloexec-socket.cpp b/test/clang-tidy/android-cloexec-socket.cpp
new file mode 100644 (file)
index 0000000..25f332d
--- /dev/null
@@ -0,0 +1,75 @@
+// RUN: %check_clang_tidy %s android-cloexec-socket %t
+
+#define SOCK_STREAM 1
+#define SOCK_DGRAM 2
+#define __O_CLOEXEC 3
+#define SOCK_CLOEXEC __O_CLOEXEC
+#define TEMP_FAILURE_RETRY(exp) \
+  ({                            \
+    int _rc;                    \
+    do {                        \
+      _rc = (exp);              \
+    } while (_rc == -1);        \
+  })
+
+extern "C" int socket(int domain, int type, int protocol);
+
+void a() {
+  socket(0, SOCK_STREAM, 0);
+  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: 'socket' should use SOCK_CLOEXEC where possible [android-cloexec-socket]
+  // CHECK-FIXES: socket(0, SOCK_STREAM | SOCK_CLOEXEC, 0)
+  TEMP_FAILURE_RETRY(socket(0, SOCK_STREAM, 0));
+  // CHECK-MESSAGES: :[[@LINE-1]]:43: warning: 'socket'
+  // CHECK-FIXES: TEMP_FAILURE_RETRY(socket(0, SOCK_STREAM | SOCK_CLOEXEC, 0))
+  socket(0, SOCK_STREAM | SOCK_DGRAM, 0);
+  // CHECK-MESSAGES: :[[@LINE-1]]:37: warning: 'socket'
+  // CHECK-FIXES: socket(0, SOCK_STREAM | SOCK_DGRAM | SOCK_CLOEXEC, 0)
+  TEMP_FAILURE_RETRY(socket(0, SOCK_STREAM | SOCK_DGRAM, 0));
+  // CHECK-MESSAGES: :[[@LINE-1]]:56: warning: 'socket'
+  // CHECK-FIXES: TEMP_FAILURE_RETRY(socket(0, SOCK_STREAM | SOCK_DGRAM | SOCK_CLOEXEC, 0))
+}
+
+void f() {
+  socket(0, 3, 0);
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: 'socket'
+  // CHECK-FIXES: socket(0, 3 | SOCK_CLOEXEC, 0)
+  TEMP_FAILURE_RETRY(socket(0, 3, 0));
+  // CHECK-MESSAGES: :[[@LINE-1]]:33: warning: 'socket'
+  // CHECK-FIXES: TEMP_FAILURE_RETRY(socket(0, 3 | SOCK_CLOEXEC, 0))
+
+  int flag = 3;
+  socket(0, flag, 0);
+  TEMP_FAILURE_RETRY(socket(0, flag, 0));
+}
+
+namespace i {
+int socket(int domain, int type, int protocol);
+
+void d() {
+  socket(0, SOCK_STREAM, 0);
+  TEMP_FAILURE_RETRY(socket(0, SOCK_STREAM, 0));
+  socket(0, SOCK_STREAM | SOCK_DGRAM, 0);
+  TEMP_FAILURE_RETRY(socket(0, SOCK_STREAM | SOCK_DGRAM, 0));
+}
+
+} // namespace i
+
+void e() {
+  socket(0, SOCK_CLOEXEC, 0);
+  TEMP_FAILURE_RETRY(socket(0, SOCK_CLOEXEC, 0));
+  socket(0, SOCK_STREAM | SOCK_CLOEXEC, 0);
+  TEMP_FAILURE_RETRY(socket(0, SOCK_STREAM | SOCK_CLOEXEC, 0));
+  socket(0, SOCK_STREAM | SOCK_CLOEXEC | SOCK_DGRAM, 0);
+  TEMP_FAILURE_RETRY(socket(0, SOCK_STREAM | SOCK_CLOEXEC | SOCK_DGRAM, 0));
+}
+
+class G {
+public:
+  int socket(int domain, int type, int protocol);
+  void d() {
+    socket(0, SOCK_STREAM, 0);
+    TEMP_FAILURE_RETRY(socket(0, SOCK_STREAM, 0));
+    socket(0, SOCK_STREAM | SOCK_DGRAM, 0);
+    TEMP_FAILURE_RETRY(socket(0, SOCK_STREAM | SOCK_DGRAM, 0));
+  }
+};
diff --git a/test/clang-tidy/basic.cpp b/test/clang-tidy/basic.cpp
new file mode 100644 (file)
index 0000000..12eb6fe
--- /dev/null
@@ -0,0 +1,6 @@
+// RUN: clang-tidy %s -checks='-*,llvm-namespace-comment' -- | FileCheck %s
+// RUN: c-index-test -test-load-source-reparse 2 all %s -Xclang -add-plugin -Xclang clang-tidy -Xclang -plugin-arg-clang-tidy -Xclang -checks='-*,llvm-namespace-comment' 2>&1 | FileCheck %s
+
+namespace i {
+}
+// CHECK: warning: namespace 'i' not terminated with a closing comment [llvm-namespace-comment]
diff --git a/test/clang-tidy/boost-use-to-string.cpp b/test/clang-tidy/boost-use-to-string.cpp
new file mode 100644 (file)
index 0000000..44ba172
--- /dev/null
@@ -0,0 +1,169 @@
+// RUN: %check_clang_tidy %s boost-use-to-string %t
+
+namespace std {
+
+template <typename T>
+class basic_string {};
+
+using string = basic_string<char>;
+using wstring = basic_string<wchar_t>;
+}
+
+namespace boost {
+template <typename T, typename V>
+T lexical_cast(const V &) {
+  return T();
+};
+}
+
+struct my_weird_type {};
+
+std::string fun(const std::string &) {}
+
+void test_to_string1() {
+
+  auto xa = boost::lexical_cast<std::string>(5);
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use std::to_string instead of boost::lexical_cast<std::string> [boost-use-to-string]
+  // CHECK-FIXES: auto xa = std::to_string(5);
+
+  auto z = boost::lexical_cast<std::string>(42LL);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use std::to_string
+  // CHECK-FIXES: auto z = std::to_string(42LL);
+
+  // this should not trigger
+  fun(boost::lexical_cast<std::string>(42.0));
+  auto non = boost::lexical_cast<my_weird_type>(42);
+  boost::lexical_cast<int>("12");
+}
+
+void test_to_string2() {
+  int a;
+  long b;
+  long long c;
+  unsigned d;
+  unsigned long e;
+  unsigned long long f;
+  float g;
+  double h;
+  long double i;
+  bool j;
+
+  fun(boost::lexical_cast<std::string>(a));
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use std::to_string
+  // CHECK-FIXES: fun(std::to_string(a));
+  fun(boost::lexical_cast<std::string>(b));
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use std::to_string
+  // CHECK-FIXES: fun(std::to_string(b));
+  fun(boost::lexical_cast<std::string>(c));
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use std::to_string
+  // CHECK-FIXES: fun(std::to_string(c));
+  fun(boost::lexical_cast<std::string>(d));
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use std::to_string
+  // CHECK-FIXES: fun(std::to_string(d));
+  fun(boost::lexical_cast<std::string>(e));
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use std::to_string
+  // CHECK-FIXES: fun(std::to_string(e));
+  fun(boost::lexical_cast<std::string>(f));
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use std::to_string
+  // CHECK-FIXES: fun(std::to_string(f));
+
+  // No change for floating numbers.
+  fun(boost::lexical_cast<std::string>(g));
+  fun(boost::lexical_cast<std::string>(h));
+  fun(boost::lexical_cast<std::string>(i));
+  // And bool.
+  fun(boost::lexical_cast<std::string>(j));
+}
+
+std::string fun(const std::wstring &) {}
+
+void test_to_wstring() {
+  int a;
+  long b;
+  long long c;
+  unsigned d;
+  unsigned long e;
+  unsigned long long f;
+  float g;
+  double h;
+  long double i;
+  bool j;
+
+  fun(boost::lexical_cast<std::wstring>(a));
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use std::to_wstring instead of boost::lexical_cast<std::wstring> [boost-use-to-string]
+  // CHECK-FIXES: fun(std::to_wstring(a));
+  fun(boost::lexical_cast<std::wstring>(b));
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use std::to_wstring
+  // CHECK-FIXES: fun(std::to_wstring(b));
+  fun(boost::lexical_cast<std::wstring>(c));
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use std::to_wstring
+  // CHECK-FIXES: fun(std::to_wstring(c));
+  fun(boost::lexical_cast<std::wstring>(d));
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use std::to_wstring
+  // CHECK-FIXES: fun(std::to_wstring(d));
+  fun(boost::lexical_cast<std::wstring>(e));
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use std::to_wstring
+  // CHECK-FIXES: fun(std::to_wstring(e));
+  fun(boost::lexical_cast<std::wstring>(f));
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use std::to_wstring
+  // CHECK-FIXES: fun(std::to_wstring(f));
+
+  // No change for floating numbers
+  fun(boost::lexical_cast<std::wstring>(g));
+  fun(boost::lexical_cast<std::wstring>(h));
+  fun(boost::lexical_cast<std::wstring>(i));
+  // and bool.
+  fun(boost::lexical_cast<std::wstring>(j));
+}
+
+const auto glob = boost::lexical_cast<std::string>(42);
+// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use std::to_string
+// CHECK-FIXES: const auto glob = std::to_string(42);
+
+template <typename T>
+void string_as_T(T t = T()) {
+  boost::lexical_cast<std::string>(42);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use std::to_string
+  // CHECK-FIXES: std::to_string(42);
+
+  boost::lexical_cast<T>(42);
+  string_as_T(boost::lexical_cast<T>(42));
+  auto p = boost::lexical_cast<T>(42);
+  auto p2 = (T)boost::lexical_cast<T>(42);
+  auto p3 = static_cast<T>(boost::lexical_cast<T>(42));
+}
+
+#define my_to_string boost::lexical_cast<std::string>
+
+void no_fixup_inside_macro() {
+  my_to_string(12);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use std::to_string
+}
+
+void no_warnings() {
+  fun(boost::lexical_cast<std::string>("abc"));
+  fun(boost::lexical_cast<std::wstring>("abc"));
+  fun(boost::lexical_cast<std::string>(my_weird_type{}));
+  string_as_T<int>();
+  string_as_T<std::string>();
+}
+
+struct Fields {
+  int integer;
+  float floating;
+  Fields* wierd;
+  const int &getConstInteger() const {return integer;}
+};
+
+void testFields() {
+  Fields fields;
+  auto s1 = boost::lexical_cast<std::string>(fields.integer);
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use std::to_string
+  // CHECK-FIXES: auto s1 = std::to_string(fields.integer);
+
+  auto s2 = boost::lexical_cast<std::string>(fields.floating);
+  auto s3 = boost::lexical_cast<std::string>(fields.wierd);
+  auto s4 = boost::lexical_cast<std::string>(fields.getConstInteger());
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use std::to_string
+  // CHECK-FIXES: auto s4 = std::to_string(fields.getConstInteger());
+}
diff --git a/test/clang-tidy/bugprone-suspicious-memset-usage.cpp b/test/clang-tidy/bugprone-suspicious-memset-usage.cpp
new file mode 100644 (file)
index 0000000..f33ae5a
--- /dev/null
@@ -0,0 +1,77 @@
+// RUN: %check_clang_tidy %s bugprone-suspicious-memset-usage %t
+
+void *memset(void *, int, __SIZE_TYPE__);
+
+namespace std {
+  using ::memset;
+}
+
+template <typename T>
+void mtempl(int *ptr) {
+  memset(ptr, '0', sizeof(T));
+// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: memset fill value is char '0', potentially mistaken for int 0 [bugprone-suspicious-memset-usage]
+// CHECK-FIXES: memset(ptr, 0, sizeof(T));
+  memset(ptr, 256, sizeof(T));
+// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: memset fill value is out of unsigned character range, gets truncated [bugprone-suspicious-memset-usage]
+  memset(0, sizeof(T), 0);
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: memset of size zero, potentially swapped arguments [bugprone-suspicious-memset-usage]
+// CHECK-FIXES: memset(0, 0, sizeof(T));
+  memset(0, sizeof(int), 0);
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: memset of size zero, potentially swapped arguments [bugprone-suspicious-memset-usage]
+// CHECK-FIXES: memset(0, 0, sizeof(int));
+}
+
+void foo(int xsize, int ysize) {
+  int i[5] = {1, 2, 3, 4, 5};
+  char ca[3] = {'a', 'b', 'c'};
+  int *p = i;
+  int l = 5;
+  char z = '1';
+  char *c = &z;
+  int v = 0;
+
+  memset(p, '0', l);
+// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: memset fill value is char '0', potentially mistaken for int 0 [bugprone-suspicious-memset-usage]
+// CHECK-FIXES: memset(p, 0, l);
+
+  memset(p, 0xabcd, l);
+// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: memset fill value is out of unsigned character range, gets truncated [bugprone-suspicious-memset-usage]
+
+  memset(p, sizeof(int), 0);
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: memset of size zero, potentially swapped arguments [bugprone-suspicious-memset-usage]
+// CHECK-FIXES: memset(p, 0, sizeof(int));
+  std::memset(p, sizeof(int), 0x00);
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: memset of size zero, potentially swapped arguments [bugprone-suspicious-memset-usage]
+// CHECK-FIXES: std::memset(p, 0x00, sizeof(int));
+
+#define M_CHAR_ZERO memset(p, '0', l);
+  M_CHAR_ZERO
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: memset fill value is char '0', potentially mistaken for int 0 [bugprone-suspicious-memset-usage]
+
+#define M_OUTSIDE_RANGE memset(p, 0xabcd, l);
+  M_OUTSIDE_RANGE
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: memset fill value is out of unsigned character range, gets truncated [bugprone-suspicious-memset-usage]
+
+#define M_ZERO_LENGTH memset(p, sizeof(int), 0);
+  M_ZERO_LENGTH
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: memset of size zero, potentially swapped arguments [bugprone-suspicious-memset-usage]
+
+  memset(p, '2', l);
+  memset(p, 0, l);
+  memset(c, '0', 1);
+  memset(ca, '0', sizeof(ca));
+
+  memset(p, 0x00, l);
+  mtempl<int>(p);
+
+  memset(p, sizeof(int), v + 1);
+  memset(p, 0xcd, 1);
+
+  // Don't warn when the fill char and the length are both known to be
+  // zero.  No bug is possible.
+  memset(p, 0, v);
+
+  // -1 is clearly not a length by virtue of being negative, so no warning
+  // despite v == 0.
+  memset(p, -1, v);
+}
diff --git a/test/clang-tidy/bugprone-undefined-memory-manipulation.cpp b/test/clang-tidy/bugprone-undefined-memory-manipulation.cpp
new file mode 100644 (file)
index 0000000..2f68bc2
--- /dev/null
@@ -0,0 +1,178 @@
+// RUN: %check_clang_tidy %s bugprone-undefined-memory-manipulation %t
+
+void *memset(void *, int, __SIZE_TYPE__);
+void *memcpy(void *, const void *, __SIZE_TYPE__);
+void *memmove(void *, const void *, __SIZE_TYPE__);
+
+namespace std {
+using ::memcpy;
+using ::memmove;
+using ::memset;
+}
+
+// TriviallyCopyable types:
+struct Plain {
+  int n;
+};
+
+enum E {
+  X,
+  Y,
+  Z
+};
+
+struct Base {
+  float b;
+};
+
+struct Derived : Base {
+  bool d;
+};
+
+// not TriviallyCopyable types:
+struct Destruct {
+  ~Destruct() {}
+};
+
+struct Copy {
+  Copy() {}
+  Copy(const Copy &) {}
+};
+
+struct Move {
+  Move() {}
+  Move(Move &&) {}
+};
+
+struct VirtualFunc {
+  virtual void f() {}
+};
+
+struct VirtualBase : virtual Base {
+  int vb;
+};
+
+template <typename T>
+void memset_temp(T *b) {
+  memset(b, 0, sizeof(T));
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, destination object is not TriviallyCopyable [bugprone-undefined-memory-manipulation]
+}
+
+template <typename S, typename T>
+void memcpy_temp(S *a, T *b) {
+  memcpy(a, b, sizeof(T));
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, source object is not TriviallyCopyable [bugprone-undefined-memory-manipulation]
+}
+
+template <typename S, typename T>
+void memmove_temp(S *a, T *b) {
+  memmove(a, b, sizeof(T));
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, source object is not TriviallyCopyable [bugprone-undefined-memory-manipulation]
+}
+
+void notTriviallyCopyable() {
+  Plain p; // TriviallyCopyable for variety
+  Destruct d;
+  Copy c;
+  Move m;
+  VirtualFunc vf;
+  VirtualBase vb;
+
+  memset(&vf, 0, sizeof(int));
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, destination object is not TriviallyCopyable [bugprone-undefined-memory-manipulation]
+  memset(&d, 0, sizeof(int));
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, destination object is not TriviallyCopyable [bugprone-undefined-memory-manipulation]
+  memset(&c, 0, sizeof(int));
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, destination object is not TriviallyCopyable [bugprone-undefined-memory-manipulation]
+  std::memset(&m, 0, sizeof(int));
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, destination object is not TriviallyCopyable [bugprone-undefined-memory-manipulation]
+  ::memset(&vb, 0, sizeof(int));
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, destination object is not TriviallyCopyable [bugprone-undefined-memory-manipulation]
+
+  memcpy(&p, &vf, sizeof(int));
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, source object is not TriviallyCopyable [bugprone-undefined-memory-manipulation]
+  memcpy(&p, &d, sizeof(int));
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, source object is not TriviallyCopyable [bugprone-undefined-memory-manipulation]
+  memcpy(&c, &p, sizeof(int));
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, destination object is not TriviallyCopyable [bugprone-undefined-memory-manipulation]
+  std::memcpy(&m, &p, sizeof(int));
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, destination object is not TriviallyCopyable [bugprone-undefined-memory-manipulation]
+  ::memcpy(&vb, &p, sizeof(int));
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, destination object is not TriviallyCopyable [bugprone-undefined-memory-manipulation]
+
+  memmove(&vf, &p, sizeof(int));
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, destination object is not TriviallyCopyable [bugprone-undefined-memory-manipulation]
+  memmove(&d, &p, sizeof(int));
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, destination object is not TriviallyCopyable [bugprone-undefined-memory-manipulation]
+  memmove(&p, &c, sizeof(int));
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, source object is not TriviallyCopyable [bugprone-undefined-memory-manipulation]
+  std::memmove(&p, &m, sizeof(int));
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, source object is not TriviallyCopyable [bugprone-undefined-memory-manipulation]
+  ::memmove(&p, &vb, sizeof(int));
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, source object is not TriviallyCopyable [bugprone-undefined-memory-manipulation]
+
+#define MEMSET memset(&vf, 0, sizeof(int));
+  MEMSET
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, destination object is not TriviallyCopyable [bugprone-undefined-memory-manipulation]
+#define MEMCPY memcpy(&d, &p, sizeof(int));
+  MEMCPY
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, destination object is not TriviallyCopyable [bugprone-undefined-memory-manipulation]
+#define MEMMOVE memmove(&p, &c, sizeof(int));
+  MEMMOVE
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: undefined behavior, source object is not TriviallyCopyable [bugprone-undefined-memory-manipulation]
+
+  memset_temp<VirtualFunc>(&vf);
+  memcpy_temp<Plain, VirtualFunc>(&p, &vf);
+  memmove_temp<Plain, VirtualFunc>(&p, &vf);
+}
+
+void triviallyCopyable() {
+  Plain p;
+  Base base;
+  Derived derived;
+
+  int i = 5;
+  int ia[3] = {1, 2, 3};
+  float f = 3.14;
+  float fa[3] = {1.1, 2.2, 3.3};
+  bool b = false;
+  bool ba[2] = {true, false};
+  E e = X;
+  p.n = 2;
+
+  memset(&p, 0, sizeof(int));
+  memset(&base, 0, sizeof(float));
+  memset(&derived, 0, sizeof(bool));
+  memset(&i, 0, sizeof(int));
+  memset(ia, 0, sizeof(int));
+  memset(&f, 0, sizeof(float));
+  memset(fa, 0, sizeof(float));
+  memset(&b, 0, sizeof(bool));
+  memset(ba, 0, sizeof(bool));
+  memset(&e, 0, sizeof(int));
+  memset(&p.n, 0, sizeof(int));
+
+  memcpy(&p, &p, sizeof(int));
+  memcpy(&base, &base, sizeof(float));
+  memcpy(&derived, &derived, sizeof(bool));
+  memcpy(&i, &i, sizeof(int));
+  memcpy(ia, ia, sizeof(int));
+  memcpy(&f, &f, sizeof(float));
+  memcpy(fa, fa, sizeof(float));
+  memcpy(&b, &b, sizeof(bool));
+  memcpy(ba, ba, sizeof(bool));
+  memcpy(&e, &e, sizeof(int));
+  memcpy(&p.n, &p.n, sizeof(int));
+
+  memmove(&p, &p, sizeof(int));
+  memmove(&base, &base, sizeof(float));
+  memmove(&derived, &derived, sizeof(bool));
+  memmove(&i, &i, sizeof(int));
+  memmove(ia, ia, sizeof(int));
+  memmove(&f, &f, sizeof(float));
+  memmove(fa, fa, sizeof(float));
+  memmove(&b, &b, sizeof(bool));
+  memmove(ba, ba, sizeof(bool));
+  memmove(&e, &e, sizeof(int));
+  memmove(&p.n, &p.n, sizeof(int));
+}
diff --git a/test/clang-tidy/cert-dcl21-cpp.cpp b/test/clang-tidy/cert-dcl21-cpp.cpp
new file mode 100644 (file)
index 0000000..c975f70
--- /dev/null
@@ -0,0 +1,134 @@
+// RUN: %check_clang_tidy %s cert-dcl21-cpp %t
+
+class A {};
+
+A operator++(A &, int);
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: overloaded 'operator++' returns a non-constant object instead of a constant object type [cert-dcl21-cpp]
+// CHECK-FIXES: {{^}}const A operator++(A &, int);
+
+A operator--(A &, int);
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: overloaded 'operator--' returns a no
+// CHECK-FIXES: {{^}}const A operator--(A &, int);
+
+class B {};
+
+B &operator++(B &);
+const B operator++(B &, int);
+
+B &operator--(B &);
+const B operator--(B &, int);
+
+
+class D {
+D &operator++();
+const D operator++(int);
+
+D &operator--();
+const D operator--(int);
+};
+
+class C {
+C operator++(int);
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: overloaded 'operator++' returns a no
+// CHECK-FIXES: {{^}}const C operator++(int);
+
+C operator--(int);
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: overloaded 'operator--' returns a no
+// CHECK-FIXES: {{^}}const C operator--(int);
+};
+
+class E {};
+
+E &operator++(E &, int);
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: overloaded 'operator++' returns a reference instead of a constant object type [cert-dcl21-cpp]
+// CHECK-FIXES: {{^}}const E operator++(E &, int);
+
+E &operator--(E &, int);
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: overloaded 'operator--' returns a re
+// CHECK-FIXES: {{^}}const E operator--(E &, int);
+
+class G {
+G &operator++(int);
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: overloaded 'operator++' returns a re
+// CHECK-FIXES: {{^}}const G operator++(int);
+
+G &operator--(int);
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: overloaded 'operator--' returns a re
+// CHECK-FIXES: {{^}}const G operator--(int);
+};
+
+class F {};
+
+const F &operator++(F &, int);
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: overloaded 'operator++' returns a re
+// CHECK-FIXES: {{^}}const F operator++(F &, int);
+
+const F &operator--(F &, int);
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: overloaded 'operator--' returns a re
+// CHECK-FIXES: {{^}}const F operator--(F &, int);
+
+class H {
+const H &operator++(int);
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: overloaded 'operator++' returns a re
+// CHECK-FIXES: {{^}}const H operator++(int);
+
+const H &operator--(int);
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: overloaded 'operator--' returns a re
+// CHECK-FIXES: {{^}}const H operator--(int);
+};
+
+
+#define FROM_MACRO P&
+class P {
+const FROM_MACRO operator++(int);
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: overloaded 'operator++' returns a re
+// CHECK-FIXES: {{^}}const FROM_MACRO operator++(int);
+};
+
+
+template<typename T>
+class Q {
+const Q &operator++(int);
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: overloaded 'operator++' returns a re
+// CHECK-FIXES: {{^}}const Q<T> operator++(int);
+
+const Q &operator--(int);
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: overloaded 'operator--' returns a re
+// CHECK-FIXES: {{^}}const Q<T> operator--(int);
+};
+
+void foobar() {
+  Q<int> a;
+  Q<float> b;
+  (void)a;
+  (void)b;
+}
+
+struct S {};
+typedef S& SRef;
+
+SRef operator++(SRef, int);
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: overloaded 'operator++' returns a re
+// CHECK-FIXES: {{^}}SRef operator++(SRef, int);
+
+struct T {
+  typedef T& TRef;
+  
+  TRef operator++(int);
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: overloaded 'operator++' returns a re
+// CHECK-FIXES: {{^}}  TRef operator++(int);
+};
+
+struct U {
+  typedef const U& ConstURef;
+  
+  ConstURef& operator++(int);
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: overloaded 'operator++' returns a re
+// CHECK-FIXES: {{^}}  ConstURef& operator++(int);
+};
+
+struct V {
+  V *operator++(int);
+  V *const operator--(int);
+};
+
diff --git a/test/clang-tidy/cert-dcl58-cpp.cpp b/test/clang-tidy/cert-dcl58-cpp.cpp
new file mode 100644 (file)
index 0000000..34ee368
--- /dev/null
@@ -0,0 +1,67 @@
+// RUN: %check_clang_tidy %s cert-dcl58-cpp %t -- -- -std=c++1z -I %S/Inputs/Headers
+
+#include "system-header-simulation.h"
+
+namespace A {
+  namespace B {
+    int b;
+  }
+}
+
+namespace A {
+  namespace B {
+    int c;
+  }
+}
+
+namespace posix {
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: modification of 'posix' namespace can result in undefined behavior [cert-dcl58-cpp]
+  namespace vmi {
+  }
+}
+
+namespace std {
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: modification of 'std' namespace can
+  int stdInt;
+}
+
+namespace foobar {
+  namespace std {
+    int bar;
+  }
+}
+
+namespace posix::a {
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: modification of 'posix' namespace 
+}
+
+enum class MyError {
+  ErrorA,
+  ErrorB
+};
+
+namespace std {
+template <>
+struct is_error_code_enum<MyError> : std::true_type {};
+
+template<>
+void swap<MyError>(MyError &a, MyError &b);
+}
+
+enum class MyError2 {
+  Error2A,
+  Error2B
+};
+
+namespace std {
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: modification of 'std' namespace 
+template <>
+struct is_error_code_enum<MyError2> : std::true_type {};
+
+int foobar;
+}
+
+using namespace std;
+
+int x;
+
diff --git a/test/clang-tidy/cert-env33-c.c b/test/clang-tidy/cert-env33-c.c
new file mode 100644 (file)
index 0000000..5846b49
--- /dev/null
@@ -0,0 +1,20 @@
+// RUN: %check_clang_tidy %s cert-env33-c %t
+
+typedef struct FILE {} FILE;
+
+extern int system(const char *);
+extern FILE *popen(const char *, const char *);
+extern FILE *_popen(const char *, const char *);
+
+void f(void) {
+  // It is permissible to check for the presence of a command processor.
+  system(0);
+
+  system("test");
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: calling 'system' uses a command processor [cert-env33-c]
+
+  popen("test", "test");
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: calling 'popen' uses a command processor
+  _popen("test", "test");
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: calling '_popen' uses a command processor
+}
diff --git a/test/clang-tidy/cert-err34-c.c b/test/clang-tidy/cert-err34-c.c
new file mode 100644 (file)
index 0000000..e2cfc21
--- /dev/null
@@ -0,0 +1,103 @@
+// RUN: %check_clang_tidy %s cert-err34-c %t -- -- -std=c11
+
+typedef __SIZE_TYPE__      size_t;
+typedef signed             ptrdiff_t;
+typedef long long          intmax_t;
+typedef unsigned long long uintmax_t;
+typedef void *             FILE;
+
+extern FILE *stdin;
+
+extern int fscanf(FILE * restrict stream, const char * restrict format, ...);
+extern int scanf(const char * restrict format, ...);
+extern int sscanf(const char * restrict s, const char * restrict format, ...);
+
+extern double atof(const char *nptr);
+extern int atoi(const char *nptr);
+extern long int atol(const char *nptr);
+extern long long int atoll(const char *nptr);
+
+void f1(const char *in) {
+  int i;
+  long long ll;
+  unsigned int ui;
+  unsigned long long ull;
+  intmax_t im;
+  uintmax_t uim;
+  float f;
+  double d;
+  long double ld;
+
+  // CHECK-MESSAGES: :[[@LINE+1]]:3: warning: 'sscanf' used to convert a string to an integer value, but function will not report conversion errors; consider using 'strtol' instead [cert-err34-c]
+  sscanf(in, "%d", &i);
+  // CHECK-MESSAGES: :[[@LINE+1]]:3: warning: 'fscanf' used to convert a string to an integer value, but function will not report conversion errors; consider using 'strtoll' instead [cert-err34-c]
+  fscanf(stdin, "%lld", &ll);
+  // CHECK-MESSAGES: :[[@LINE+1]]:3: warning: 'sscanf' used to convert a string to an unsigned integer value, but function will not report conversion errors; consider using 'strtoul' instead [cert-err34-c]
+  sscanf(in, "%u", &ui);
+  // CHECK-MESSAGES: :[[@LINE+1]]:3: warning: 'fscanf' used to convert a string to an unsigned integer value, but function will not report conversion errors; consider using 'strtoull' instead [cert-err34-c]
+  fscanf(stdin, "%llu", &ull);
+  // CHECK-MESSAGES: :[[@LINE+1]]:3: warning: 'scanf' used to convert a string to an integer value, but function will not report conversion errors; consider using 'strtoimax' instead [cert-err34-c]
+  scanf("%jd", &im);
+  // CHECK-MESSAGES: :[[@LINE+1]]:3: warning: 'fscanf' used to convert a string to an unsigned integer value, but function will not report conversion errors; consider using 'strtoumax' instead [cert-err34-c]
+  fscanf(stdin, "%ju", &uim);
+  // CHECK-MESSAGES: :[[@LINE+1]]:3: warning: 'sscanf' used to convert a string to a floating-point value, but function will not report conversion errors; consider using 'strtof' instead [cert-err34-c]
+  sscanf(in, "%f", &f); // to float
+  // CHECK-MESSAGES: :[[@LINE+1]]:3: warning: 'fscanf' used to convert a string to a floating-point value, but function will not report conversion errors; consider using 'strtod' instead [cert-err34-c]
+  fscanf(stdin, "%lg", &d);
+  // CHECK-MESSAGES: :[[@LINE+1]]:3: warning: 'sscanf' used to convert a string to a floating-point value, but function will not report conversion errors; consider using 'strtold' instead [cert-err34-c]
+  sscanf(in, "%Le", &ld);
+
+  // These are conversions with other modifiers
+  short s;
+  char c;
+  size_t st;
+  ptrdiff_t pt;
+
+  // CHECK-MESSAGES: :[[@LINE+1]]:3: warning: 'scanf' used to convert
+  scanf("%hhd", &c);
+  // CHECK-MESSAGES: :[[@LINE+1]]:3: warning: 'scanf' used to convert
+  scanf("%hd", &s);
+  // CHECK-MESSAGES: :[[@LINE+1]]:3: warning: 'scanf' used to convert
+  scanf("%zu", &st);
+  // CHECK-MESSAGES: :[[@LINE+1]]:3: warning: 'scanf' used to convert
+  scanf("%td", &pt);
+  // CHECK-MESSAGES: :[[@LINE+1]]:3: warning: 'scanf' used to convert
+  scanf("%o", ui);
+  // CHECK-MESSAGES: :[[@LINE+1]]:3: warning: 'scanf' used to convert
+  scanf("%X", ui);
+  // CHECK-MESSAGES: :[[@LINE+1]]:3: warning: 'scanf' used to convert
+  scanf("%x", ui);
+}
+
+void f2(const char *in) {
+  // CHECK-MESSAGES: :[[@LINE+1]]:11: warning: 'atoi' used to convert a string to an integer value, but function will not report conversion errors; consider using 'strtol' instead [cert-err34-c]
+  int i = atoi(in); // to int
+  // CHECK-MESSAGES: :[[@LINE+1]]:12: warning: 'atol' used to convert a string to an integer value, but function will not report conversion errors; consider using 'strtol' instead [cert-err34-c]
+  long l = atol(in); // to long
+  // CHECK-MESSAGES: :[[@LINE+1]]:18: warning: 'atoll' used to convert a string to an integer value, but function will not report conversion errors; consider using 'strtoll' instead [cert-err34-c]
+  long long ll = atoll(in); // to long long
+  // CHECK-MESSAGES: :[[@LINE+1]]:14: warning: 'atof' used to convert a string to a floating-point value, but function will not report conversion errors; consider using 'strtod' instead [cert-err34-c]
+  double d = atof(in); // to double
+}
+
+void f3(void) {
+  int i;
+  unsigned int u;
+  float f;
+  char str[32];
+
+  // Test that we don't report multiple infractions for a single call.
+  // CHECK-MESSAGES: :[[@LINE+1]]:3: warning: 'scanf' used to convert
+  scanf("%d%u%f", &i, &u, &f);
+
+  // Test that we still catch infractions that are not the first specifier.
+  // CHECK-MESSAGES: :[[@LINE+1]]:3: warning: 'scanf' used to convert
+  scanf("%s%d", str, &i);
+}
+
+void do_not_diagnose(void) {
+  char str[32];
+
+  scanf("%s", str); // Not a numerical conversion
+  scanf("%*d"); // Assignment suppressed
+}
diff --git a/test/clang-tidy/cert-err34-c.cpp b/test/clang-tidy/cert-err34-c.cpp
new file mode 100644 (file)
index 0000000..dde7dc1
--- /dev/null
@@ -0,0 +1,43 @@
+// RUN: %check_clang_tidy %s cert-err34-c %t -- -- -std=c++11
+
+typedef void *             FILE;
+
+extern FILE *stdin;
+
+extern int fscanf(FILE * stream, const char * format, ...);
+extern int sscanf(const char * s, const char * format, ...);
+
+extern double atof(const char *nptr);
+extern int atoi(const char *nptr);
+extern long int atol(const char *nptr);
+extern long long int atoll(const char *nptr);
+
+namespace std {
+using ::FILE; using ::stdin;
+using ::fscanf; using ::sscanf;
+using ::atof; using ::atoi; using ::atol; using ::atoll;
+}
+
+void f1(const char *in) {
+  int i;
+  long long ll;
+
+  // CHECK-MESSAGES: :[[@LINE+1]]:3: warning: 'sscanf' used to convert a string to an integer value, but function will not report conversion errors; consider using 'strtol' instead [cert-err34-c]
+  std::sscanf(in, "%d", &i);
+  // CHECK-MESSAGES: :[[@LINE+1]]:3: warning: 'fscanf' used to convert a string to an integer value, but function will not report conversion errors; consider using 'strtoll' instead [cert-err34-c]
+  std::fscanf(std::stdin, "%lld", &ll);
+}
+
+void f2(const char *in) {
+  // CHECK-MESSAGES: :[[@LINE+1]]:11: warning: 'atoi' used to convert a string to an integer value, but function will not report conversion errors; consider using 'strtol' instead [cert-err34-c]
+  int i = std::atoi(in); // to int
+  // CHECK-MESSAGES: :[[@LINE+1]]:12: warning: 'atol' used to convert a string to an integer value, but function will not report conversion errors; consider using 'strtol' instead [cert-err34-c]
+  long l = std::atol(in); // to long
+
+  using namespace std;
+
+  // CHECK-MESSAGES: :[[@LINE+1]]:18: warning: 'atoll' used to convert a string to an integer value, but function will not report conversion errors; consider using 'strtoll' instead [cert-err34-c]
+  long long ll = atoll(in); // to long long
+  // CHECK-MESSAGES: :[[@LINE+1]]:14: warning: 'atof' used to convert a string to a floating-point value, but function will not report conversion errors; consider using 'strtod' instead [cert-err34-c]
+  double d = atof(in); // to double
+}
diff --git a/test/clang-tidy/cert-flp30-c.c b/test/clang-tidy/cert-flp30-c.c
new file mode 100644 (file)
index 0000000..eee16be
--- /dev/null
@@ -0,0 +1,19 @@
+// RUN: %check_clang_tidy %s cert-flp30-c %t
+
+float g(void);
+
+void func(void) {
+  for (float x = 0.1f; x <= 1.0f; x += 0.1f) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:37: warning: loop induction expression should not have floating-point type [cert-flp30-c]
+
+  float f = 1.0f;
+  for (; f > 0; --f) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: loop induction expression
+
+  for (;;g()) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: loop induction expression
+
+  for (int i = 0; i < 10; i += 1.0f) {}
+
+  for (int i = 0; i < 10; ++i) {}
+}
diff --git a/test/clang-tidy/cert-limited-randomness.c b/test/clang-tidy/cert-limited-randomness.c
new file mode 100644 (file)
index 0000000..c8009b5
--- /dev/null
@@ -0,0 +1,13 @@
+// RUN: %check_clang_tidy %s cert-msc30-c %t
+
+extern int rand(void);
+int nonrand();
+
+int cTest() {
+  int i = rand();
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: rand() has limited randomness [cert-msc30-c]
+
+  int k = nonrand();
+
+  return 0;
+}
diff --git a/test/clang-tidy/cert-limited-randomness.cpp b/test/clang-tidy/cert-limited-randomness.cpp
new file mode 100644 (file)
index 0000000..845b735
--- /dev/null
@@ -0,0 +1,28 @@
+// RUN: %check_clang_tidy %s cert-msc50-cpp %t
+
+int rand();
+int rand(int);
+
+namespace std {
+using ::rand;
+}
+
+namespace nonstd {
+  int rand();
+}
+
+void testFunction1() {
+  int i = std::rand();
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: rand() has limited randomness; use C++11 random library instead [cert-msc50-cpp]
+
+  int j = ::rand();
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: rand() has limited randomness; use C++11 random library instead [cert-msc50-cpp]
+
+  int k = rand(i);
+
+  int l = nonstd::rand();
+
+  int m = rand();
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: rand() has limited randomness; use C++11 random library instead [cert-msc50-cpp]
+}
+
diff --git a/test/clang-tidy/cert-oop11-cpp.cpp b/test/clang-tidy/cert-oop11-cpp.cpp
new file mode 100644 (file)
index 0000000..a4493bd
--- /dev/null
@@ -0,0 +1,21 @@
+// RUN: %check_clang_tidy %s cert-oop11-cpp %t -- -- -std=c++11
+
+struct B {
+  B(B&&) noexcept = default;
+
+  B(const B &) = default;
+  B& operator=(const B&) = default;
+  ~B() {}
+};
+
+struct D {
+  B b;
+
+  // CHECK-MESSAGES: :[[@LINE+1]]:14: warning: move constructor initializes class member by calling a copy constructor [cert-oop11-cpp]
+  D(D &&d) : b(d.b) {}
+
+  // This should not produce a diagnostic because it is not covered under
+  // the CERT guideline for OOP11-CPP. However, this will produce a diagnostic
+  // under misc-move-constructor-init.
+  D(B b) : b(b) {}
+};
diff --git a/test/clang-tidy/cert-setlongjmp.cpp b/test/clang-tidy/cert-setlongjmp.cpp
new file mode 100644 (file)
index 0000000..1bf5444
--- /dev/null
@@ -0,0 +1,26 @@
+// RUN: %check_clang_tidy %s cert-err52-cpp %t -- -- -std=c++11
+
+typedef void *jmp_buf;
+extern int __setjmpimpl(jmp_buf);
+#define setjmp(x) __setjmpimpl(x)
+[[noreturn]] extern void longjmp(jmp_buf, int);
+
+namespace std {
+using ::jmp_buf;
+using ::longjmp;
+}
+
+static jmp_buf env;
+void g() {
+  std::longjmp(env, 1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not call 'longjmp'; consider using exception handling instead [cert-err52-cpp]
+  ::longjmp(env, 1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not call 'longjmp'; consider using exception handling instead
+  longjmp(env, 1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not call 'longjmp'; consider using exception handling instead
+}
+
+void f() {
+  (void)setjmp(env);
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: do not call 'setjmp'; consider using exception handling instead
+}
diff --git a/test/clang-tidy/cert-static-object-exception.cpp b/test/clang-tidy/cert-static-object-exception.cpp
new file mode 100644 (file)
index 0000000..aa34bbd
--- /dev/null
@@ -0,0 +1,225 @@
+// RUN: clang-tidy %s -checks="-*,cert-err58-cpp" -- -std=c++11 -target x86_64-pc-linux-gnu \
+// RUN:   | FileCheck %s -check-prefix=CHECK-EXCEPTIONS \
+// RUN:   -implicit-check-not="{{warning|error}}:"
+// RUN: clang-tidy %s -checks="-*,cert-err58-cpp" -- -fno-exceptions -std=c++11 -target x86_64-pc-linux-gnu \
+// RUN:   | FileCheck %s -allow-empty -check-prefix=CHECK-NONEXCEPTIONS \
+// RUN:   -implicit-check-not="{{warning|error}}:"
+
+struct S {
+  S() noexcept(false);
+};
+
+struct T {
+  T() noexcept;
+};
+
+struct U {
+  U() {}
+};
+
+struct V {
+  explicit V(const char *) {} // Can throw
+};
+
+struct Cleanup {
+  ~Cleanup() {}
+};
+
+struct W {
+  W(Cleanup c = {}) noexcept(false);
+};
+
+struct X {
+  X(S = {}) noexcept;
+};
+
+struct Y {
+  S s;
+};
+
+struct Z {
+  T t;
+};
+
+int f();
+int g() noexcept(false);
+int h() noexcept(true);
+
+struct UserConv_Bad {
+  operator int() noexcept(false);
+};
+
+struct UserConv_Good {
+  operator int() noexcept;
+};
+
+UserConv_Bad some_bad_func() noexcept;
+UserConv_Good some_good_func() noexcept;
+
+S s;
+// CHECK-EXCEPTIONS: :[[@LINE-1]]:3: warning: initialization of 's' with static storage duration may throw an exception that cannot be caught [cert-err58-cpp]
+// CHECK-EXCEPTIONS: 9:3: note: possibly throwing constructor declared here
+// CHECK-NONEXCEPTIONS-NOT: warning:
+T t; // ok
+U u;
+// CHECK-EXCEPTIONS: :[[@LINE-1]]:3: warning: initialization of 'u' with static storage duration may throw an exception that cannot be caught
+// CHECK-EXCEPTIONS: 17:3: note: possibly throwing constructor declared here
+// CHECK-NONEXCEPTIONS-NOT: warning:
+V v("v");
+// CHECK-EXCEPTIONS: :[[@LINE-1]]:3: warning: initialization of 'v' with static storage duration may throw an exception that cannot be caught
+// CHECK-EXCEPTIONS: 21:12: note: possibly throwing constructor declared here
+// CHECK-NONEXCEPTIONS-NOT: warning:
+W w;
+// CHECK-EXCEPTIONS: :[[@LINE-1]]:3: warning: initialization of 'w' with static storage duration may throw an exception that cannot be caught
+// CHECK-EXCEPTIONS: 29:3: note: possibly throwing constructor declared here
+// CHECK-NONEXCEPTIONS-NOT: warning:
+X x1(S{});
+// CHECK-EXCEPTIONS: :[[@LINE-1]]:3: warning: initialization of 'x1' with static storage duration may throw an exception that cannot be caught
+// CHECK-EXCEPTIONS: 9:3: note: possibly throwing constructor declared here
+// CHECK-NONEXCEPTIONS-NOT: warning:
+X x2;
+// CHECK-EXCEPTIONS: :[[@LINE-1]]:3: warning: initialization of 'x2' with static storage duration may throw an exception that cannot be caught
+// CHECK-EXCEPTIONS: 9:3: note: possibly throwing constructor declared here
+// CHECK-NONEXCEPTIONS-NOT: warning:
+Y y;
+// CHECK-EXCEPTIONS: :[[@LINE-1]]:3: warning: initialization of 'y' with static storage duration may throw an exception that cannot be caught
+// CHECK-EXCEPTIONS: 36:8: note: possibly throwing constructor declared here
+// CHECK-NONEXCEPTIONS-NOT: warning:
+Z z;
+
+int i = f();
+// CHECK-EXCEPTIONS: :[[@LINE-1]]:5: warning: initialization of 'i' with static storage duration may throw an exception that cannot be caught
+// CHECK-EXCEPTIONS: 44:5: note: possibly throwing function declared here
+// CHECK-NONEXCEPTIONS-NOT: warning:
+int j = g();
+// CHECK-EXCEPTIONS: :[[@LINE-1]]:5: warning: initialization of 'j' with static storage duration may throw an exception that cannot be caught
+// CHECK-EXCEPTIONS: 45:5: note: possibly throwing function declared here
+// CHECK-NONEXCEPTIONS-NOT: warning:
+int k = h();
+int l = some_bad_func();
+// CHECK-EXCEPTIONS: :[[@LINE-1]]:5: warning: initialization of 'l' with static storage duration may throw an exception that cannot be caught
+// CHECK-EXCEPTIONS: 49:3: note: possibly throwing function declared here
+// CHECK-NONEXCEPTIONS-NOT: warning:
+int m = some_good_func();
+
+typedef decltype(sizeof(int)) size_t;
+inline void *operator new(size_t sz, void *here) noexcept { return here; }
+char n[sizeof(int)];
+int *o = new (n) int();
+int *p = new int();
+// CHECK-EXCEPTIONS: :[[@LINE-1]]:6: warning: initialization of 'p' with static storage duration may throw an exception that cannot be caught
+// CHECK-NONEXCEPTIONS-NOT: warning:
+
+thread_local S s3;
+// CHECK-EXCEPTIONS: :[[@LINE-1]]:16: warning: initialization of 's3' with thread_local storage duration may throw an exception that cannot be caught
+// CHECK-NONEXCEPTIONS-NOT: warning:
+thread_local T t3; // ok
+thread_local U u3;
+// CHECK-EXCEPTIONS: :[[@LINE-1]]:16: warning: initialization of 'u3' with thread_local storage duration may throw an exception that cannot be caught
+// CHECK-NONEXCEPTIONS-NOT: warning:
+thread_local V v3("v");
+// CHECK-EXCEPTIONS: :[[@LINE-1]]:16: warning: initialization of 'v3' with thread_local storage duration may throw an exception that cannot be caught
+// CHECK-NONEXCEPTIONS-NOT: warning:
+thread_local W w3;
+// CHECK-EXCEPTIONS: :[[@LINE-1]]:16: warning: initialization of 'w3' with thread_local storage duration may throw an exception that cannot be caught
+// CHECK-NONEXCEPTIONS-NOT: warning:
+
+void f(S s1, T t1, U u1, V v1, W w1) { // ok, ok, ok, ok, ok
+  S s2; // ok
+  T t2; // ok
+  U u2; // ok
+  V v2("v"); // ok
+  W w2; // ok
+
+  thread_local S s3; // ok
+  thread_local T t3; // ok
+  thread_local U u3; // ok
+  thread_local V v3("v"); // ok
+  thread_local W w3; // ok
+
+  static S s4; // ok
+  static T t4; // ok
+  static U u4; // ok
+  static V v4("v"); // ok
+  static W w4; // ok
+}
+
+namespace {
+S s;
+// CHECK-EXCEPTIONS: :[[@LINE-1]]:3: warning: initialization of 's' with static storage duration may throw an exception that cannot be caught [cert-err58-cpp]
+// CHECK-EXCEPTIONS: 9:3: note: possibly throwing constructor declared here
+// CHECK-NONEXCEPTIONS-NOT: warning:
+T t; // ok
+U u;
+// CHECK-EXCEPTIONS: :[[@LINE-1]]:3: warning: initialization of 'u' with static storage duration may throw an exception that cannot be caught
+// CHECK-EXCEPTIONS: 17:3: note: possibly throwing constructor declared here
+// CHECK-NONEXCEPTIONS-NOT: warning:
+V v("v");
+// CHECK-EXCEPTIONS: :[[@LINE-1]]:3: warning: initialization of 'v' with static storage duration may throw an exception that cannot be caught
+// CHECK-EXCEPTIONS: 21:12: note: possibly throwing constructor declared here
+// CHECK-NONEXCEPTIONS-NOT: warning:
+W w;
+// CHECK-EXCEPTIONS: :[[@LINE-1]]:3: warning: initialization of 'w' with static storage duration may throw an exception that cannot be caught
+// CHECK-EXCEPTIONS: 29:3: note: possibly throwing constructor declared here
+// CHECK-NONEXCEPTIONS-NOT: warning:
+
+thread_local S s3;
+// CHECK-EXCEPTIONS: :[[@LINE-1]]:16: warning: initialization of 's3' with thread_local storage duration may throw an exception that cannot be caught
+// CHECK-NONEXCEPTIONS-NOT: warning:
+thread_local T t3; // ok
+thread_local U u3;
+// CHECK-EXCEPTIONS: :[[@LINE-1]]:16: warning: initialization of 'u3' with thread_local storage duration may throw an exception that cannot be caught
+// CHECK-NONEXCEPTIONS-NOT: warning:
+thread_local V v3("v");
+// CHECK-EXCEPTIONS: :[[@LINE-1]]:16: warning: initialization of 'v3' with thread_local storage duration may throw an exception that cannot be caught
+// CHECK-NONEXCEPTIONS-NOT: warning:
+thread_local W w3;
+// CHECK-EXCEPTIONS: :[[@LINE-1]]:16: warning: initialization of 'w3' with thread_local storage duration may throw an exception that cannot be caught
+// CHECK-NONEXCEPTIONS-NOT: warning:
+}; // namespace
+
+class Statics {
+  static S s; // warn when initialized
+  static T t; // ok
+  static U u; // warn when initialized
+  static V v; // warn when initialized
+  static W w; // warn when initialized
+
+  void f(S s, T t, U u, V v) {
+    S s2;      // ok
+    T t2;      // ok
+    U u2;      // ok
+    V v2("v"); // ok
+    W w2;      // ok
+
+    thread_local S s3;      // ok
+    thread_local T t3;      // ok
+    thread_local U u3;      // ok
+    thread_local V v3("v"); // ok
+    thread_local W w3;      // ok
+
+    static S s4;      // ok
+    static T t4;      // ok
+    static U u4;      // ok
+    static V v4("v"); // ok
+    static W w4;      // ok
+  }
+};
+
+S Statics::s;
+// CHECK-EXCEPTIONS: :[[@LINE-1]]:12: warning: initialization of 's' with static storage duration may throw an exception that cannot be caught [cert-err58-cpp]
+// CHECK-EXCEPTIONS: 9:3: note: possibly throwing constructor declared here
+// CHECK-NONEXCEPTIONS-NOT: warning:
+T Statics::t;
+U Statics::u;
+// CHECK-EXCEPTIONS: :[[@LINE-1]]:12: warning: initialization of 'u' with static storage duration may throw an exception that cannot be caught
+// CHECK-EXCEPTIONS: 17:3: note: possibly throwing constructor declared here
+// CHECK-NONEXCEPTIONS-NOT: warning:
+V Statics::v("v");
+// CHECK-EXCEPTIONS: :[[@LINE-1]]:12: warning: initialization of 'v' with static storage duration may throw an exception that cannot be caught
+// CHECK-EXCEPTIONS: 21:12: note: possibly throwing constructor declared here
+// CHECK-NONEXCEPTIONS-NOT: warning:
+W Statics::w;
+// CHECK-EXCEPTIONS: :[[@LINE-1]]:12: warning: initialization of 'w' with static storage duration may throw an exception that cannot be caught
+// CHECK-EXCEPTIONS: 29:3: note: possibly throwing constructor declared here
+// CHECK-NONEXCEPTIONS-NOT: warning:
diff --git a/test/clang-tidy/cert-throw-exception-type.cpp b/test/clang-tidy/cert-throw-exception-type.cpp
new file mode 100644 (file)
index 0000000..2ff9be5
--- /dev/null
@@ -0,0 +1,127 @@
+// RUN: %check_clang_tidy %s cert-err60-cpp %t -- -- -std=c++11 -fcxx-exceptions
+
+struct S {};
+struct T : S {};
+struct U {
+  U() = default;
+  U(const U&) = default;
+};
+
+struct V {
+  V() = default;
+  V(const V&) noexcept;
+};
+
+struct W {
+  W() = default;
+  W(const W&) noexcept(false);
+};
+
+struct X {
+  X() = default;
+  X(const X&) {}
+};
+
+struct Y {
+  Y() = default;
+  Y(const Y&) throw();
+};
+
+struct Z {
+  Z() = default;
+  Z(const Z&) throw(int);
+};
+
+void g() noexcept(false);
+
+struct A {
+  A() = default;
+  A(const A&) noexcept(noexcept(g()));
+};
+
+struct B {
+  B() = default;
+  B(const B&) = default;
+  B(const A&) noexcept(false);
+};
+
+class C {
+  W M; // W is not no-throw copy constructible
+public:
+  C() = default;
+  C(const C&) = default;
+};
+
+struct D {
+  D() = default;
+  D(const D&) noexcept(false);
+  D(D&) noexcept(true);
+};
+
+struct E {
+  E() = default;
+  E(E&) noexcept(true);
+  E(const E&) noexcept(false);
+};
+
+struct Allocates {
+  int *x;
+  Allocates() : x(new int(0)) {}
+  Allocates(const Allocates &other) : x(new int(*other.x)) {}
+};
+
+struct OptionallyAllocates {
+  int *x;
+  OptionallyAllocates() : x(new int(0)) {}
+  OptionallyAllocates(const Allocates &other) noexcept(true) {
+    try {
+      x = new int(*other.x);
+    } catch (...) {
+      x = nullptr;
+    }
+  }
+};
+
+void f() {
+  throw 12; // ok
+  throw "test"; // ok
+  throw S(); // ok
+  throw T(); // ok
+  throw U(); // ok
+  throw V(); // ok
+  throw W(); // match, noexcept(false)
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: thrown exception type is not nothrow copy constructible [cert-err60-cpp]
+  throw X(); // match, no noexcept clause, nontrivial
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: thrown exception type is not nothrow copy constructible
+  throw Y(); // ok
+  throw Z(); // match, throw(int)
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: thrown exception type is not nothrow copy constructible
+  throw A(); // match, noexcept(false)
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: thrown exception type is not nothrow copy constructible
+  throw B(); // ok
+  throw C(); // match, C has a member variable that makes it throwing on copy
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: thrown exception type is not nothrow copy constructible
+  throw D(); // match, has throwing copy constructor
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: thrown exception type is not nothrow copy constructible
+  throw E(); // match, has throwing copy constructor
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: thrown exception type is not nothrow copy constructible
+  throw Allocates(); // match, copy constructor throws
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: thrown exception type is not nothrow copy constructible
+  throw OptionallyAllocates(); // ok
+}
+
+namespace PR25574 {
+struct B {
+  B(const B&) noexcept;
+};
+
+struct D : B {
+  D();
+  virtual ~D() noexcept;
+};
+
+template <typename T>
+void f() {
+  throw D();
+}
+}
diff --git a/test/clang-tidy/cert-variadic-function-def.cpp b/test/clang-tidy/cert-variadic-function-def.cpp
new file mode 100644 (file)
index 0000000..6b2421b
--- /dev/null
@@ -0,0 +1,24 @@
+// RUN: %check_clang_tidy %s cert-dcl50-cpp %t
+
+// Variadic function definitions are diagnosed.
+void f1(int, ...) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: do not define a C-style variadic function; consider using a function parameter pack or currying instead [cert-dcl50-cpp]
+
+// Variadic function *declarations* are not diagnosed.
+void f2(int, ...); // ok
+
+// Function parameter packs are good, however.
+template <typename Arg, typename... Ts>
+void f3(Arg F, Ts... Rest) {}
+
+struct S {
+  void f(int, ...); // ok
+  void f1(int, ...) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: do not define a C-style variadic function; consider using a function parameter pack or currying instead
+};
+
+// Function definitions that are extern "C" are good.
+extern "C" void f4(int, ...) {} // ok
+extern "C" {
+  void f5(int, ...) {} // ok
+}
diff --git a/test/clang-tidy/check_clang_tidy.py b/test/clang-tidy/check_clang_tidy.py
new file mode 100755 (executable)
index 0000000..114f0d5
--- /dev/null
@@ -0,0 +1,140 @@
+#!/usr/bin/env python
+#
+#===- check_clang_tidy.py - ClangTidy Test Helper ------------*- python -*--===#
+#
+#                     The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+#
+#===------------------------------------------------------------------------===#
+
+r"""
+ClangTidy Test Helper
+=====================
+
+This script runs clang-tidy in fix mode and verify fixes, messages or both.
+
+Usage:
+  check_clang_tidy.py [-resource-dir <resource-dir>] \
+    <source-file> <check-name> <temp-file> \
+    -- [optional clang-tidy arguments]
+
+Example:
+  // RUN: %check_clang_tidy %s llvm-include-order %t -- -- -isystem %S/Inputs
+"""
+
+import argparse
+import re
+import subprocess
+import sys
+
+
+def write_file(file_name, text):
+  with open(file_name, 'w') as f:
+    f.write(text)
+    f.truncate()
+
+def main():
+  parser = argparse.ArgumentParser()
+  parser.add_argument('-resource-dir')
+  parser.add_argument('input_file_name')
+  parser.add_argument('check_name')
+  parser.add_argument('temp_file_name')
+
+  args, extra_args = parser.parse_known_args()
+
+  resource_dir = args.resource_dir
+  input_file_name = args.input_file_name
+  check_name = args.check_name
+  temp_file_name = args.temp_file_name
+
+  extension = '.cpp'
+  if (input_file_name.endswith('.c')):
+    extension = '.c'
+  if (input_file_name.endswith('.hpp')):
+    extension = '.hpp'
+  temp_file_name = temp_file_name + extension
+
+  clang_tidy_extra_args = extra_args
+  if len(clang_tidy_extra_args) == 0:
+    clang_tidy_extra_args = ['--', '--std=c++11'] \
+        if extension == '.cpp' or extension == '.hpp' else ['--']
+
+  # Tests should not rely on STL being available, and instead provide mock
+  # implementations of relevant APIs.
+  clang_tidy_extra_args.append('-nostdinc++')
+
+  if resource_dir is not None:
+    clang_tidy_extra_args.append('-resource-dir=%s' % resource_dir)
+
+  with open(input_file_name, 'r') as input_file:
+    input_text = input_file.read()
+
+  has_check_fixes = input_text.find('CHECK-FIXES') >= 0
+  has_check_messages = input_text.find('CHECK-MESSAGES') >= 0
+
+  if not has_check_fixes and not has_check_messages:
+    sys.exit('Neither CHECK-FIXES nor CHECK-MESSAGES found in the input')
+
+  # Remove the contents of the CHECK lines to avoid CHECKs matching on
+  # themselves.  We need to keep the comments to preserve line numbers while
+  # avoiding empty lines which could potentially trigger formatting-related
+  # checks.
+  cleaned_test = re.sub('// *CHECK-[A-Z-]*:[^\r\n]*', '//', input_text)
+
+  write_file(temp_file_name, cleaned_test)
+
+  original_file_name = temp_file_name + ".orig"
+  write_file(original_file_name, cleaned_test)
+
+  args = ['clang-tidy', temp_file_name, '-fix', '--checks=-*,' + check_name] + \
+        clang_tidy_extra_args
+  print('Running ' + repr(args) + '...')
+  try:
+    clang_tidy_output = \
+        subprocess.check_output(args, stderr=subprocess.STDOUT).decode()
+  except subprocess.CalledProcessError as e:
+    print('clang-tidy failed:\n' + e.output.decode())
+    raise
+
+  print('------------------------ clang-tidy output -----------------------\n' +
+        clang_tidy_output +
+        '\n------------------------------------------------------------------')
+
+  try:
+    diff_output = subprocess.check_output(
+        ['diff', '-u', original_file_name, temp_file_name],
+        stderr=subprocess.STDOUT)
+  except subprocess.CalledProcessError as e:
+    diff_output = e.output
+
+  print('------------------------------ Fixes -----------------------------\n' +
+        diff_output.decode() +
+        '\n------------------------------------------------------------------')
+
+  if has_check_fixes:
+    try:
+      subprocess.check_output(
+          ['FileCheck', '-input-file=' + temp_file_name, input_file_name,
+           '-check-prefix=CHECK-FIXES', '-strict-whitespace'],
+          stderr=subprocess.STDOUT)
+    except subprocess.CalledProcessError as e:
+      print('FileCheck failed:\n' + e.output.decode())
+      raise
+
+  if has_check_messages:
+    messages_file = temp_file_name + '.msg'
+    write_file(messages_file, clang_tidy_output)
+    try:
+      subprocess.check_output(
+          ['FileCheck', '-input-file=' + messages_file, input_file_name,
+           '-check-prefix=CHECK-MESSAGES',
+           '-implicit-check-not={{warning|error}}:'],
+          stderr=subprocess.STDOUT)
+    except subprocess.CalledProcessError as e:
+      print('FileCheck failed:\n' + e.output.decode())
+      raise
+
+if __name__ == '__main__':
+  main()
diff --git a/test/clang-tidy/clang-tidy-diff.cpp b/test/clang-tidy/clang-tidy-diff.cpp
new file mode 100644 (file)
index 0000000..146287b
--- /dev/null
@@ -0,0 +1,27 @@
+// REQUIRES: shell
+// RUN: sed 's/placeholder_for_f/f/' %s > %t.cpp
+// RUN: clang-tidy -checks=-*,modernize-use-override %t.cpp -- -std=c++11 | FileCheck -check-prefix=CHECK-SANITY %s
+// RUN: not diff -U0 %s %t.cpp | %clang_tidy_diff -checks=-*,modernize-use-override -- -std=c++11 2>&1 | FileCheck %s
+// RUN: not diff -U0 %s %t.cpp | %clang_tidy_diff -checks=-*,modernize-use-override -quiet -- -std=c++11 2>&1 | FileCheck -check-prefix=CHECK-QUIET %s
+// RUN: mkdir -p %T/compilation-database-test/
+// RUN: echo '[{"directory": "%T", "command": "clang++ -o test.o -std=c++11 %t.cpp", "file": "%t.cpp"}]' > %T/compilation-database-test/compile_commands.json
+// RUN: not diff -U0 %s %t.cpp | %clang_tidy_diff -checks=-*,modernize-use-override -path %T/compilation-database-test 2>&1 | FileCheck -check-prefix=CHECK %s
+struct A {
+  virtual void f() {}
+  virtual void g() {}
+};
+// CHECK-NOT: warning:
+// CHECK-QUIET-NOT: warning:
+struct B : public A {
+  void placeholder_for_f() {}
+// CHECK-SANITY: [[@LINE-1]]:8: warning: annotate this
+// CHECK: [[@LINE-2]]:8: warning: annotate this
+// CHECK-QUIET: [[@LINE-3]]:8: warning: annotate this
+  void g() {}
+// CHECK-SANITY: [[@LINE-1]]:8: warning: annotate this
+// CHECK-NOT: warning:
+// CHECK-QUIET-NOT: warning:
+};
+// CHECK-SANITY-NOT: Suppressed
+// CHECK: Suppressed 1 warnings (1 due to line filter).
+// CHECK-QUIET-NOT: Suppressed
diff --git a/test/clang-tidy/clang-tidy-run-with-database.cpp b/test/clang-tidy/clang-tidy-run-with-database.cpp
new file mode 100644 (file)
index 0000000..05bc14c
--- /dev/null
@@ -0,0 +1,23 @@
+// RUN: mkdir -p %T/compilation-database-test/include
+// RUN: mkdir -p %T/compilation-database-test/a
+// RUN: mkdir -p %T/compilation-database-test/b
+// RUN: echo 'int *AA = 0;' > %T/compilation-database-test/a/a.cpp
+// RUN: echo 'int *AB = 0;' > %T/compilation-database-test/a/b.cpp
+// RUN: echo 'int *BB = 0;' > %T/compilation-database-test/b/b.cpp
+// RUN: echo 'int *BC = 0;' > %T/compilation-database-test/b/c.cpp
+// RUN: echo 'int *HP = 0;' > %T/compilation-database-test/include/header.h
+// RUN: echo '#include "header.h"' > %T/compilation-database-test/b/d.cpp
+// RUN: sed 's|test_dir|%/T/compilation-database-test|g' %S/Inputs/compilation-database/template.json > %T/compile_commands.json
+// RUN: clang-tidy --checks=-*,modernize-use-nullptr -p %T %T/compilation-database-test/b/not-exist -header-filter=.*
+// RUN: clang-tidy --checks=-*,modernize-use-nullptr -p %T %T/compilation-database-test/a/a.cpp %T/compilation-database-test/a/b.cpp %T/compilation-database-test/b/b.cpp %T/compilation-database-test/b/c.cpp %T/compilation-database-test/b/d.cpp -header-filter=.* -fix
+// RUN: FileCheck -input-file=%T/compilation-database-test/a/a.cpp %s -check-prefix=CHECK-FIX1
+// RUN: FileCheck -input-file=%T/compilation-database-test/a/b.cpp %s -check-prefix=CHECK-FIX2
+// RUN: FileCheck -input-file=%T/compilation-database-test/b/b.cpp %s -check-prefix=CHECK-FIX3
+// RUN: FileCheck -input-file=%T/compilation-database-test/b/c.cpp %s -check-prefix=CHECK-FIX4
+// RUN: FileCheck -input-file=%T/compilation-database-test/include/header.h %s -check-prefix=CHECK-FIX5
+
+// CHECK-FIX1: int *AA = nullptr;
+// CHECK-FIX2: int *AB = nullptr;
+// CHECK-FIX3: int *BB = nullptr;
+// CHECK-FIX4: int *BC = nullptr;
+// CHECK-FIX5: int *HP = nullptr;
diff --git a/test/clang-tidy/clean-up-code.cpp b/test/clang-tidy/clean-up-code.cpp
new file mode 100644 (file)
index 0000000..15873ed
--- /dev/null
@@ -0,0 +1,14 @@
+// RUN: %check_clang_tidy %s misc-unused-using-decls %t
+// RUN: %check_clang_tidy %s misc-unused-using-decls %t -- -format-style=none --
+// RUN: %check_clang_tidy %s misc-unused-using-decls %t -- -format-style=llvm --
+namespace a { class A {}; }
+namespace b {
+using a::A;
+}
+namespace c {}
+// CHECK-MESSAGES: :[[@LINE-3]]:10: warning: using decl 'A' is unused [misc-unused-using-decls]
+// CHECK-FIXES: {{^namespace a { class A {}; }$}}
+// CHECK-FIXES-NOT: namespace
+// CHECK-FIXES: {{^namespace c {}$}}
+// FIXME: cleanupAroundReplacements leaves whitespace. Otherwise we could just
+// check the next line.
diff --git a/test/clang-tidy/config-files.cpp b/test/clang-tidy/config-files.cpp
new file mode 100644 (file)
index 0000000..65ac54a
--- /dev/null
@@ -0,0 +1,12 @@
+// RUN: clang-tidy -dump-config %S/Inputs/config-files/- -- | FileCheck %s -check-prefix=CHECK-BASE
+// CHECK-BASE: Checks: {{.*}}from-parent
+// CHECK-BASE: HeaderFilterRegex: parent
+// RUN: clang-tidy -dump-config %S/Inputs/config-files/1/- -- | FileCheck %s -check-prefix=CHECK-CHILD1
+// CHECK-CHILD1: Checks: {{.*}}from-child1
+// CHECK-CHILD1: HeaderFilterRegex: child1
+// RUN: clang-tidy -dump-config %S/Inputs/config-files/2/- -- | FileCheck %s -check-prefix=CHECK-CHILD2
+// CHECK-CHILD2: Checks: {{.*}}from-parent
+// CHECK-CHILD2: HeaderFilterRegex: parent
+// RUN: clang-tidy -dump-config -checks='from-command-line' -header-filter='from command line' %S/Inputs/config-files/- -- | FileCheck %s -check-prefix=CHECK-COMMAND-LINE
+// CHECK-COMMAND-LINE: Checks: {{.*}}from-parent,from-command-line
+// CHECK-COMMAND-LINE: HeaderFilterRegex: from command line
diff --git a/test/clang-tidy/cppcoreguidelines-interfaces-global-init.cpp b/test/clang-tidy/cppcoreguidelines-interfaces-global-init.cpp
new file mode 100644 (file)
index 0000000..51f79e5
--- /dev/null
@@ -0,0 +1,84 @@
+// RUN: %check_clang_tidy %s cppcoreguidelines-interfaces-global-init %t
+
+constexpr int makesInt() { return 3; }
+constexpr int takesInt(int i) { return i + 1; }
+constexpr int takesIntPtr(int *i) { return *i; }
+
+extern int ExternGlobal;
+static int GlobalScopeBadInit1 = ExternGlobal;
+// CHECK-MESSAGES: [[@LINE-1]]:12: warning: initializing non-local variable with non-const expression depending on uninitialized non-local variable 'ExternGlobal'
+static int GlobalScopeBadInit2 = takesInt(ExternGlobal);
+// CHECK-MESSAGES: [[@LINE-1]]:12: warning: initializing non-local variable with non-const expression depending on uninitialized non-local variable 'ExternGlobal'
+static int GlobalScopeBadInit3 = takesIntPtr(&ExternGlobal);
+// CHECK-MESSAGES: [[@LINE-1]]:12: warning: initializing non-local variable with non-const expression depending on uninitialized non-local variable 'ExternGlobal'
+static int GlobalScopeBadInit4 = 3 * (ExternGlobal + 2);
+// CHECK-MESSAGES: [[@LINE-1]]:12: warning: initializing non-local variable with non-const expression depending on uninitialized non-local variable 'ExternGlobal'
+
+namespace ns {
+static int NamespaceScope = makesInt();
+static int NamespaceScopeBadInit = takesInt(ExternGlobal);
+// CHECK-MESSAGES: [[@LINE-1]]:12: warning: initializing non-local variable with non-const expression depending on uninitialized non-local variable 'ExternGlobal'
+
+struct A {
+  static int ClassScope;
+  static int ClassScopeBadInit;
+};
+
+int A::ClassScopeBadInit = takesInt(ExternGlobal);
+// CHECK-MESSAGES: [[@LINE-1]]:8: warning: initializing non-local variable with non-const expression depending on uninitialized non-local variable 'ExternGlobal'
+
+static int FromClassBadInit = takesInt(A::ClassScope);
+// CHECK-MESSAGES: [[@LINE-1]]:12: warning: initializing non-local variable with non-const expression depending on uninitialized non-local variable 'ClassScope'
+} // namespace ns
+
+// "const int B::I;" is fine, it just ODR-defines B::I. See [9.4.3] Static
+// members [class.static]. However the ODR-definitions are not in the right
+// order (C::I after C::J, see [3.6.2.3]).
+class B1 {
+  static const int I = 0;
+  static const int J = I;
+};
+const int B1::J;
+// CHECK-MESSAGES: [[@LINE-1]]:15: warning: initializing non-local variable with non-const expression depending on uninitialized non-local variable 'I'
+const int B1::I;
+
+void f() {
+  // This is fine, it's executed after dynamic initialization occurs.
+  static int G = takesInt(ExternGlobal);
+}
+
+// Declaration then definition then usage is fine.
+extern int ExternGlobal2;
+extern int ExternGlobal2;
+int ExternGlobal2 = 123;
+static int GlobalScopeGoodInit1 = ExternGlobal2;
+
+
+// Defined global variables are fine:
+static int GlobalScope = makesInt();
+static int GlobalScopeGoodInit2 = takesInt(GlobalScope);
+static int GlobalScope2 = takesInt(ns::NamespaceScope);
+// Enums are fine.
+enum Enum { kEnumValue = 1 };
+static int GlobalScopeFromEnum = takesInt(kEnumValue);
+
+// Leave constexprs alone.
+extern constexpr int GlobalScopeConstexpr = makesInt();
+static constexpr int GlobalScopeConstexprOk =
+    takesInt(GlobalScopeConstexpr);
+
+// This is a pretty common instance: People are usually not using constexpr, but
+// this is what they should write:
+static constexpr const char kValue[] = "value";
+constexpr int Fingerprint(const char *value) { return 0; }
+static int kFingerprint = Fingerprint(kValue);
+
+// This is fine because the ODR-definitions are in the right order (C::J after
+// C::I).
+class B2 {
+  static const int I = 0;
+  static const int J = I;
+};
+const int B2::I;
+const int B2::J;
+
diff --git a/test/clang-tidy/cppcoreguidelines-no-malloc-custom.cpp b/test/clang-tidy/cppcoreguidelines-no-malloc-custom.cpp
new file mode 100644 (file)
index 0000000..89142fb
--- /dev/null
@@ -0,0 +1,59 @@
+// RUN: %check_clang_tidy %s cppcoreguidelines-no-malloc %t \
+// RUN: -config='{CheckOptions: \
+// RUN:  [{key: cppcoreguidelines-no-malloc.Allocations, value: "::malloc;::align_malloc;::calloc"},\
+// RUN:   {key: cppcoreguidelines-no-malloc.Reallocations, value: "::realloc;::align_realloc"},\
+// RUN:   {key: cppcoreguidelines-no-malloc.Deallocations, value: "::free;::align_free"}]}' \
+// RUN: --
+
+using size_t = __SIZE_TYPE__;
+
+void *malloc(size_t size);
+void *align_malloc(size_t size, unsigned short aligmnent);
+void *calloc(size_t num, size_t size);
+void *realloc(void *ptr, size_t size);
+void *align_realloc(void *ptr, size_t size, unsigned short alignment);
+void free(void *ptr);
+void *align_free(void *ptr);
+
+void malloced_array() {
+  int *array0 = (int *)malloc(sizeof(int) * 20);
+  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: do not manage memory manually; consider a container or a smart pointer [cppcoreguidelines-no-malloc]
+
+  int *zeroed = (int *)calloc(20, sizeof(int));
+  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: do not manage memory manually; consider a container or a smart pointer [cppcoreguidelines-no-malloc]
+
+  int *aligned = (int *)align_malloc(20 * sizeof(int), 16);
+  // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: do not manage memory manually; consider a container or a smart pointer [cppcoreguidelines-no-malloc]
+
+  // reallocation memory, std::vector shall be used
+  char *realloced = (char *)realloc(array0, 50 * sizeof(int));
+  // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: do not manage memory manually; consider std::vector or std::string [cppcoreguidelines-no-malloc]
+
+  char *align_realloced = (char *)align_realloc(aligned, 50 * sizeof(int), 16);
+  // CHECK-MESSAGES: :[[@LINE-1]]:35: warning: do not manage memory manually; consider std::vector or std::string [cppcoreguidelines-no-malloc]
+
+  // freeing memory the bad way
+  free(realloced);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not manage memory manually; use RAII [cppcoreguidelines-no-malloc]
+
+  align_free(align_realloced);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not manage memory manually; use RAII [cppcoreguidelines-no-malloc]
+  
+  // check if a call to malloc as function argument is found as well
+  free(malloc(20));
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not manage memory manually; use RAII [cppcoreguidelines-no-malloc]
+  // CHECK-MESSAGES: :[[@LINE-2]]:8: warning: do not manage memory manually; consider a container or a smart pointer [cppcoreguidelines-no-malloc]
+}
+
+/// newing an array is still not good, but not relevant to this checker
+void newed_array() {
+  int *new_array = new int[10]; // OK(1)
+}
+
+void arbitrary_call() {
+  // we dont want every function to raise the warning even if malloc is in the name
+  malloced_array(); // OK(2)
+
+  // completly unrelated function call to malloc
+  newed_array(); // OK(3)
+}
diff --git a/test/clang-tidy/cppcoreguidelines-no-malloc-no-functions.cpp b/test/clang-tidy/cppcoreguidelines-no-malloc-no-functions.cpp
new file mode 100644 (file)
index 0000000..8b4d464
--- /dev/null
@@ -0,0 +1,17 @@
+// RUN: %check_clang_tidy %s cppcoreguidelines-no-malloc %t \
+// RUN: -config='{CheckOptions: \
+// RUN:  [{key: cppcoreguidelines-no-malloc.Allocations, value: "::malloc"},\
+// RUN:   {key: cppcoreguidelines-no-malloc.Reallocations, value: ""},\
+// RUN:   {key: cppcoreguidelines-no-malloc.Deallocations, value: ""}]}' \
+// RUN: --
+
+// Just ensure, the check will not crash, when no functions shall be checked.
+
+using size_t = __SIZE_TYPE__;
+
+void *malloc(size_t size);
+
+void malloced_array() {
+  int *array0 = (int *)malloc(sizeof(int) * 20);
+  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: do not manage memory manually; consider a container or a smart pointer [cppcoreguidelines-no-malloc]
+}
diff --git a/test/clang-tidy/cppcoreguidelines-no-malloc.cpp b/test/clang-tidy/cppcoreguidelines-no-malloc.cpp
new file mode 100644 (file)
index 0000000..7b7ccf3
--- /dev/null
@@ -0,0 +1,42 @@
+// RUN: %check_clang_tidy %s cppcoreguidelines-no-malloc %t
+
+using size_t = __SIZE_TYPE__;
+
+void *malloc(size_t size);
+void *calloc(size_t num, size_t size);
+void *realloc(void *ptr, size_t size);
+void free(void *ptr);
+
+void malloced_array() {
+  int *array0 = (int *)malloc(sizeof(int) * 20);
+  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: do not manage memory manually; consider a container or a smart pointer [cppcoreguidelines-no-malloc]
+
+  int *zeroed = (int *)calloc(20, sizeof(int));
+  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: do not manage memory manually; consider a container or a smart pointer [cppcoreguidelines-no-malloc]
+
+  // reallocation memory, std::vector shall be used
+  char *realloced = (char *)realloc(array0, 50 * sizeof(int));
+  // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: do not manage memory manually; consider std::vector or std::string [cppcoreguidelines-no-malloc]
+
+  // freeing memory the bad way
+  free(realloced);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not manage memory manually; use RAII [cppcoreguidelines-no-malloc]
+
+  // check if a call to malloc as function argument is found as well
+  free(malloc(20));
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not manage memory manually; use RAII [cppcoreguidelines-no-malloc]
+  // CHECK-MESSAGES: :[[@LINE-2]]:8: warning: do not manage memory manually; consider a container or a smart pointer [cppcoreguidelines-no-malloc]
+}
+
+/// newing an array is still not good, but not relevant to this checker
+void newed_array() {
+  int *new_array = new int[10]; // OK(1)
+}
+
+void arbitrary_call() {
+  // we dont want every function to raise the warning even if malloc is in the name
+  malloced_array(); // OK(2)
+
+  // completly unrelated function call to malloc
+  newed_array(); // OK(3)
+}
diff --git a/test/clang-tidy/cppcoreguidelines-pro-bounds-array-to-pointer-decay.cpp b/test/clang-tidy/cppcoreguidelines-pro-bounds-array-to-pointer-decay.cpp
new file mode 100644 (file)
index 0000000..ce19280
--- /dev/null
@@ -0,0 +1,47 @@
+// RUN: %check_clang_tidy %s cppcoreguidelines-pro-bounds-array-to-pointer-decay %t
+#include <stddef.h>
+
+namespace gsl {
+template <class T>
+class array_view {
+public:
+  template <class U, size_t N>
+  array_view(U (&arr)[N]);
+};
+}
+
+void pointerfun(int *p);
+void arrayfun(int p[]);
+void arrayviewfun(gsl::array_view<int> &p);
+size_t s();
+
+void f() {
+  int a[5];
+  pointerfun(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: do not implicitly decay an array into a pointer; consider using gsl::array_view or an explicit cast instead [cppcoreguidelines-pro-bounds-array-to-pointer-decay]
+  pointerfun((int *)a); // OK, explicit cast
+  arrayfun(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: do not implicitly decay an array into a pointer
+
+  pointerfun(a + s() - 10); // Convert to &a[g() - 10];
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: do not implicitly decay an array into a pointer
+
+  gsl::array_view<int> av(a);
+  arrayviewfun(av); // OK
+
+  int i = a[0];      // OK
+  pointerfun(&a[0]); // OK
+
+  for (auto &e : a) // OK, iteration internally decays array to pointer
+    e = 1;
+}
+
+const char *g() {
+  return "clang"; // OK, decay string literal to pointer
+}
+
+void f2(void *const *);
+void bug25362() {
+  void *a[2];
+  f2(static_cast<void *const*>(a)); // OK, explicit cast
+}
diff --git a/test/clang-tidy/cppcoreguidelines-pro-bounds-constant-array-index-c++03.cpp b/test/clang-tidy/cppcoreguidelines-pro-bounds-constant-array-index-c++03.cpp
new file mode 100644 (file)
index 0000000..ad9fcd9
--- /dev/null
@@ -0,0 +1,11 @@
+// RUN: clang-tidy %s -checks=-*,cppcoreguidelines-pro-bounds-constant-array-index -- -std=c++03 | count 0
+
+// Note: this test expects no diagnostics, but FileCheck cannot handle that,
+// hence the use of | count 0.
+template <int index> struct B {
+  int get() {
+    // The next line used to crash the check (in C++03 mode only).
+    return x[index];
+  }
+  int x[3];
+};
diff --git a/test/clang-tidy/cppcoreguidelines-pro-bounds-constant-array-index-gslheader.cpp b/test/clang-tidy/cppcoreguidelines-pro-bounds-constant-array-index-gslheader.cpp
new file mode 100644 (file)
index 0000000..a88a2d9
--- /dev/null
@@ -0,0 +1,79 @@
+// RUN: %check_clang_tidy %s cppcoreguidelines-pro-bounds-constant-array-index %t -- -config='{CheckOptions: [{key: cppcoreguidelines-pro-bounds-constant-array-index.GslHeader, value: "dir1/gslheader.h"}]}' -- -std=c++11
+// CHECK-FIXES: #include "dir1/gslheader.h"
+
+typedef __SIZE_TYPE__ size_t;
+
+namespace std {
+  template<typename T, size_t N>
+  struct array {
+    T& operator[](size_t n);
+    T& at(size_t n);
+  };
+}
+
+
+namespace gsl {
+  template<class T, size_t N>
+  T& at( T(&a)[N], size_t index );
+
+  template<class T, size_t N>
+  T& at( std::array<T, N> &a, size_t index );
+}
+
+constexpr int const_index(int base) {
+  return base + 3;
+}
+
+void f(std::array<int, 10> a, int pos) {
+  a [ pos / 2 /*comment*/] = 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not use array subscript when the index is not an integer constant expression; use gsl::at() instead [cppcoreguidelines-pro-bounds-constant-array-index]
+  // CHECK-FIXES: gsl::at(a,  pos / 2 /*comment*/) = 1;
+  int j = a[pos - 1];
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: do not use array subscript when the index is not an integer constant expression; use gsl::at() instead
+  // CHECK-FIXES: int j = gsl::at(a, pos - 1);
+
+  a.at(pos-1) = 2; // OK, at() instead of []
+  gsl::at(a, pos-1) = 2; // OK, gsl::at() instead of []
+
+  a[-1] = 3;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: std::array<> index -1 is negative [cppcoreguidelines-pro-bounds-constant-array-index]
+  a[10] = 4;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: std::array<> index 10 is past the end of the array (which contains 10 elements) [cppcoreguidelines-pro-bounds-constant-array-index]
+
+  a[const_index(7)] = 3;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: std::array<> index 10 is past the end of the array (which contains 10 elements)
+
+  a[0] = 3; // OK, constant index and inside bounds
+  a[1] = 3; // OK, constant index and inside bounds
+  a[9] = 3; // OK, constant index and inside bounds
+  a[const_index(6)] = 3; // OK, constant index and inside bounds
+}
+
+void g() {
+  int a[10];
+  for (int i = 0; i < 10; ++i) {
+    a[i] = i;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: do not use array subscript when the index is not an integer constant expression; use gsl::at() instead
+    // CHECK-FIXES: gsl::at(a, i) = i;
+    gsl::at(a, i) = i; // OK, gsl::at() instead of []
+  }
+
+  a[-1] = 3; // flagged by clang-diagnostic-array-bounds
+  a[10] = 4; // flagged by clang-diagnostic-array-bounds
+  a[const_index(7)] = 3; // flagged by clang-diagnostic-array-bounds
+
+  a[0] = 3; // OK, constant index and inside bounds
+  a[1] = 3; // OK, constant index and inside bounds
+  a[9] = 3; // OK, constant index and inside bounds
+  a[const_index(6)] = 3; // OK, constant index and inside bounds
+}
+
+struct S {
+  int& operator[](int i);
+};
+
+void customOperator() {
+  S s;
+  int i = 0;
+  s[i] = 3; // OK, custom operator
+}
diff --git a/test/clang-tidy/cppcoreguidelines-pro-bounds-constant-array-index.cpp b/test/clang-tidy/cppcoreguidelines-pro-bounds-constant-array-index.cpp
new file mode 100644 (file)
index 0000000..3519410
--- /dev/null
@@ -0,0 +1,87 @@
+// RUN: %check_clang_tidy %s cppcoreguidelines-pro-bounds-constant-array-index %t
+
+typedef __SIZE_TYPE__ size_t;
+
+namespace std {
+  template<typename T, size_t N>
+  struct array {
+    T& operator[](size_t n);
+    T& at(size_t n);
+  };
+}
+
+
+namespace gsl {
+  template<class T, size_t N>
+  T& at( T(&a)[N], size_t index );
+
+  template<class T, size_t N>
+  T& at( std::array<T, N> &a, size_t index );
+}
+
+constexpr int const_index(int base) {
+  return base + 3;
+}
+
+void f(std::array<int, 10> a, int pos) {
+  a [ pos / 2 /*comment*/] = 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not use array subscript when the index is not an integer constant expression; use gsl::at() instead [cppcoreguidelines-pro-bounds-constant-array-index]
+  int j = a[pos - 1];
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: do not use array subscript when the index is not an integer constant expression; use gsl::at() instead
+
+  a.at(pos-1) = 2; // OK, at() instead of []
+  gsl::at(a, pos-1) = 2; // OK, gsl::at() instead of []
+
+  a[-1] = 3;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: std::array<> index -1 is negative [cppcoreguidelines-pro-bounds-constant-array-index]
+  a[10] = 4;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: std::array<> index 10 is past the end of the array (which contains 10 elements) [cppcoreguidelines-pro-bounds-constant-array-index]
+
+  a[const_index(7)] = 3;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: std::array<> index 10 is past the end of the array (which contains 10 elements)
+
+  a[0] = 3; // OK, constant index and inside bounds
+  a[1] = 3; // OK, constant index and inside bounds
+  a[9] = 3; // OK, constant index and inside bounds
+  a[const_index(6)] = 3; // OK, constant index and inside bounds
+}
+
+void g() {
+  int a[10];
+  for (int i = 0; i < 10; ++i) {
+    a[i] = i;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: do not use array subscript when the index is not an integer constant expression; use gsl::at() instead
+    // CHECK-FIXES: gsl::at(a, i) = i;
+    gsl::at(a, i) = i; // OK, gsl::at() instead of []
+  }
+
+  a[-1] = 3; // flagged by clang-diagnostic-array-bounds
+  a[10] = 4; // flagged by clang-diagnostic-array-bounds
+  a[const_index(7)] = 3; // flagged by clang-diagnostic-array-bounds
+
+  a[0] = 3; // OK, constant index and inside bounds
+  a[1] = 3; // OK, constant index and inside bounds
+  a[9] = 3; // OK, constant index and inside bounds
+  a[const_index(6)] = 3; // OK, constant index and inside bounds
+}
+
+struct S {
+  int& operator[](int i);
+};
+
+void customOperator() {
+  S s;
+  int i = 0;
+  s[i] = 3; // OK, custom operator
+}
+
+struct A {
+  // The compiler-generated copy constructor uses an ArraySubscriptExpr. Don't warn.
+  int x[3];
+};
+
+void use_A() {
+  // Force the compiler to generate a copy constructor.
+  A a;
+  A a2(a);
+}
diff --git a/test/clang-tidy/cppcoreguidelines-pro-bounds-pointer-arithmetic.cpp b/test/clang-tidy/cppcoreguidelines-pro-bounds-pointer-arithmetic.cpp
new file mode 100644 (file)
index 0000000..7cbc6dd
--- /dev/null
@@ -0,0 +1,89 @@
+// RUN: %check_clang_tidy %s cppcoreguidelines-pro-bounds-pointer-arithmetic %t
+
+enum E {
+  ENUM_LITERAL = 1
+};
+
+int i = 4;
+int j = 1;
+int *p = 0;
+int *q = 0;
+
+void fail() {
+  q = p + 4;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: do not use pointer arithmetic [cppcoreguidelines-pro-bounds-pointer-arithmetic]
+  p = q + i;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: do not use pointer arithmetic
+  p = q + ENUM_LITERAL;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: do not use pointer arithmetic
+
+  q = p - 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: do not use pointer arithmetic
+  p = q - i;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: do not use pointer arithmetic
+  p = q - ENUM_LITERAL;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: do not use pointer arithmetic
+
+  p += 4;
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: do not use pointer arithmetic
+  p += i;
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: do not use pointer arithmetic
+  p += ENUM_LITERAL;
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: do not use pointer arithmetic
+
+  q -= 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: do not use pointer arithmetic
+  q -= i;
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: do not use pointer arithmetic
+  q -= ENUM_LITERAL;
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: do not use pointer arithmetic
+
+  p++;
+  // CHECK-MESSAGES: :[[@LINE-1]]:4: warning: do not use pointer arithmetic
+  ++p;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not use pointer arithmetic
+
+  p--;
+  // CHECK-MESSAGES: :[[@LINE-1]]:4: warning: do not use pointer arithmetic
+  --p;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not use pointer arithmetic
+
+  i = p[1];
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: do not use pointer arithmetic
+}
+
+struct S {
+  operator int() const;
+};
+
+void f(S &s) {
+  int *i;
+  i = i + s;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: do not use pointer arithmetic
+}
+
+void f2(int i[]) {
+  i[1] = 0;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not use pointer arithmetic
+}
+
+void okay() {
+  int a[3];
+  i = a[2]; // OK, access to array
+
+  p = q;
+  p = &i;
+
+  i++;
+  ++i;
+  i--;
+  --i;
+  i += 1;
+  i -= 1;
+  i = j + 1;
+  i = j - 1;
+
+  auto diff = p - q; // OK, result is arithmetic
+
+  for(int ii : a) ; // OK, pointer arithmetic generated by compiler
+}
diff --git a/test/clang-tidy/cppcoreguidelines-pro-type-const-cast.cpp b/test/clang-tidy/cppcoreguidelines-pro-type-const-cast.cpp
new file mode 100644 (file)
index 0000000..2d32e13
--- /dev/null
@@ -0,0 +1,6 @@
+// RUN: %check_clang_tidy %s cppcoreguidelines-pro-type-const-cast %t
+
+const int *i;
+int *j;
+void f() { j = const_cast<int *>(i); }
+// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: do not use const_cast [cppcoreguidelines-pro-type-const-cast]
diff --git a/test/clang-tidy/cppcoreguidelines-pro-type-cstyle-cast.cpp b/test/clang-tidy/cppcoreguidelines-pro-type-cstyle-cast.cpp
new file mode 100644 (file)
index 0000000..31f59e3
--- /dev/null
@@ -0,0 +1,141 @@
+// RUN: %check_clang_tidy %s cppcoreguidelines-pro-type-cstyle-cast %t
+
+void reinterpretcast() {
+  int i = 0;
+  void *j;
+  j = (int*)j;
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: do not use C-style cast to convert between unrelated types [cppcoreguidelines-pro-type-cstyle-cast]
+}
+
+void constcast() {
+  int* i;
+  const int* j;
+  i = (int*)j;
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: do not use C-style cast to cast away constness
+  j = (const int*)i; // OK, const added
+  (void)j; // OK, not a const_cast
+}
+
+void const_and_reinterpret() {
+  int* i;
+  const void* j;
+  i = (int*)j;
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: do not use C-style cast to convert between unrelated types
+}
+
+class Base {
+};
+
+class Derived : public Base {
+};
+
+class Base2 {
+};
+
+class MultiDerived : public Base, public Base2 {
+};
+
+class PolymorphicBase {
+public:
+  virtual ~PolymorphicBase();
+};
+
+class PolymorphicDerived : public PolymorphicBase {
+};
+
+class PolymorphicMultiDerived : public Base, public PolymorphicBase {
+};
+
+void pointers() {
+
+  auto P0 = (Derived*)new Base();
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: do not use C-style cast to downcast from a base to a derived class
+
+  const Base* B0;
+  auto PC0 = (const Derived*)(B0);
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: do not use C-style cast to downcast from a base to a derived class
+
+  auto P1 = (Base*)new Derived(); // OK, upcast to a public base
+  auto P2 = (Base*)new MultiDerived(); // OK, upcast to a public base
+  auto P3 = (Base2*)new MultiDerived(); // OK, upcast to a public base
+}
+
+void pointers_polymorphic() {
+
+  auto PP0 = (PolymorphicDerived*)new PolymorphicBase();
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: do not use C-style cast to downcast from a base to a derived class; use dynamic_cast instead
+  // CHECK-FIXES: auto PP0 = dynamic_cast<PolymorphicDerived*>(new PolymorphicBase());
+
+  const PolymorphicBase* B0;
+  auto PPC0 = (const PolymorphicDerived*)B0;
+  // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: do not use C-style cast to downcast from a base to a derived class; use dynamic_cast instead
+  // CHECK-FIXES: auto PPC0 = dynamic_cast<const PolymorphicDerived*>(B0);
+
+
+  auto B1 = (PolymorphicBase*)new PolymorphicDerived(); // OK, upcast to a public base
+  auto B2 = (PolymorphicBase*)new PolymorphicMultiDerived(); // OK, upcast to a public base
+  auto B3 = (Base*)new PolymorphicMultiDerived(); // OK, upcast to a public base
+}
+
+void arrays() {
+  Base ArrayOfBase[10];
+  auto A0 = (Derived*)ArrayOfBase;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: do not use C-style cast to downcast from a base to a derived class
+}
+
+void arrays_polymorphic() {
+  PolymorphicBase ArrayOfPolymorphicBase[10];
+  auto AP0 = (PolymorphicDerived*)ArrayOfPolymorphicBase;
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: do not use C-style cast to downcast from a base to a derived class; use dynamic_cast instead
+  // CHECK-FIXES: auto AP0 = dynamic_cast<PolymorphicDerived*>(ArrayOfPolymorphicBase);
+}
+
+void references() {
+  Base B0;
+  auto R0 = (Derived&)B0;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: do not use C-style cast to downcast from a base to a derived class
+  Base& RefToBase = B0;
+  auto R1 = (Derived&)RefToBase;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: do not use C-style cast to downcast from a base to a derived class
+
+  const Base& ConstRefToBase = B0;
+  auto RC1 = (const Derived&)ConstRefToBase;
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: do not use C-style cast to downcast from a base to a derived class
+
+
+  Derived RD1;
+  auto R2 = (Base&)RD1; // OK, upcast to a public base
+}
+
+void references_polymorphic() {
+  PolymorphicBase B0;
+  auto RP0 = (PolymorphicDerived&)B0;
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: do not use C-style cast to downcast from a base to a derived class; use dynamic_cast instead
+  // CHECK-FIXES: auto RP0 = dynamic_cast<PolymorphicDerived&>(B0);
+
+  PolymorphicBase& RefToPolymorphicBase = B0;
+  auto RP1 = (PolymorphicDerived&)RefToPolymorphicBase;
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: do not use C-style cast to downcast from a base to a derived class; use dynamic_cast instead
+  // CHECK-FIXES: auto RP1 = dynamic_cast<PolymorphicDerived&>(RefToPolymorphicBase);
+
+  const PolymorphicBase& ConstRefToPolymorphicBase = B0;
+  auto RPC2 = (const PolymorphicDerived&)(ConstRefToPolymorphicBase);
+  // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: do not use C-style cast to downcast from a base to a derived class; use dynamic_cast instead
+  // CHECK-FIXES: auto RPC2 = dynamic_cast<const PolymorphicDerived&>(ConstRefToPolymorphicBase);
+
+  PolymorphicDerived d1;
+  auto RP2 = (PolymorphicBase&)d1; // OK, upcast to a public base
+}
+
+template<class B, class D>
+void templ() {
+  auto B0 = (B*)new D();
+}
+
+void templ_bad_call() {
+  templ<Derived, Base>(); //FIXME: this should trigger a warning
+}
+
+void templ_good_call() {
+  templ<Base, Derived>(); // OK, upcast to a public base
+}
diff --git a/test/clang-tidy/cppcoreguidelines-pro-type-member-init-cxx98.cpp b/test/clang-tidy/cppcoreguidelines-pro-type-member-init-cxx98.cpp
new file mode 100644 (file)
index 0000000..db97bb4
--- /dev/null
@@ -0,0 +1,116 @@
+// RUN: %check_clang_tidy %s cppcoreguidelines-pro-type-member-init %t -- -- -std=c++98 -fno-delayed-template-parsing
+
+struct PositiveFieldBeforeConstructor {
+  int F;
+  PositiveFieldBeforeConstructor() /* some comment */ {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: F
+  // CHECK-FIXES: PositiveFieldBeforeConstructor() : F() /* some comment */ {}
+};
+
+struct PositiveFieldAfterConstructor {
+  PositiveFieldAfterConstructor() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: F, G, H
+  // CHECK-FIXES: PositiveFieldAfterConstructor() : F(), G(), H() {}
+  int F;
+  bool G /* with comment */;
+  int *H;
+  PositiveFieldBeforeConstructor IgnoredField;
+};
+
+struct PositiveSeparateDefinition {
+  PositiveSeparateDefinition();
+  int F;
+};
+
+PositiveSeparateDefinition::PositiveSeparateDefinition() {}
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: constructor does not initialize these fields: F
+// CHECK-FIXES: PositiveSeparateDefinition::PositiveSeparateDefinition() : F() {}
+
+struct PositiveMixedFieldOrder {
+  PositiveMixedFieldOrder() : /* some comment */ J(0), L(0), M(0) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: I, K, N
+  // CHECK-FIXES: PositiveMixedFieldOrder() : I(), /* some comment */ J(0), K(), L(0), M(0), N() {}
+  int I;
+  int J;
+  int K;
+  int L;
+  int M;
+  int N;
+};
+
+struct PositiveAfterBaseInitializer : public PositiveMixedFieldOrder {
+  PositiveAfterBaseInitializer() : PositiveMixedFieldOrder() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: F
+  // CHECK-FIXES: PositiveAfterBaseInitializer() : PositiveMixedFieldOrder(), F() {}
+  int F;
+};
+
+struct NegativeFieldInitialized {
+  int F;
+
+  NegativeFieldInitialized() : F() {}
+};
+
+struct NegativeFieldInitializedInDefinition {
+  int F;
+
+  NegativeFieldInitializedInDefinition();
+};
+
+NegativeFieldInitializedInDefinition::NegativeFieldInitializedInDefinition() : F() {}
+
+struct NegativeInitializedInBody {
+  NegativeInitializedInBody() { I = 0; }
+  int I;
+};
+
+struct NegativeAggregateType {
+  int X;
+  int Y;
+  int Z;
+};
+
+struct TrivialType {
+  int X;
+  int Y;
+};
+
+struct PositiveUninitializedBaseOrdering : public NegativeAggregateType,
+                                           public TrivialType {
+  PositiveUninitializedBaseOrdering() : NegativeAggregateType(), TrivialType(), B() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: A
+  // CHECK-FIXES: PositiveUninitializedBaseOrdering() : NegativeAggregateType(), TrivialType(), A(), B() {}
+
+  // This is somewhat pathological with the base class initializer at the end...
+  PositiveUninitializedBaseOrdering(int) : B(), TrivialType(), A() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these bases: NegativeAggregateType
+  // CHECK-FIXES: PositiveUninitializedBaseOrdering(int) : B(), NegativeAggregateType(), TrivialType(), A() {}
+
+  PositiveUninitializedBaseOrdering(float) : NegativeAggregateType(), A() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these bases: TrivialType
+  // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: constructor does not initialize these fields: B
+  // CHECK-FIXES: PositiveUninitializedBaseOrdering(float) : NegativeAggregateType(), TrivialType(), A(), B() {}
+
+  int A, B;
+};
+
+template <class T>
+class PositiveTemplateBase : T {
+public:
+  PositiveTemplateBase() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: X
+  // CHECK-FIXES: PositiveTemplateBase() : X() {}
+
+  int X;
+};
+
+class PositiveIndirectMember {
+  struct {
+    int *A;
+  };
+
+  PositiveIndirectMember() : A() {}
+  PositiveIndirectMember(int) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: A
+  // CHECK-FIXES: PositiveIndirectMember(int) : A() {}
+};
diff --git a/test/clang-tidy/cppcoreguidelines-pro-type-member-init-delayed.cpp b/test/clang-tidy/cppcoreguidelines-pro-type-member-init-delayed.cpp
new file mode 100644 (file)
index 0000000..d3436ba
--- /dev/null
@@ -0,0 +1,32 @@
+// RUN: %check_clang_tidy %s cppcoreguidelines-pro-type-member-init %t -- -- -fdelayed-template-parsing
+
+template <class T>
+struct PositiveFieldBeforeConstructor {
+  int F;
+  bool G /* with comment */;
+  int *H;
+  PositiveFieldBeforeConstructor() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: F, G, H
+};
+// Explicit instantiation.
+template class PositiveFieldBeforeConstructor<int>;
+
+template <class T>
+struct PositiveFieldAfterConstructor {
+  PositiveFieldAfterConstructor() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: F, G, H
+  int F;
+  bool G /* with comment */;
+  int *H;
+};
+// Explicit instantiation.
+template class PositiveFieldAfterConstructor<int>;
+
+// This declaration isn't used and won't be parsed 'delayed-template-parsing'.
+// The body of the declaration is 'null' and may cause crash if not handled
+// properly by checkers.
+template <class T>
+struct UnusedDelayedConstructor {
+  UnusedDelayedConstructor() {}
+  int F;
+};
diff --git a/test/clang-tidy/cppcoreguidelines-pro-type-member-init.cpp b/test/clang-tidy/cppcoreguidelines-pro-type-member-init.cpp
new file mode 100644 (file)
index 0000000..7b1d96f
--- /dev/null
@@ -0,0 +1,488 @@
+// RUN: %check_clang_tidy %s cppcoreguidelines-pro-type-member-init %t -- -- -std=c++11 -fno-delayed-template-parsing
+
+struct PositiveFieldBeforeConstructor {
+  int F;
+  // CHECK-FIXES: int F{};
+  PositiveFieldBeforeConstructor() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: F
+  // CHECK-FIXES: PositiveFieldBeforeConstructor() {}
+};
+
+struct PositiveFieldAfterConstructor {
+  PositiveFieldAfterConstructor() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: F, G
+  // CHECK-FIXES: PositiveFieldAfterConstructor() {}
+  int F;
+  // CHECK-FIXES: int F{};
+  bool G /* with comment */;
+  // CHECK-FIXES: bool G{} /* with comment */;
+  PositiveFieldBeforeConstructor IgnoredField;
+};
+
+struct PositiveSeparateDefinition {
+  PositiveSeparateDefinition();
+  int F;
+  // CHECK-FIXES: int F{};
+};
+
+PositiveSeparateDefinition::PositiveSeparateDefinition() {}
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: constructor does not initialize these fields: F
+// CHECK-FIXES: PositiveSeparateDefinition::PositiveSeparateDefinition() {}
+
+struct PositiveMixedFieldOrder {
+  PositiveMixedFieldOrder() : J(0) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: I, K
+  // CHECK-FIXES: PositiveMixedFieldOrder() : J(0) {}
+  int I;
+  // CHECK-FIXES: int I{};
+  int J;
+  int K;
+  // CHECK-FIXES: int K{};
+};
+
+template <typename T>
+struct Template {
+  Template() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: F
+  int F;
+  // CHECK-FIXES: int F{};
+  T T1;
+  // CHECK-FIXES: T T1;
+};
+
+void instantiate() {
+  Template<int> TInt;
+}
+
+struct NegativeFieldInitialized {
+  int F;
+
+  NegativeFieldInitialized() : F() {}
+};
+
+struct NegativeFieldInitializedInDefinition {
+  int F;
+
+  NegativeFieldInitializedInDefinition();
+};
+NegativeFieldInitializedInDefinition::NegativeFieldInitializedInDefinition() : F() {}
+
+struct NegativeInClassInitialized {
+  int F = 0;
+
+  NegativeInClassInitialized() {}
+};
+
+struct NegativeInClassInitializedDefaulted {
+  int F = 0;
+  NegativeInClassInitializedDefaulted() = default;
+};
+
+struct NegativeConstructorDelegated {
+  int F;
+
+  NegativeConstructorDelegated(int F) : F(F) {}
+  NegativeConstructorDelegated() : NegativeConstructorDelegated(0) {}
+};
+
+struct NegativeInitializedInBody {
+  NegativeInitializedInBody() { I = 0; }
+  int I;
+};
+
+struct A {};
+template <class> class AA;
+template <class T> class NegativeTemplateConstructor {
+  NegativeTemplateConstructor(const AA<T> &, A) {}
+  bool Bool{false};
+  // CHECK-FIXES: bool Bool{false};
+};
+
+#define UNINITIALIZED_FIELD_IN_MACRO_BODY(FIELD) \
+  struct UninitializedField##FIELD {             \
+    UninitializedField##FIELD() {}               \
+    int FIELD;                                   \
+  };                                             \
+// Ensure FIELD is not initialized since fixes inside of macros are disabled.
+// CHECK-FIXES: int FIELD;
+
+UNINITIALIZED_FIELD_IN_MACRO_BODY(F);
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: constructor does not initialize these fields: F
+UNINITIALIZED_FIELD_IN_MACRO_BODY(G);
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: constructor does not initialize these fields: G
+
+#define UNINITIALIZED_FIELD_IN_MACRO_ARGUMENT(ARGUMENT) \
+  ARGUMENT
+
+UNINITIALIZED_FIELD_IN_MACRO_ARGUMENT(struct UninitializedFieldInMacroArg {
+  UninitializedFieldInMacroArg() {}
+  int Field;
+});
+// CHECK-MESSAGES: :[[@LINE-3]]:3: warning: constructor does not initialize these fields: Field
+// Ensure FIELD is not initialized since fixes inside of macros are disabled.
+// CHECK-FIXES: int Field;
+
+struct NegativeAggregateType {
+  int X;
+  int Y;
+  int Z;
+};
+
+struct PositiveTrivialType {
+  PositiveTrivialType() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: F
+
+  NegativeAggregateType F;
+  // CHECK-FIXES: NegativeAggregateType F{};
+};
+
+struct NegativeNonTrivialType {
+  PositiveTrivialType F;
+};
+
+static void PositiveUninitializedTrivialType() {
+  NegativeAggregateType X;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: uninitialized record type: 'X'
+  // CHECK-FIXES: NegativeAggregateType X{};
+
+  NegativeAggregateType A[10]; // Don't warn because this isn't an object type.
+}
+
+static void NegativeInitializedTrivialType() {
+  NegativeAggregateType X{};
+  NegativeAggregateType Y = {};
+  NegativeAggregateType Z = NegativeAggregateType();
+  NegativeAggregateType A[10]{};
+  NegativeAggregateType B[10] = {};
+  int C; // No need to initialize this because we don't have a constructor.
+  int D[8];
+  NegativeAggregateType E = {0, 1, 2};
+  NegativeAggregateType F({});
+}
+
+struct NonTrivialType {
+  NonTrivialType() = default;
+  NonTrivialType(const NonTrivialType &RHS) : X(RHS.X), Y(RHS.Y) {}
+
+  int X;
+  int Y;
+};
+
+static void PositiveNonTrivialTypeWithCopyConstructor() {
+  NonTrivialType T;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: uninitialized record type: 'T'
+  // CHECK-FIXES: NonTrivialType T{};
+
+  NonTrivialType A[8];
+  // Don't warn because this isn't an object type
+}
+
+struct ComplexNonTrivialType {
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: constructor does not initialize these fields: Y
+  NegativeFieldInitialized X;
+  int Y;
+  // CHECK-FIXES: int Y{};
+};
+
+static void PositiveComplexNonTrivialType() {
+  ComplexNonTrivialType T;
+}
+
+struct NegativeStaticMember {
+  static NonTrivialType X;
+  static NonTrivialType Y;
+  static constexpr NonTrivialType Z{};
+};
+
+NonTrivialType NegativeStaticMember::X;
+NonTrivialType NegativeStaticMember::Y{};
+
+struct PositiveMultipleConstructors {
+  PositiveMultipleConstructors() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: A, B
+
+  PositiveMultipleConstructors(int) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: A, B
+
+  PositiveMultipleConstructors(const PositiveMultipleConstructors &) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: A, B
+
+  // FIXME: The fix-its here collide providing an erroneous fix
+  int A, B;
+  // CHECK-FIXES: int A{}{}{}, B{}{}{};
+};
+
+typedef struct {
+  int Member;
+} CStyleStruct;
+
+struct PositiveUninitializedBase : public NegativeAggregateType, CStyleStruct {
+  PositiveUninitializedBase() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these bases: NegativeAggregateType, CStyleStruct
+  // CHECK-FIXES: PositiveUninitializedBase() : NegativeAggregateType(), CStyleStruct() {}
+};
+
+struct PositiveUninitializedBaseOrdering : public NegativeAggregateType,
+                                           public NonTrivialType {
+  PositiveUninitializedBaseOrdering() : B() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these bases: NegativeAggregateType, NonTrivialType
+  // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: constructor does not initialize these fields: A
+  // CHECK-FIXES: PositiveUninitializedBaseOrdering() : NegativeAggregateType(), NonTrivialType(), B() {}
+
+  // This is somewhat pathological with the base class initializer at the end...
+  PositiveUninitializedBaseOrdering(int) : B(), NonTrivialType(), A() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these bases: NegativeAggregateType
+  // CHECK-FIXES: PositiveUninitializedBaseOrdering(int) : B(), NegativeAggregateType(), NonTrivialType(), A() {}
+
+  PositiveUninitializedBaseOrdering(float) : B(), NegativeAggregateType(), A() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these bases: NonTrivialType
+  // CHECK-FIXES: PositiveUninitializedBaseOrdering(float) : B(), NegativeAggregateType(), NonTrivialType(), A() {}
+
+  int A, B;
+  // CHECK-FIXES: int A{}, B;
+};
+
+// We shouldn't need to initialize anything because PositiveUninitializedBase
+// has a user-defined constructor.
+struct NegativeUninitializedBase : public PositiveUninitializedBase {
+  NegativeUninitializedBase() {}
+};
+
+struct InheritedAggregate : public NegativeAggregateType {
+  int F;
+};
+
+static InheritedAggregate NegativeGlobal;
+
+enum TestEnum {
+  A,
+  B,
+  C
+};
+
+enum class TestScopedEnum {
+  A,
+  B,
+  C
+};
+
+struct PositiveEnumType {
+  PositiveEnumType() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: X, Y
+  // No proposed fixes, as we don't know whether value initialization for these
+  // enums really makes sense.
+
+  TestEnum X;
+  TestScopedEnum Y;
+};
+
+extern "C" {
+struct NegativeCStruct {
+  int X, Y, Z;
+};
+
+static void PositiveCStructVariable() {
+  NegativeCStruct X;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: uninitialized record type: 'X'
+  // CHECK-FIXES: NegativeCStruct X{};
+}
+}
+
+static void NegativeStaticVariable() {
+  static NegativeCStruct S;
+  (void)S;
+}
+
+union NegativeUnionInClass {
+  NegativeUnionInClass() {} // No message as a union can only initialize one member.
+  int X = 0;
+  float Y;
+};
+
+union PositiveUnion {
+  PositiveUnion() : X() {} // No message as a union can only initialize one member.
+  PositiveUnion(int) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: union constructor should initialize one of these fields: X, Y
+
+  int X;
+  // CHECK-FIXES: int X{};
+
+  // Make sure we don't give Y an initializer.
+  float Y;
+  // CHECK-FIXES-NOT: float Y{};
+};
+
+struct PositiveAnonymousUnionAndStruct {
+  PositiveAnonymousUnionAndStruct() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: A, B, Y, Z, C, D, E, F, X
+
+  union {
+    int A;
+    // CHECK-FIXES: int A{};
+    short B;
+  };
+
+  struct {
+    int Y;
+    // CHECK-FIXES: int Y{};
+    char *Z;
+    // CHECK-FIXES: char *Z{};
+
+    struct {
+      short C;
+      // CHECK-FIXES: short C{};
+      double D;
+      // CHECK-FIXES: double D{};
+    };
+
+    union {
+      long E;
+      // CHECK-FIXES: long E{};
+      float F;
+    };
+  };
+  int X;
+  // CHECK-FIXES: int X{};
+};
+
+// This check results in a CXXConstructorDecl with no body.
+struct NegativeDeletedConstructor : NegativeAggregateType {
+  NegativeDeletedConstructor() = delete;
+
+  Template<int> F;
+};
+
+// This pathological template fails to compile if actually instantiated. It
+// results in the check seeing a null RecordDecl when examining the base class
+// initializer list.
+template <typename T>
+class PositiveSelfInitialization : NegativeAggregateType
+{
+  PositiveSelfInitialization() : PositiveSelfInitialization() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these bases: NegativeAggregateType
+  // CHECK-FIXES: PositiveSelfInitialization() : NegativeAggregateType(), PositiveSelfInitialization() {}
+};
+
+class PositiveIndirectMember {
+  struct {
+    int *A;
+    // CHECK-FIXES: int *A{};
+  };
+
+  PositiveIndirectMember() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: A
+};
+
+void Bug30487()
+{
+  NegativeInClassInitializedDefaulted s;
+}
+
+struct PositiveVirtualMethod {
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: constructor does not initialize these fields: F
+  int F;
+  // CHECK-FIXES: int F{};
+  virtual int f() = 0;
+};
+
+struct PositiveVirtualDestructor {
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: constructor does not initialize these fields: F
+  PositiveVirtualDestructor() = default;
+  int F;
+  // CHECK-FIXES: int F{};
+  virtual ~PositiveVirtualDestructor() {}
+};
+
+struct PositiveVirtualBase : public virtual NegativeAggregateType {
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: constructor does not initialize these bases: NegativeAggregateType
+  // CHECK-MESSAGES: :[[@LINE-2]]:8: warning: constructor does not initialize these fields: F
+  int F;
+  // CHECK-FIXES: int F{};
+};
+
+template <typename T>
+struct PositiveTemplateVirtualDestructor {
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: constructor does not initialize these fields: F
+  T Val;
+  int F;
+  // CHECK-FIXES: int F{};
+  virtual ~PositiveTemplateVirtualDestructor() = default;
+};
+
+template struct PositiveTemplateVirtualDestructor<int>;
+
+#define UNINITIALIZED_FIELD_IN_MACRO_BODY_VIRTUAL(FIELD) \
+  struct UninitializedFieldVirtual##FIELD {              \
+    int FIELD;                                           \
+    virtual ~UninitializedFieldVirtual##FIELD() {}       \
+  };                                                     \
+// Ensure FIELD is not initialized since fixes inside of macros are disabled.
+// CHECK-FIXES: int FIELD;
+
+UNINITIALIZED_FIELD_IN_MACRO_BODY_VIRTUAL(F);
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: constructor does not initialize these fields: F
+UNINITIALIZED_FIELD_IN_MACRO_BODY_VIRTUAL(G);
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: constructor does not initialize these fields: G
+
+struct NegativeEmpty {
+};
+
+static void NegativeEmptyVar() {
+  NegativeEmpty e;
+  (void)e;
+}
+
+struct NegativeEmptyMember {
+  NegativeEmptyMember() {}
+  NegativeEmpty e;
+};
+
+struct NegativeEmptyBase : NegativeEmpty {
+  NegativeEmptyBase() {}
+};
+
+struct NegativeEmptyArrayMember {
+  NegativeEmptyArrayMember() {}
+  char e[0];
+};
+
+struct NegativeIncompleteArrayMember {
+  NegativeIncompleteArrayMember() {}
+  char e[];
+};
+
+template <typename T> class NoCrash {
+  class B : public NoCrash {
+    template <typename U> B(U u) {}
+  };
+};
+
+struct PositiveBitfieldMember {
+  PositiveBitfieldMember() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: F
+  unsigned F : 5;
+};
+
+struct NegativeUnnamedBitfieldMember {
+  NegativeUnnamedBitfieldMember() {}
+  unsigned : 5;
+};
+
+struct NegativeInitializedBitfieldMembers {
+  NegativeInitializedBitfieldMembers() : F(3) { G = 2; }
+  unsigned F : 5;
+  unsigned G : 5;
+};
+
+struct NegativeImplicitInheritedCtorBase {
+  NegativeImplicitInheritedCtorBase(unsigned F) : F(F) {}
+  unsigned F;
+};
+
+struct NegativeImplicitInheritedCtor : NegativeImplicitInheritedCtorBase {
+  using NegativeImplicitInheritedCtorBase::NegativeImplicitInheritedCtorBase;
+};
+
+void Bug33557() {
+  NegativeImplicitInheritedCtor I(5);
+}
diff --git a/test/clang-tidy/cppcoreguidelines-pro-type-reinterpret-cast.cpp b/test/clang-tidy/cppcoreguidelines-pro-type-reinterpret-cast.cpp
new file mode 100644 (file)
index 0000000..5420810
--- /dev/null
@@ -0,0 +1,6 @@
+// RUN: %check_clang_tidy %s cppcoreguidelines-pro-type-reinterpret-cast %t
+
+int i = 0;
+void *j;
+void f() { j = reinterpret_cast<void *>(i); }
+// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: do not use reinterpret_cast [cppcoreguidelines-pro-type-reinterpret-cast]
diff --git a/test/clang-tidy/cppcoreguidelines-pro-type-static-cast-downcast.cpp b/test/clang-tidy/cppcoreguidelines-pro-type-static-cast-downcast.cpp
new file mode 100644 (file)
index 0000000..7a6c027
--- /dev/null
@@ -0,0 +1,118 @@
+// RUN: %check_clang_tidy %s cppcoreguidelines-pro-type-static-cast-downcast %t
+
+class Base {
+};
+
+class Derived : public Base {
+};
+
+class Base2 {
+};
+
+class MultiDerived : public Base, public Base2 {
+};
+
+class PolymorphicBase {
+public:
+  virtual ~PolymorphicBase();
+};
+
+class PolymorphicDerived : public PolymorphicBase {
+};
+
+class PolymorphicMultiDerived : public Base, public PolymorphicBase {
+};
+
+void pointers() {
+
+  auto P0 = static_cast<Derived*>(new Base());
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: do not use static_cast to downcast from a base to a derived class [cppcoreguidelines-pro-type-static-cast-downcast]
+
+  const Base* B0;
+  auto PC0 = static_cast<const Derived*>(B0);
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: do not use static_cast to downcast from a base to a derived class [cppcoreguidelines-pro-type-static-cast-downcast]
+
+  auto P1 = static_cast<Base*>(new Derived()); // OK, upcast to a public base
+  auto P2 = static_cast<Base*>(new MultiDerived()); // OK, upcast to a public base
+  auto P3 = static_cast<Base2*>(new MultiDerived()); // OK, upcast to a public base
+}
+
+void pointers_polymorphic() {
+
+  auto PP0 = static_cast<PolymorphicDerived*>(new PolymorphicBase());
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: do not use static_cast to downcast from a base to a derived class; use dynamic_cast instead [cppcoreguidelines-pro-type-static-cast-downcast]
+  // CHECK-FIXES: auto PP0 = dynamic_cast<PolymorphicDerived*>(new PolymorphicBase());
+
+  const PolymorphicBase* B0;
+  auto PPC0 = static_cast<const PolymorphicDerived*>(B0);
+  // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: do not use static_cast to downcast from a base to a derived class; use dynamic_cast instead [cppcoreguidelines-pro-type-static-cast-downcast]
+  // CHECK-FIXES: auto PPC0 = dynamic_cast<const PolymorphicDerived*>(B0);
+
+
+  auto B1 = static_cast<PolymorphicBase*>(new PolymorphicDerived()); // OK, upcast to a public base
+  auto B2 = static_cast<PolymorphicBase*>(new PolymorphicMultiDerived()); // OK, upcast to a public base
+  auto B3 = static_cast<Base*>(new PolymorphicMultiDerived()); // OK, upcast to a public base
+}
+
+void arrays() {
+  Base ArrayOfBase[10];
+  auto A0 = static_cast<Derived*>(ArrayOfBase);
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: do not use static_cast to downcast from a base to a derived class [cppcoreguidelines-pro-type-static-cast-downcast]
+}
+
+void arrays_polymorphic() {
+  PolymorphicBase ArrayOfPolymorphicBase[10];
+  auto AP0 = static_cast<PolymorphicDerived*>(ArrayOfPolymorphicBase);
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: do not use static_cast to downcast from a base to a derived class; use dynamic_cast instead
+  // CHECK-FIXES: auto AP0 = dynamic_cast<PolymorphicDerived*>(ArrayOfPolymorphicBase);
+}
+
+void references() {
+  Base B0;
+  auto R0 = static_cast<Derived&>(B0);
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: do not use static_cast to downcast from a base to a derived class [cppcoreguidelines-pro-type-static-cast-downcast]
+  Base& RefToBase = B0;
+  auto R1 = static_cast<Derived&>(RefToBase);
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: do not use static_cast to downcast from a base to a derived class [cppcoreguidelines-pro-type-static-cast-downcast]
+
+  const Base& ConstRefToBase = B0;
+  auto RC1 = static_cast<const Derived&>(ConstRefToBase);
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: do not use static_cast to downcast from a base to a derived class [cppcoreguidelines-pro-type-static-cast-downcast]
+
+
+  Derived RD1;
+  auto R2 = static_cast<Base&>(RD1); // OK, upcast to a public base
+}
+
+void references_polymorphic() {
+  PolymorphicBase B0;
+  auto RP0 = static_cast<PolymorphicDerived&>(B0);
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: do not use static_cast to downcast from a base to a derived class; use dynamic_cast instead
+  // CHECK-FIXES: auto RP0 = dynamic_cast<PolymorphicDerived&>(B0);
+
+  PolymorphicBase& RefToPolymorphicBase = B0;
+  auto RP1 = static_cast<PolymorphicDerived&>(RefToPolymorphicBase);
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: do not use static_cast to downcast from a base to a derived class; use dynamic_cast instead [cppcoreguidelines-pro-type-static-cast-downcast]
+  // CHECK-FIXES: auto RP1 = dynamic_cast<PolymorphicDerived&>(RefToPolymorphicBase);
+
+  const PolymorphicBase& ConstRefToPolymorphicBase = B0;
+  auto RPC2 = static_cast<const PolymorphicDerived&>(ConstRefToPolymorphicBase);
+  // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: do not use static_cast to downcast from a base to a derived class; use dynamic_cast instead [cppcoreguidelines-pro-type-static-cast-downcast]
+  // CHECK-FIXES: auto RPC2 = dynamic_cast<const PolymorphicDerived&>(ConstRefToPolymorphicBase);
+
+  PolymorphicDerived d1;
+  auto RP2 = static_cast<PolymorphicBase&>(d1); // OK, upcast to a public base
+}
+
+template<class B, class D>
+void templ() {
+  auto B0 = static_cast<B*>(new D());
+}
+
+void templ_bad_call() {
+  templ<Derived, Base>(); //FIXME: this should trigger a warning
+}
+
+void templ_good_call() {
+  templ<Base, Derived>(); // OK, upcast to a public base
+}
diff --git a/test/clang-tidy/cppcoreguidelines-pro-type-union-access.cpp b/test/clang-tidy/cppcoreguidelines-pro-type-union-access.cpp
new file mode 100644 (file)
index 0000000..6abc22b
--- /dev/null
@@ -0,0 +1,41 @@
+// RUN: %check_clang_tidy %s cppcoreguidelines-pro-type-union-access %t
+
+union U {
+  bool union_member1;
+  char union_member2;
+} u;
+
+struct S {
+  int non_union_member;
+  union {
+    bool union_member;
+  };
+  union {
+    char union_member2;
+  } u;
+} s;
+
+
+void f(char);
+void f2(U);
+void f3(U&);
+void f4(U*);
+
+void check()
+{
+  u.union_member1 = true;
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: do not access members of unions; use (boost::)variant instead [cppcoreguidelines-pro-type-union-access]
+  auto b = u.union_member2;
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: do not access members of unions; use (boost::)variant instead
+  auto a = &s.union_member;
+  // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: do not access members of unions; use (boost::)variant instead
+  f(s.u.union_member2);
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: do not access members of unions; use (boost::)variant instead
+
+  s.non_union_member = 2; // OK
+
+  U u2 = u; // OK
+  f2(u); // OK
+  f3(u); // OK
+  f4(&u); // OK
+}
diff --git a/test/clang-tidy/cppcoreguidelines-pro-type-vararg.cpp b/test/clang-tidy/cppcoreguidelines-pro-type-vararg.cpp
new file mode 100644 (file)
index 0000000..021322a
--- /dev/null
@@ -0,0 +1,51 @@
+// RUN: %check_clang_tidy %s cppcoreguidelines-pro-type-vararg %t
+
+void f(int i);
+void f_vararg(int i, ...);
+
+struct C {
+  void g_vararg(...);
+  void g(const char*);
+} c;
+
+template<typename... P>
+void cpp_vararg(P... p);
+
+void check() {
+  f_vararg(1, 7, 9);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not call c-style vararg functions [cppcoreguidelines-pro-type-vararg]
+  c.g_vararg("foo");
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: do not call c-style vararg functions
+
+  f(3); // OK
+  c.g("foo"); // OK
+  cpp_vararg(1, 7, 9); // OK
+}
+
+// ... as a parameter is allowed (e.g. for SFINAE)
+template <typename T>
+void CallFooIfAvailableImpl(T& t, ...) {
+  // nothing
+}
+template <typename T>
+void CallFooIfAvailableImpl(T& t, decltype(t.foo())*) {
+  t.foo();
+}
+template <typename T>
+void CallFooIfAvailable(T& t) {
+  CallFooIfAvailableImpl(t, 0); // OK to call variadic function when the argument is a literal 0
+}
+
+#include <stdarg.h>
+void my_printf(const char* format, ...) {
+  va_list ap;
+  va_start(ap, format);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not call c-style vararg functions
+  va_list n;
+  va_copy(n, ap); // Don't warn, va_copy is anyway useless without va_start
+  int i = va_arg(ap, int);
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: do not use va_start/va_arg to define c-style vararg functions; use variadic templates instead
+  va_end(ap); // Don't warn, va_end is anyway useless without va_start
+}
+
+int my_vprintf(const char* format, va_list arg ); // OK to declare function taking va_list
diff --git a/test/clang-tidy/cppcoreguidelines-slicing.cpp b/test/clang-tidy/cppcoreguidelines-slicing.cpp
new file mode 100644 (file)
index 0000000..6856f52
--- /dev/null
@@ -0,0 +1,100 @@
+// RUN: %check_clang_tidy %s cppcoreguidelines-slicing %t
+
+class Base {
+  int i;
+  void f() {}
+  virtual void g() {}
+};
+
+class DerivedWithMemberVariables : public Base {
+  void f();
+  int j;
+};
+
+class TwiceDerivedWithNoMemberVariables : public DerivedWithMemberVariables {
+  void f();
+};
+
+class DerivedWithOverride : public Base {
+  void f();
+  void g() override {}
+};
+
+class TwiceDerivedWithNoOverride : public DerivedWithOverride {
+  void f();
+};
+
+void TakesBaseByValue(Base base);
+
+DerivedWithMemberVariables ReturnsDerived();
+
+void positivesWithMemberVariables() {
+  DerivedWithMemberVariables b;
+  Base a{b};
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: slicing object from type 'DerivedWithMemberVariables' to 'Base' discards {{[0-9]*}} bytes of state [cppcoreguidelines-slicing]
+  a = b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: slicing object from type 'DerivedWithMemberVariables' to 'Base' discards {{[0-9]*}} bytes of state
+  TakesBaseByValue(b);
+  // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: slicing object from type 'DerivedWithMemberVariables' to 'Base' discards {{[0-9]*}} bytes of state
+
+  TwiceDerivedWithNoMemberVariables c;
+  a = c;
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: slicing object from type 'TwiceDerivedWithNoMemberVariables' to 'Base' discards {{[0-9]*}} bytes of state
+
+  a = ReturnsDerived();
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: slicing object from type 'DerivedWithMemberVariables' to 'Base' discards {{[0-9]*}} bytes of state
+}
+
+void positivesWithOverride() {
+  DerivedWithOverride b;
+  Base a{b};
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: slicing object from type 'DerivedWithOverride' to 'Base' discards override 'g'
+  a = b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: slicing object from type 'DerivedWithOverride' to 'Base' discards override 'g'
+  TakesBaseByValue(b);
+  // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: slicing object from type 'DerivedWithOverride' to 'Base' discards override 'g'
+
+  TwiceDerivedWithNoOverride c;
+  a = c;
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: slicing object from type 'DerivedWithOverride' to 'Base' discards override 'g'
+}
+
+void TakesBaseByReference(Base &base);
+
+class DerivedThatAddsVirtualH : public Base {
+  virtual void h();
+};
+
+class DerivedThatOverridesH : public DerivedThatAddsVirtualH {
+  void h() override;
+};
+
+void negatives() {
+  // OK, simple copying from the same type.
+  Base a;
+  TakesBaseByValue(a);
+  DerivedWithMemberVariables b;
+  DerivedWithMemberVariables c{b};
+  b = c;
+
+  // OK, derived type does not have extra state.
+  TwiceDerivedWithNoMemberVariables d;
+  DerivedWithMemberVariables e{d};
+  e = d;
+
+  // OK, derived does not override any method.
+  TwiceDerivedWithNoOverride f;
+  DerivedWithOverride g{f};
+  g = f;
+
+  // OK, no copying.
+  TakesBaseByReference(d);
+  TakesBaseByReference(f);
+
+  // Derived type overrides methods, but these methods are not in the base type,
+  // so cannot be called accidentally. Right now this triggers, but we might
+  // want to allow it.
+  DerivedThatOverridesH h;
+  a = h;
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: slicing object from type 'DerivedThatOverridesH' to 'Base' discards override 'h'
+}
diff --git a/test/clang-tidy/cppcoreguidelines-special-member-functions-cxx-03.cpp b/test/clang-tidy/cppcoreguidelines-special-member-functions-cxx-03.cpp
new file mode 100644 (file)
index 0000000..8fe5e42
--- /dev/null
@@ -0,0 +1,26 @@
+// RUN: %check_clang_tidy %s cppcoreguidelines-special-member-functions %t -- -- -std=c++03
+
+class DefinesDestructor {
+  ~DefinesDestructor();
+};
+// CHECK-MESSAGES: [[@LINE-3]]:7: warning: class 'DefinesDestructor' defines a non-default destructor but does not define a copy constructor or a copy assignment operator [cppcoreguidelines-special-member-functions]
+
+class DefinesCopyConstructor {
+  DefinesCopyConstructor(const DefinesCopyConstructor &);
+};
+// CHECK-MESSAGES: [[@LINE-3]]:7: warning: class 'DefinesCopyConstructor' defines a copy constructor but does not define a destructor or a copy assignment operator [cppcoreguidelines-special-member-functions]
+
+class DefinesCopyAssignment {
+  DefinesCopyAssignment &operator=(const DefinesCopyAssignment &);
+};
+// CHECK-MESSAGES: [[@LINE-3]]:7: warning: class 'DefinesCopyAssignment' defines a copy assignment operator but does not define a destructor or a copy constructor [cppcoreguidelines-special-member-functions]
+
+class DefinesNothing {
+};
+
+class DefinesEverything {
+  DefinesEverything(const DefinesEverything &);
+  DefinesEverything &operator=(const DefinesEverything &);
+  ~DefinesEverything();
+};
+
diff --git a/test/clang-tidy/cppcoreguidelines-special-member-functions-relaxed.cpp b/test/clang-tidy/cppcoreguidelines-special-member-functions-relaxed.cpp
new file mode 100644 (file)
index 0000000..4fff02d
--- /dev/null
@@ -0,0 +1,71 @@
+// RUN: %check_clang_tidy %s cppcoreguidelines-special-member-functions %t -- -config="{CheckOptions: [{key: cppcoreguidelines-special-member-functions.AllowMissingMoveFunctions, value: 1}, {key: cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor, value: 1}]}" --
+
+class DefinesDestructor {
+  ~DefinesDestructor();
+};
+// CHECK-MESSAGES: [[@LINE-3]]:7: warning: class 'DefinesDestructor' defines a non-default destructor but does not define a copy constructor or a copy assignment operator [cppcoreguidelines-special-member-functions]
+
+class DefinesDefaultedDestructor {
+  ~DefinesDefaultedDestructor() = default;
+};
+
+class DefinesCopyConstructor {
+  DefinesCopyConstructor(const DefinesCopyConstructor &);
+};
+// CHECK-MESSAGES: [[@LINE-3]]:7: warning: class 'DefinesCopyConstructor' defines a copy constructor but does not define a destructor or a copy assignment operator [cppcoreguidelines-special-member-functions]
+
+class DefinesCopyAssignment {
+  DefinesCopyAssignment &operator=(const DefinesCopyAssignment &);
+};
+// CHECK-MESSAGES: [[@LINE-3]]:7: warning: class 'DefinesCopyAssignment' defines a copy assignment operator but does not define a destructor or a copy constructor [cppcoreguidelines-special-member-functions]
+
+class DefinesMoveConstructor {
+  DefinesMoveConstructor(DefinesMoveConstructor &&);
+};
+// CHECK-MESSAGES: [[@LINE-3]]:7: warning: class 'DefinesMoveConstructor' defines a move constructor but does not define a destructor, a copy constructor, a copy assignment operator or a move assignment operator [cppcoreguidelines-special-member-functions]
+
+class DefinesMoveAssignment {
+  DefinesMoveAssignment &operator=(DefinesMoveAssignment &&);
+};
+// CHECK-MESSAGES: [[@LINE-3]]:7: warning: class 'DefinesMoveAssignment' defines a move assignment operator but does not define a destructor, a copy constructor, a copy assignment operator or a move constructor [cppcoreguidelines-special-member-functions]
+class DefinesNothing {
+};
+
+class DefinesEverything {
+  DefinesEverything(const DefinesEverything &);
+  DefinesEverything &operator=(const DefinesEverything &);
+  DefinesEverything(DefinesEverything &&);
+  DefinesEverything &operator=(DefinesEverything &&);
+  ~DefinesEverything();
+};
+
+class DeletesEverything {
+  DeletesEverything(const DeletesEverything &) = delete;
+  DeletesEverything &operator=(const DeletesEverything &) = delete;
+  DeletesEverything(DeletesEverything &&) = delete;
+  DeletesEverything &operator=(DeletesEverything &&) = delete;
+  ~DeletesEverything() = delete;
+};
+
+class DeletesCopyDefaultsMove {
+  DeletesCopyDefaultsMove(const DeletesCopyDefaultsMove &) = delete;
+  DeletesCopyDefaultsMove &operator=(const DeletesCopyDefaultsMove &) = delete;
+  DeletesCopyDefaultsMove(DeletesCopyDefaultsMove &&) = default;
+  DeletesCopyDefaultsMove &operator=(DeletesCopyDefaultsMove &&) = default;
+  ~DeletesCopyDefaultsMove() = default;
+};
+
+template <typename T>
+struct TemplateClass {
+  TemplateClass() = default;
+  TemplateClass(const TemplateClass &);
+  TemplateClass &operator=(const TemplateClass &);
+  TemplateClass(TemplateClass &&);
+  TemplateClass &operator=(TemplateClass &&);
+  ~TemplateClass();
+};
+
+// Multiple instantiations of a class template will trigger multiple matches for defined special members.
+// This should not cause problems.
+TemplateClass<int> InstantiationWithInt;
+TemplateClass<double> InstantiationWithDouble;
diff --git a/test/clang-tidy/cppcoreguidelines-special-member-functions.cpp b/test/clang-tidy/cppcoreguidelines-special-member-functions.cpp
new file mode 100644 (file)
index 0000000..013ba86
--- /dev/null
@@ -0,0 +1,72 @@
+// RUN: %check_clang_tidy %s cppcoreguidelines-special-member-functions %t
+
+class DefinesDestructor {
+  ~DefinesDestructor();
+};
+// CHECK-MESSAGES: [[@LINE-3]]:7: warning: class 'DefinesDestructor' defines a non-default destructor but does not define a copy constructor, a copy assignment operator, a move constructor or a move assignment operator [cppcoreguidelines-special-member-functions]
+
+class DefinesDefaultedDestructor {
+  ~DefinesDefaultedDestructor() = default;
+};
+// CHECK-MESSAGES: [[@LINE-3]]:7: warning: class 'DefinesDefaultedDestructor' defines a default destructor but does not define a copy constructor, a copy assignment operator, a move constructor or a move assignment operator [cppcoreguidelines-special-member-functions]
+
+class DefinesCopyConstructor {
+  DefinesCopyConstructor(const DefinesCopyConstructor &);
+};
+// CHECK-MESSAGES: [[@LINE-3]]:7: warning: class 'DefinesCopyConstructor' defines a copy constructor but does not define a destructor, a copy assignment operator, a move constructor or a move assignment operator [cppcoreguidelines-special-member-functions]
+
+class DefinesCopyAssignment {
+  DefinesCopyAssignment &operator=(const DefinesCopyAssignment &);
+};
+// CHECK-MESSAGES: [[@LINE-3]]:7: warning: class 'DefinesCopyAssignment' defines a copy assignment operator but does not define a destructor, a copy constructor, a move constructor or a move assignment operator [cppcoreguidelines-special-member-functions]
+
+class DefinesMoveConstructor {
+  DefinesMoveConstructor(DefinesMoveConstructor &&);
+};
+// CHECK-MESSAGES: [[@LINE-3]]:7: warning: class 'DefinesMoveConstructor' defines a move constructor but does not define a destructor, a copy constructor, a copy assignment operator or a move assignment operator [cppcoreguidelines-special-member-functions]
+
+class DefinesMoveAssignment {
+  DefinesMoveAssignment &operator=(DefinesMoveAssignment &&);
+};
+// CHECK-MESSAGES: [[@LINE-3]]:7: warning: class 'DefinesMoveAssignment' defines a move assignment operator but does not define a destructor, a copy constructor, a copy assignment operator or a move constructor [cppcoreguidelines-special-member-functions]
+class DefinesNothing {
+};
+
+class DefinesEverything {
+  DefinesEverything(const DefinesEverything &);
+  DefinesEverything &operator=(const DefinesEverything &);
+  DefinesEverything(DefinesEverything &&);
+  DefinesEverything &operator=(DefinesEverything &&);
+  ~DefinesEverything();
+};
+
+class DeletesEverything {
+  DeletesEverything(const DeletesEverything &) = delete;
+  DeletesEverything &operator=(const DeletesEverything &) = delete;
+  DeletesEverything(DeletesEverything &&) = delete;
+  DeletesEverything &operator=(DeletesEverything &&) = delete;
+  ~DeletesEverything() = delete;
+};
+
+class DeletesCopyDefaultsMove {
+  DeletesCopyDefaultsMove(const DeletesCopyDefaultsMove &) = delete;
+  DeletesCopyDefaultsMove &operator=(const DeletesCopyDefaultsMove &) = delete;
+  DeletesCopyDefaultsMove(DeletesCopyDefaultsMove &&) = default;
+  DeletesCopyDefaultsMove &operator=(DeletesCopyDefaultsMove &&) = default;
+  ~DeletesCopyDefaultsMove() = default;
+};
+
+template <typename T>
+struct TemplateClass {
+  TemplateClass() = default;
+  TemplateClass(const TemplateClass &);
+  TemplateClass &operator=(const TemplateClass &);
+  TemplateClass(TemplateClass &&);
+  TemplateClass &operator=(TemplateClass &&);
+  ~TemplateClass();
+};
+
+// Multiple instantiations of a class template will trigger multiple matches for defined special members.
+// This should not cause problems.
+TemplateClass<int> InstantiationWithInt;
+TemplateClass<double> InstantiationWithDouble;
diff --git a/test/clang-tidy/custom-diagnostics.cpp b/test/clang-tidy/custom-diagnostics.cpp
new file mode 100644 (file)
index 0000000..8183770
--- /dev/null
@@ -0,0 +1,27 @@
+// RUN: clang-tidy -checks='-*,modernize-use-override,clang-diagnostic-shadow,clang-diagnostic-float-conversion' %s -- | count 0
+//
+// Enable warnings using -config:
+// RUN: clang-tidy -checks='-*,modernize-use-override,clang-diagnostic-shadow,clang-diagnostic-float-conversion' \
+// RUN:   -config='{ExtraArgs: ["-Wshadow","-Wno-unused-variable"], ExtraArgsBefore: ["-Wno-shadow","-Wfloat-conversion","-Wunused-variable"]}' %s -- \
+// RUN:   | FileCheck -implicit-check-not='{{warning:|error:}}' %s
+//
+// ... -extra-arg:
+// RUN: clang-tidy -checks='-*,modernize-use-override,clang-diagnostic-shadow,clang-diagnostic-float-conversion' \
+// RUN:   -extra-arg=-Wshadow -extra-arg=-Wno-unused-variable \
+// RUN:   -extra-arg-before=-Wno-shadow -extra-arg-before=-Wfloat-conversion \
+// RUN:   -extra-arg-before=-Wunused-variable %s -- \
+// RUN:   | FileCheck -implicit-check-not='{{warning:|error:}}' %s
+//
+// ... a combination of -config and -extra-arg(-before):
+// RUN: clang-tidy -checks='-*,modernize-use-override,clang-diagnostic-shadow,clang-diagnostic-float-conversion' \
+// RUN:   -config='{ExtraArgs: ["-Wno-unused-variable"], ExtraArgsBefore: ["-Wno-shadow","-Wfloat-conversion"]}' \
+// RUN:   -extra-arg=-Wshadow -extra-arg-before=-Wunused-variable %s -- \
+// RUN:   | FileCheck -implicit-check-not='{{warning:|error:}}' %s
+
+void f(float x) {
+  int a;
+  { int a; }
+  // CHECK: :[[@LINE-1]]:9: warning: declaration shadows a local variable [clang-diagnostic-shadow]
+  int b = x;
+  // CHECK: :[[@LINE-1]]:11: warning: implicit conversion turns floating-point number into integer: 'float' to 'int' [clang-diagnostic-float-conversion]
+}
diff --git a/test/clang-tidy/deduplication.cpp b/test/clang-tidy/deduplication.cpp
new file mode 100644 (file)
index 0000000..056fe4e
--- /dev/null
@@ -0,0 +1,10 @@
+// RUN: %check_clang_tidy %s google-explicit-constructor %t
+
+template<typename T>
+struct A { A(T); };
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: single-argument constructors must be marked explicit
+
+void f() {
+  A<int> a(0);
+  A<double> b(0);
+}
diff --git a/test/clang-tidy/diagnostic.cpp b/test/clang-tidy/diagnostic.cpp
new file mode 100644 (file)
index 0000000..656e05b
--- /dev/null
@@ -0,0 +1,40 @@
+// RUN: clang-tidy -checks='-*,modernize-use-override' %s.nonexistent.cpp -- | FileCheck -check-prefix=CHECK1 -implicit-check-not='{{warning:|error:}}' %s
+// RUN: clang-tidy -checks='-*,clang-diagnostic-*,google-explicit-constructor' %s -- -fan-unknown-option | FileCheck -check-prefix=CHECK2 -implicit-check-not='{{warning:|error:}}' %s
+// RUN: clang-tidy -checks='-*,google-explicit-constructor,clang-diagnostic-literal-conversion' %s -- -fan-unknown-option | FileCheck -check-prefix=CHECK3 -implicit-check-not='{{warning:|error:}}' %s
+// RUN: clang-tidy -checks='-*,modernize-use-override,clang-diagnostic-macro-redefined' %s -- -DMACRO_FROM_COMMAND_LINE | FileCheck -check-prefix=CHECK4 -implicit-check-not='{{warning:|error:}}' %s
+//
+// Now repeat the tests and ensure no other errors appear on stderr:
+// RUN: clang-tidy -checks='-*,modernize-use-override' %s.nonexistent.cpp -- 2>&1 | FileCheck -check-prefix=CHECK1 -implicit-check-not='{{warning:|error:}}' %s
+// RUN: clang-tidy -checks='-*,clang-diagnostic-*,google-explicit-constructor' %s -- -fan-unknown-option 2>&1 | FileCheck -check-prefix=CHECK2 -implicit-check-not='{{warning:|error:}}' %s
+// RUN: clang-tidy -checks='-*,google-explicit-constructor,clang-diagnostic-literal-conversion' %s -- -fan-unknown-option 2>&1 | FileCheck -check-prefix=CHECK3 -implicit-check-not='{{warning:|error:}}' %s
+// RUN: clang-tidy -checks='-*,modernize-use-override,clang-diagnostic-macro-redefined' %s -- -DMACRO_FROM_COMMAND_LINE 2>&1 | FileCheck -check-prefix=CHECK4 -implicit-check-not='{{warning:|error:}}' %s
+//
+// Now create a directory with a compilation database file and ensure we don't
+// use it after failing to parse commands from the command line:
+//
+// RUN: mkdir -p %T/diagnostics/
+// RUN: echo '[{"directory": "%/T/diagnostics/","command": "clang++ -fan-option-from-compilation-database -c %/T/diagnostics/input.cpp", "file": "%/T/diagnostics/input.cpp"}]' > %T/diagnostics/compile_commands.json
+// RUN: cat %s > %T/diagnostics/input.cpp
+// RUN: clang-tidy -checks='-*,modernize-use-override' %T/diagnostics/nonexistent.cpp -- 2>&1 | FileCheck -check-prefix=CHECK1 -implicit-check-not='{{warning:|error:}}' %s
+// RUN: clang-tidy -checks='-*,clang-diagnostic-*,google-explicit-constructor' %T/diagnostics/input.cpp -- -fan-unknown-option 2>&1 | FileCheck -check-prefix=CHECK2 -implicit-check-not='{{warning:|error:}}' %s
+// RUN: clang-tidy -checks='-*,google-explicit-constructor,clang-diagnostic-literal-conversion' %T/diagnostics/input.cpp -- -fan-unknown-option 2>&1 | FileCheck -check-prefix=CHECK3 -implicit-check-not='{{warning:|error:}}' %s
+// RUN: clang-tidy -checks='-*,modernize-use-override,clang-diagnostic-macro-redefined' %T/diagnostics/input.cpp -- -DMACRO_FROM_COMMAND_LINE 2>&1 | FileCheck -check-prefix=CHECK4 -implicit-check-not='{{warning:|error:}}' %s
+// RUN: clang-tidy -checks='-*,clang-diagnostic-*,google-explicit-constructor' %T/diagnostics/input.cpp 2>&1 | FileCheck -check-prefix=CHECK5 -implicit-check-not='{{warning:|error:}}' %s
+
+// CHECK1: error: error reading '{{.*}}nonexistent.cpp' [clang-diagnostic-error]
+// CHECK2: error: unknown argument: '-fan-unknown-option' [clang-diagnostic-error]
+// CHECK3: error: unknown argument: '-fan-unknown-option' [clang-diagnostic-error]
+// CHECK5: error: unknown argument: '-fan-option-from-compilation-database' [clang-diagnostic-error]
+
+// CHECK2: :[[@LINE+3]]:9: warning: implicit conversion from 'double' to 'int' changes value from 1.5 to 1 [clang-diagnostic-literal-conversion]
+// CHECK3: :[[@LINE+2]]:9: warning: implicit conversion from 'double' to 'int' changes value
+// CHECK5: :[[@LINE+1]]:9: warning: implicit conversion from 'double' to 'int' changes value
+int a = 1.5;
+
+// CHECK2: :[[@LINE+3]]:11: warning: single-argument constructors must be marked explicit
+// CHECK3: :[[@LINE+2]]:11: warning: single-argument constructors must be marked explicit
+// CHECK5: :[[@LINE+1]]:11: warning: single-argument constructors must be marked explicit
+class A { A(int) {} };
+
+#define MACRO_FROM_COMMAND_LINE
+// CHECK4: :[[@LINE-1]]:9: warning: 'MACRO_FROM_COMMAND_LINE' macro redefined
diff --git a/test/clang-tidy/explain-checks.cpp b/test/clang-tidy/explain-checks.cpp
new file mode 100644 (file)
index 0000000..9bff30c
--- /dev/null
@@ -0,0 +1,12 @@
+// RUN: clang-tidy -checks=-*,modernize-use-nullptr -explain-config | FileCheck --check-prefix=CHECK-MESSAGE1 %s
+// RUN: clang-tidy -config="{Checks: '-*,modernize-use-nullptr'}" -explain-config | FileCheck --check-prefix=CHECK-MESSAGE2 %s
+// RUN: clang-tidy -checks=modernize-use-nullptr -config="{Checks: '-*,modernize-use-nullptr'}" -explain-config | FileCheck --check-prefix=CHECK-MESSAGE3 %s
+// RUN: clang-tidy -checks=modernize-use-nullptr -config="{Checks: '-*,-modernize-use-nullptr'}" %S/Inputs/explain-config/a.cc -explain-config -- | FileCheck --check-prefix=CHECK-MESSAGE4 %s
+// RUN: clang-tidy -checks=modernize-use-nullptr -config="{Checks: '-*,modernize-*'}" -explain-config | FileCheck --check-prefix=CHECK-MESSAGE5 %s
+// RUN: clang-tidy -explain-config %S/Inputs/explain-config/a.cc -- | grep "'modernize-use-nullptr' is enabled in the .*[/\\]Inputs[/\\]explain-config[/\\].clang-tidy."
+
+// CHECK-MESSAGE1: 'modernize-use-nullptr' is enabled in the command-line option '-checks'.
+// CHECK-MESSAGE2: 'modernize-use-nullptr' is enabled in the command-line option '-config'.
+// CHECK-MESSAGE3: 'modernize-use-nullptr' is enabled in the command-line option '-checks'.
+// CHECK-MESSAGE4: 'modernize-use-nullptr' is enabled in the command-line option '-checks'.
+// CHECK-MESSAGE5: 'modernize-use-nullptr' is enabled in the command-line option '-checks'.
diff --git a/test/clang-tidy/extra-args.cpp b/test/clang-tidy/extra-args.cpp
new file mode 100644 (file)
index 0000000..d65aacc
--- /dev/null
@@ -0,0 +1,7 @@
+// RUN: clang-tidy -checks='-*,modernize-use-override' \
+// RUN:   -config='{ExtraArgs: ["-DTEST4"], ExtraArgsBefore: ["-DTEST1"]}' \
+// RUN:   -extra-arg=-DTEST3 -extra-arg-before=-DTEST2 %s -- -v 2>&1 \
+// RUN:   | FileCheck -implicit-check-not='{{warning:|error:}}' %s
+
+// CHECK: {{^}}clang Invocation:{{$}}
+// CHECK-NEXT: {{"-D" "TEST1" .*"-D" "TEST2" .*"-D" "TEST3" .*"-D" "TEST4"}}
diff --git a/test/clang-tidy/file-filter.cpp b/test/clang-tidy/file-filter.cpp
new file mode 100644 (file)
index 0000000..9ee5cad
--- /dev/null
@@ -0,0 +1,73 @@
+// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='' %s -- -I %S/Inputs/file-filter -isystem %S/Inputs/file-filter/system 2>&1 | FileCheck %s
+// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='' -quiet %s -- -I %S/Inputs/file-filter -isystem %S/Inputs/file-filter/system 2>&1 | FileCheck --check-prefix=CHECK-QUIET %s
+// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='.*' %s -- -I %S/Inputs/file-filter -isystem %S/Inputs/file-filter/system 2>&1 | FileCheck --check-prefix=CHECK2 %s
+// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='.*' -quiet %s -- -I %S/Inputs/file-filter -isystem %S/Inputs/file-filter/system 2>&1 | FileCheck --check-prefix=CHECK2-QUIET %s
+// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='header2\.h' %s -- -I %S/Inputs/file-filter -isystem %S/Inputs/file-filter/system 2>&1 | FileCheck --check-prefix=CHECK3 %s
+// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='header2\.h' -quiet %s -- -I %S/Inputs/file-filter -isystem %S/Inputs/file-filter/system 2>&1 | FileCheck --check-prefix=CHECK3-QUIET %s
+// FIXME: "-I %S/Inputs/file-filter/system/.." must be redundant.
+//       On Win32, file-filter/system\system-header1.h precedes
+//       file-filter\header*.h due to code order between '/' and '\\'.
+// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='.*' -system-headers %s -- -I %S/Inputs/file-filter/system/.. -isystem %S/Inputs/file-filter/system 2>&1 | FileCheck --check-prefix=CHECK4 %s
+// RUN: clang-tidy -checks='-*,google-explicit-constructor' -header-filter='.*' -system-headers -quiet %s -- -I %S/Inputs/file-filter/system/.. -isystem %S/Inputs/file-filter/system 2>&1 | FileCheck --check-prefix=CHECK4-QUIET %s
+
+#include "header1.h"
+// CHECK-NOT: warning:
+// CHECK-QUIET-NOT: warning:
+// CHECK2: header1.h:1:12: warning: single-argument constructors must be marked explicit
+// CHECK2-QUIET: header1.h:1:12: warning: single-argument constructors must be marked explicit
+// CHECK3-NOT: warning:
+// CHECK3-QUIET-NOT: warning:
+// CHECK4: header1.h:1:12: warning: single-argument constructors
+// CHECK4-QUIET: header1.h:1:12: warning: single-argument constructors
+
+#include "header2.h"
+// CHECK-NOT: warning:
+// CHECK-QUIET-NOT: warning:
+// CHECK2: header2.h:1:12: warning: single-argument constructors
+// CHECK2-QUIET: header2.h:1:12: warning: single-argument constructors
+// CHECK3: header2.h:1:12: warning: single-argument constructors
+// CHECK3-QUIET: header2.h:1:12: warning: single-argument constructors
+// CHECK4: header2.h:1:12: warning: single-argument constructors
+// CHECK4-QUIET: header2.h:1:12: warning: single-argument constructors
+
+#include <system-header.h>
+// CHECK-NOT: warning:
+// CHECK-QUIET-NOT: warning:
+// CHECK2-NOT: warning:
+// CHECK2-QUIET-NOT: warning:
+// CHECK3-NOT: warning:
+// CHECK3-QUIET-NOT: warning:
+// CHECK4: system-header.h:1:12: warning: single-argument constructors
+// CHECK4-QUIET: system-header.h:1:12: warning: single-argument constructors
+
+class A { A(int); };
+// CHECK: :[[@LINE-1]]:11: warning: single-argument constructors
+// CHECK-QUIET: :[[@LINE-2]]:11: warning: single-argument constructors
+// CHECK2: :[[@LINE-3]]:11: warning: single-argument constructors
+// CHECK2-QUIET: :[[@LINE-4]]:11: warning: single-argument constructors
+// CHECK3: :[[@LINE-5]]:11: warning: single-argument constructors
+// CHECK3-QUIET: :[[@LINE-6]]:11: warning: single-argument constructors
+// CHECK4: :[[@LINE-7]]:11: warning: single-argument constructors
+// CHECK4-QUIET: :[[@LINE-8]]:11: warning: single-argument constructors
+
+// CHECK-NOT: warning:
+// CHECK-QUIET-NOT: warning:
+// CHECK2-NOT: warning:
+// CHECK2-QUIET-NOT: warning:
+// CHECK3-NOT: warning:
+// CHECK3-QUIET-NOT: warning:
+// CHECK4-NOT: warning:
+// CHECK4-QUIET-NOT: warning:
+
+// CHECK: Suppressed 3 warnings (3 in non-user code)
+// CHECK: Use -header-filter=.* to display errors from all non-system headers.
+// CHECK-QUIET-NOT: Suppressed
+// CHECK2: Suppressed 1 warnings (1 in non-user code)
+// CHECK2: Use -header-filter=.* {{.*}}
+// CHECK2-QUIET-NOT: Suppressed
+// CHECK3: Suppressed 2 warnings (2 in non-user code)
+// CHECK3: Use -header-filter=.* {{.*}}
+// CHECK3-QUIET-NOT: Suppressed
+// CHECK4-NOT: Suppressed {{.*}} warnings
+// CHECK4-NOT: Use -header-filter=.* {{.*}}
+// CHECK4-QUIET-NOT: Suppressed
diff --git a/test/clang-tidy/fix-errors.cpp b/test/clang-tidy/fix-errors.cpp
new file mode 100644 (file)
index 0000000..639873e
--- /dev/null
@@ -0,0 +1,15 @@
+// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
+// RUN: clang-tidy %t.cpp -checks='-*,google-explicit-constructor' -fix -- > %t.msg 2>&1
+// RUN: FileCheck -input-file=%t.cpp -check-prefix=CHECK-FIX %s
+// RUN: FileCheck -input-file=%t.msg -check-prefix=CHECK-MESSAGES %s
+// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
+// RUN: clang-tidy %t.cpp -checks='-*,google-explicit-constructor' -fix-errors -- > %t.msg 2>&1
+// RUN: FileCheck -input-file=%t.cpp -check-prefix=CHECK-FIX2 %s
+// RUN: FileCheck -input-file=%t.msg -check-prefix=CHECK-MESSAGES2 %s
+
+class A { A(int i); }
+// CHECK-FIX: class A { A(int i); }{{$}}
+// CHECK-MESSAGES: Fixes have NOT been applied.
+// CHECK-FIX2: class A { explicit A(int i); };
+// CHECK-MESSAGES2: note: FIX-IT applied suggested code changes
+// CHECK-MESSAGES2: clang-tidy applied 2 of 2 suggested fixes.
diff --git a/test/clang-tidy/fix.cpp b/test/clang-tidy/fix.cpp
new file mode 100644 (file)
index 0000000..db6bc22
--- /dev/null
@@ -0,0 +1,18 @@
+// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
+// RUN: clang-tidy %t.cpp -checks='-*,google-explicit-constructor,llvm-namespace-comment' -fix -export-fixes=%t.yaml -- > %t.msg 2>&1
+// RUN: FileCheck -input-file=%t.cpp %s
+// RUN: FileCheck -input-file=%t.msg -check-prefix=CHECK-MESSAGES %s
+// RUN: FileCheck -input-file=%t.yaml -check-prefix=CHECK-YAML %s
+
+namespace i {
+void f(); // So that the namespace isn't empty.
+}
+// CHECK: } // namespace i
+// CHECK-MESSAGES: note: FIX-IT applied suggested code changes
+// CHECK-YAML: ReplacementText: ' // namespace i'
+
+class A { A(int i); };
+// CHECK: class A { explicit A(int i); };
+// CHECK-MESSAGES: note: FIX-IT applied suggested code changes
+// CHECK-MESSAGES: clang-tidy applied 2 of 2 suggested fixes.
+// CHECK-YAML: ReplacementText: 'explicit '
diff --git a/test/clang-tidy/google-build-explicit-make-pair.cpp b/test/clang-tidy/google-build-explicit-make-pair.cpp
new file mode 100644 (file)
index 0000000..6dcd357
--- /dev/null
@@ -0,0 +1,51 @@
+// RUN: %check_clang_tidy %s google-build-explicit-make-pair %t
+
+namespace std {
+template <class T1, class T2>
+struct pair {
+  pair(T1 x, T2 y) {}
+};
+
+template <class T1, class T2>
+pair<T1, T2> make_pair(T1 x, T2 y) {
+  return pair<T1, T2>(x, y);
+}
+}
+
+template <typename T>
+void templ(T a, T b) {
+  std::make_pair<T, unsigned>(a, b);
+  std::make_pair<int, int>(1, 2);
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: for C++11-compatibility, omit template arguments from make_pair
+// CHECK-FIXES: std::make_pair(1, 2)
+}
+
+template <typename T>
+int t();
+
+void test(int i) {
+  std::make_pair<int, int>(i, i);
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: for C++11-compatibility, omit template arguments from make_pair
+// CHECK-FIXES: std::make_pair(i, i)
+
+  std::make_pair<unsigned, int>(i, i);
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: for C++11-compatibility, use pair directly
+// CHECK-FIXES: std::pair<unsigned, int>(i, i)
+
+  std::make_pair<int, unsigned>(i, i);
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: for C++11-compatibility, use pair directly
+// CHECK-FIXES: std::pair<int, unsigned>(i, i)
+
+#define M std::make_pair<int, unsigned>(i, i);
+M
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: for C++11-compatibility, use pair directly
+// Can't fix in macros.
+// CHECK-FIXES: #define M std::make_pair<int, unsigned>(i, i);
+// CHECK-FIXES-NEXT: M
+
+  templ(i, i);
+  templ(1U, 2U);
+
+  std::make_pair(i, 1); // no-warning
+  std::make_pair(t<int>, 1);
+}
diff --git a/test/clang-tidy/google-default-arguments.cpp b/test/clang-tidy/google-default-arguments.cpp
new file mode 100644 (file)
index 0000000..48d3f2d
--- /dev/null
@@ -0,0 +1,29 @@
+// RUN: %check_clang_tidy %s google-default-arguments %t
+
+struct A {
+  virtual void f(int I, int J = 3);
+  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: default arguments on virtual or override methods are prohibited [google-default-arguments]
+};
+
+struct B : public A {
+  void f(int I, int J = 5);
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: default arguments on virtual or override methods are prohibited
+};
+
+struct C : public B {
+  void f(int I, int J = 5) override;
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: default arguments on virtual or override methods are prohibited
+};
+
+// Negatives.
+struct D : public B {
+  void f(int I, int J) override;
+};
+
+struct X {
+  void f(int I, int J = 3);
+};
+
+struct Y : public X {
+  void f(int I, int J = 5);
+};
diff --git a/test/clang-tidy/google-explicit-constructor.cpp b/test/clang-tidy/google-explicit-constructor.cpp
new file mode 100644 (file)
index 0000000..921237a
--- /dev/null
@@ -0,0 +1,188 @@
+// RUN: %check_clang_tidy %s google-explicit-constructor %t
+
+namespace std {
+  typedef decltype(sizeof(int)) size_t;
+
+  // libc++'s implementation
+  template <class _E>
+  class initializer_list
+  {
+    const _E* __begin_;
+    size_t    __size_;
+
+    initializer_list(const _E* __b, size_t __s)
+      : __begin_(__b),
+        __size_(__s)
+    {}
+
+  public:
+    typedef _E        value_type;
+    typedef const _E& reference;
+    typedef const _E& const_reference;
+    typedef size_t    size_type;
+
+    typedef const _E* iterator;
+    typedef const _E* const_iterator;
+
+    initializer_list() : __begin_(nullptr), __size_(0) {}
+
+    size_t    size()  const {return __size_;}
+    const _E* begin() const {return __begin_;}
+    const _E* end()   const {return __begin_ + __size_;}
+  };
+}
+
+struct A {
+  A() {}
+  A(int x, int y) {}
+
+  explicit A(void *x) {}
+  explicit A(void *x, void *y) {}
+  explicit operator bool() const { return true; }
+
+  operator double() const = delete;
+
+  explicit A(const A& a) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: copy constructor should not be declared explicit [google-explicit-constructor]
+  // CHECK-FIXES: {{^  }}A(const A& a) {}
+
+  A(int x1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: single-argument constructors must be marked explicit to avoid unintentional implicit conversions [google-explicit-constructor]
+  // CHECK-FIXES: {{^  }}explicit A(int x1);
+
+  A(double x2, double y = 3.14) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructors that are callable with a single argument must be marked explicit to avoid unintentional implicit conversions [google-explicit-constructor]
+  // CHECK-FIXES: {{^  }}explicit A(double x2, double y = 3.14) {}
+
+  template <typename... T>
+  A(T&&... args);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructors that are callable with a single argument
+  // CHECK-FIXES: {{^  }}explicit A(T&&... args);
+};
+
+inline A::A(int x1) {}
+
+struct B {
+  B(std::initializer_list<int> list1) {}
+  B(const std::initializer_list<unsigned> &list2) {}
+  B(std::initializer_list<unsigned> &&list3) {}
+
+  operator bool() const { return true; }
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 'operator bool' must be marked explicit to avoid unintentional implicit conversions [google-explicit-constructor]
+  // CHECK-FIXES: {{^  }}explicit operator bool() const { return true; }
+
+  operator double() const;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 'operator double' must be marked explicit to avoid unintentional implicit conversions [google-explicit-constructor]
+  // CHECK-FIXES: {{^  }}explicit operator double() const;
+
+  explicit B(::std::initializer_list<double> list4) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: initializer-list constructor should not be declared explicit [google-explicit-constructor]
+  // CHECK-FIXES: {{^  }}B(::std::initializer_list<double> list4) {}
+
+  explicit B(const ::std::initializer_list<char> &list5) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: initializer-list constructor
+  // CHECK-FIXES: {{^  }}B(const ::std::initializer_list<char> &list5) {}
+
+  explicit B(::std::initializer_list<char> &&list6) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: initializer-list constructor
+  // CHECK-FIXES: {{^  }}B(::std::initializer_list<char> &&list6) {}
+};
+
+inline B::operator double() const { return 0.0; }
+
+struct StructWithFnPointer {
+  void (*f)();
+} struct_with_fn_pointer = {[] {}};
+
+using namespace std;
+
+struct C {
+  C(initializer_list<int> list1) {}
+  C(const initializer_list<unsigned> &list2) {}
+  C(initializer_list<unsigned> &&list3) {}
+};
+
+template <typename T>
+struct C2 {
+  C2(initializer_list<int> list1) {}
+  C2(const initializer_list<unsigned> &list2) {}
+  C2(initializer_list<unsigned> &&list3) {}
+
+  explicit C2(initializer_list<double> list4) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: initializer-list constructor
+  // CHECK-FIXES: {{^  }}C2(initializer_list<double> list4) {}
+};
+
+template <typename T>
+struct C3 {
+  C3(initializer_list<T> list1) {}
+  C3(const std::initializer_list<T*> &list2) {}
+  C3(::std::initializer_list<T**> &&list3) {}
+
+  template <typename U>
+  C3(initializer_list<U> list3) {}
+};
+
+struct D {
+  template <typename T>
+  explicit D(T t) {}
+};
+
+template <typename T>
+struct E {
+  E(T *pt) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: single-argument constructors
+  // CHECK-FIXES: {{^  }}explicit E(T *pt) {}
+  template <typename U>
+  E(U *pu) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: single-argument constructors
+  // CHECK-FIXES: {{^  }}explicit E(U *pu) {}
+
+  explicit E(T t) {}
+  template <typename U>
+  explicit E(U u) {}
+};
+
+void f(std::initializer_list<int> list) {
+  D d(list);
+  E<decltype(list)> e(list);
+  E<int> e2(list);
+}
+
+template <typename T>
+struct F {};
+
+template<typename T>
+struct G {
+  operator bool() const;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 'operator bool' must be marked
+  // CHECK-FIXES: {{^}}  explicit operator bool() const;
+  operator F<T>() const;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 'operator F<type-parameter-0-0>' must be marked
+  // CHECK-FIXES: {{^}}  explicit operator F<T>() const;
+  template<typename U>
+  operator F<U>*() const;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 'operator F<type-parameter-1-0> *' must be marked
+  // CHECK-FIXES: {{^}}  explicit operator F<U>*() const;
+};
+
+void f2() {
+  G<int> a;
+  (void)(F<int>)a;
+  if (a) {}
+  (void)(F<int>*)a;
+  (void)(F<int*>*)a;
+
+  G<double> b;
+  (void)(F<double>)b;
+  if (b) {}
+  (void)(F<double>*)b;
+  (void)(F<double*>*)b;
+}
+
+#define DEFINE_STRUCT_WITH_OPERATOR_BOOL(name) \
+  struct name {                                \
+    operator bool() const;                     \
+  }
+
+DEFINE_STRUCT_WITH_OPERATOR_BOOL(H);
diff --git a/test/clang-tidy/google-module.cpp b/test/clang-tidy/google-module.cpp
new file mode 100644 (file)
index 0000000..494ac48
--- /dev/null
@@ -0,0 +1,10 @@
+// RUN: clang-tidy -checks='-*,google*' -config='{}' -dump-config - -- | FileCheck %s
+// CHECK: CheckOptions:
+// CHECK: {{- key: *google-readability-braces-around-statements.ShortStatementLines}}
+// CHECK-NEXT: {{value: *'1'}}
+// CHECK: {{- key: *google-readability-function-size.StatementThreshold}}
+// CHECK-NEXT: {{value: *'800'}}
+// CHECK: {{- key: *google-readability-namespace-comments.ShortNamespaceLines}}
+// CHECK-NEXT: {{value: *'10'}}
+// CHECK: {{- key: *google-readability-namespace-comments.SpacesBeforeComments}}
+// CHECK-NEXT: {{value: *'2'}}
diff --git a/test/clang-tidy/google-namespaces.cpp b/test/clang-tidy/google-namespaces.cpp
new file mode 100644 (file)
index 0000000..9bda3c0
--- /dev/null
@@ -0,0 +1,52 @@
+// RUN: clang-tidy %s -checks='-*,google-build-namespaces,google-build-using-namespace' -header-filter='.*' -- | FileCheck %s -implicit-check-not="{{warning|error}}:"
+#include "Inputs/google-namespaces.h"
+// CHECK: warning: do not use unnamed namespaces in header files [google-build-namespaces]
+
+using namespace spaaaace;
+// CHECK: :[[@LINE-1]]:1: warning: do not use namespace using-directives; use using-declarations instead [google-build-using-namespace]
+
+using spaaaace::core; // no-warning
+
+namespace std {
+inline namespace literals {
+inline namespace chrono_literals {
+}
+inline namespace complex_literals {
+}
+inline namespace string_literals {
+}
+}
+}
+
+using namespace std::chrono_literals;            // no-warning
+using namespace std::complex_literals;           // no-warning
+using namespace std::literals;                   // no-warning
+using namespace std::literals::chrono_literals;  // no-warning
+using namespace std::literals::complex_literals; // no-warning
+using namespace std::literals::string_literals;  // no-warning
+using namespace std::string_literals;            // no-warning
+
+namespace literals {}
+
+using namespace literals;
+// CHECK: :[[@LINE-1]]:1: warning: do not use namespace using-directives; use using-declarations instead [google-build-using-namespace]
+
+namespace foo {
+inline namespace literals {
+inline namespace bar_literals {}
+}
+}
+
+using namespace foo::literals;
+// CHECK: :[[@LINE-1]]:1: warning: do not use namespace using-directives; use using-declarations instead [google-build-using-namespace]
+
+using namespace foo::bar_literals;
+// CHECK: :[[@LINE-1]]:1: warning: do not use namespace using-directives; use using-declarations instead [google-build-using-namespace]
+
+using namespace foo::literals::bar_literals;
+// CHECK: :[[@LINE-1]]:1: warning: do not use namespace using-directives; use using-declarations instead [google-build-using-namespace]
+
+namespace foo_literals {}
+
+using namespace foo_literals;
+// CHECK: :[[@LINE-1]]:1: warning: do not use namespace using-directives; use using-declarations instead [google-build-using-namespace]
diff --git a/test/clang-tidy/google-overloaded-unary-and.cpp b/test/clang-tidy/google-overloaded-unary-and.cpp
new file mode 100644 (file)
index 0000000..16893d2
--- /dev/null
@@ -0,0 +1,25 @@
+// RUN: %check_clang_tidy %s google-runtime-operator %t
+
+struct Foo {
+  void *operator&();
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not overload unary operator&, it is dangerous. [google-runtime-operator]
+};
+
+template <typename T>
+struct TFoo {
+  T *operator&();
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not overload unary operator&
+};
+
+TFoo<int> tfoo;
+
+struct Bar;
+void *operator&(Bar &b);
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: do not overload unary operator&
+
+// No warnings on binary operators.
+struct Qux {
+  void *operator&(Qux &q);
+};
+
+void *operator&(Qux &q, Qux &r);
diff --git a/test/clang-tidy/google-readability-casting.c b/test/clang-tidy/google-readability-casting.c
new file mode 100644 (file)
index 0000000..488bcd7
--- /dev/null
@@ -0,0 +1,24 @@
+// RUN: %check_clang_tidy %s google-readability-casting %t -- -- -x c
+// The testing script always adds .cpp extension to the input file name, so we
+// need to run clang-tidy directly in order to verify handling of .c files:
+// RUN: clang-tidy --checks=-*,google-readability-casting %s -- -x c++ | FileCheck %s -check-prefix=CHECK-MESSAGES -implicit-check-not='{{warning|error}}:'
+// RUN: cp %s %t.main_file.cpp
+// RUN: clang-tidy --checks=-*,google-readability-casting -header-filter='.*' %t.main_file.cpp -- -I%S -DTEST_INCLUDE -x c++ | FileCheck %s -check-prefix=CHECK-MESSAGES -implicit-check-not='{{warning|error}}:'
+
+#ifdef TEST_INCLUDE
+
+#undef TEST_INCLUDE
+#include "google-readability-casting.c"
+
+#else
+
+void f(const char *cpc) {
+  const char *cpc2 = (const char*)cpc;
+  // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: redundant cast to the same type [google-readability-casting]
+  // CHECK-FIXES: const char *cpc2 = cpc;
+  char *pc = (char*)cpc;
+  typedef const char *Typedef1;
+  (Typedef1)cpc;
+}
+
+#endif
diff --git a/test/clang-tidy/google-readability-casting.cpp b/test/clang-tidy/google-readability-casting.cpp
new file mode 100644 (file)
index 0000000..d36fef6
--- /dev/null
@@ -0,0 +1,323 @@
+// RUN: %check_clang_tidy %s google-readability-casting %t
+
+bool g() { return false; }
+
+enum Enum { Enum1 };
+struct X {};
+struct Y : public X {};
+
+void f(int a, double b, const char *cpc, const void *cpv, X *pX) {
+  const char *cpc2 = (const char*)cpc;
+  // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: redundant cast to the same type [google-readability-casting]
+  // CHECK-FIXES: const char *cpc2 = cpc;
+
+  typedef const char *Typedef1;
+  typedef const char *Typedef2;
+  Typedef1 t1;
+  (Typedef2)t1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: C-style casts are discouraged; use static_cast (if needed, the cast may be redundant) [google-readability-casting]
+  // CHECK-FIXES: {{^}}  static_cast<Typedef2>(t1);
+  (const char*)t1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: {{.*}}; use static_cast (if needed
+  // CHECK-FIXES: {{^}}  static_cast<const char*>(t1);
+  (Typedef1)cpc;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: {{.*}}; use static_cast (if needed
+  // CHECK-FIXES: {{^}}  static_cast<Typedef1>(cpc);
+  (Typedef1)t1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: redundant cast to the same type
+  // CHECK-FIXES: {{^}}  t1;
+
+  char *pc = (char*)cpc;
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: C-style casts are discouraged; use const_cast [google-readability-casting]
+  // CHECK-FIXES: char *pc = const_cast<char*>(cpc);
+  typedef char Char;
+  Char *pChar = (Char*)pc;
+  // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: {{.*}}; use static_cast (if needed
+  // CHECK-FIXES: {{^}}  Char *pChar = static_cast<Char*>(pc);
+
+  (Char)*cpc;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: {{.*}}; use static_cast (if needed
+  // CHECK-FIXES: {{^}}  static_cast<Char>(*cpc);
+
+  (char)*pChar;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: {{.*}}; use static_cast (if needed
+  // CHECK-FIXES: {{^}}  static_cast<char>(*pChar);
+
+  (const char*)cpv;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: {{.*}}; use static_cast [
+  // CHECK-FIXES: static_cast<const char*>(cpv);
+
+  char *pc2 = (char*)(cpc + 33);
+  // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: {{.*}}; use const_cast [
+  // CHECK-FIXES: char *pc2 = const_cast<char*>(cpc + 33);
+
+  const char &crc = *cpc;
+  char &rc = (char&)crc;
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: {{.*}}; use const_cast [
+  // CHECK-FIXES: char &rc = const_cast<char&>(crc);
+
+  char &rc2 = (char&)*cpc;
+  // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: {{.*}}; use const_cast [
+  // CHECK-FIXES: char &rc2 = const_cast<char&>(*cpc);
+
+  char ** const* const* ppcpcpc;
+  char ****ppppc = (char****)ppcpcpc;
+  // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: {{.*}}; use const_cast [
+  // CHECK-FIXES: char ****ppppc = const_cast<char****>(ppcpcpc);
+
+  char ***pppc = (char***)*(ppcpcpc);
+  // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: {{.*}}; use const_cast [
+  // CHECK-FIXES: char ***pppc = const_cast<char***>(*(ppcpcpc));
+
+  char ***pppc2 = (char***)(*ppcpcpc);
+  // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: {{.*}}; use const_cast [
+  // CHECK-FIXES: char ***pppc2 = const_cast<char***>(*ppcpcpc);
+
+  char *pc5 = (char*)(const char*)(cpv);
+  // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: {{.*}}; use const_cast [
+  // CHECK-MESSAGES: :[[@LINE-2]]:22: warning: {{.*}}; use static_cast [
+  // CHECK-FIXES: char *pc5 = const_cast<char*>(static_cast<const char*>(cpv));
+
+  int b1 = (int)b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: {{.*}}; use static_cast [
+  // CHECK-FIXES: int b1 = static_cast<int>(b);
+  b1 = (const int&)b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: {{.*}}; use static_cast/const_cast/reinterpret_cast [
+  // CHECK-FIXES: b1 = (const int&)b;
+
+  b1 = (int) b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: {{.*}}; use static_cast {{.*}}
+  // CHECK-FIXES: b1 = static_cast<int>(b);
+
+  b1 = (int)         b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: {{.*}}; use static_cast {{.*}}
+  // CHECK-FIXES: b1 = static_cast<int>(b);
+
+  b1 = (int) (b);
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: {{.*}}; use static_cast {{.*}}
+  // CHECK-FIXES: b1 = static_cast<int>(b);
+
+  b1 = (int)         (b);
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: {{.*}}; use static_cast {{.*}}
+  // CHECK-FIXES: b1 = static_cast<int>(b);
+
+  Y *pB = (Y*)pX;
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: {{.*}}; use static_cast/const_cast/reinterpret_cast [
+  Y &rB = (Y&)*pX;
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: {{.*}}; use static_cast/const_cast/reinterpret_cast [
+
+  const char *pc3 = (const char*)cpv;
+  // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: {{.*}}; use static_cast [
+  // CHECK-FIXES: const char *pc3 = static_cast<const char*>(cpv);
+
+  char *pc4 = (char*)cpv;
+  // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: {{.*}}; use static_cast/const_cast/reinterpret_cast [
+  // CHECK-FIXES: char *pc4 = (char*)cpv;
+
+  b1 = (int)Enum1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: {{.*}}; use static_cast [
+  // CHECK-FIXES: b1 = static_cast<int>(Enum1);
+
+  Enum e = (Enum)b1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: {{.*}}; use static_cast [
+  // CHECK-FIXES: Enum e = static_cast<Enum>(b1);
+
+  e = (Enum)Enum1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: redundant cast to the same type
+  // CHECK-FIXES: {{^}}  e = Enum1;
+
+  e = (Enum)e;
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: redundant cast to the same type
+  // CHECK-FIXES: {{^}}  e = e;
+
+  e = (Enum)           e;
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: redundant cast to the same type
+  // CHECK-FIXES: {{^}}  e = e;
+
+  e = (Enum)           (e);
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: redundant cast to the same type
+  // CHECK-FIXES: {{^}}  e = (e);
+
+  static const int kZero = 0;
+  (int)kZero;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: redundant cast to the same type
+  // CHECK-FIXES: {{^}}  kZero;
+
+  int b2 = int(b);
+  int b3 = static_cast<double>(b);
+  int b4 = b;
+  double aa = a;
+  (void)b2;
+  return (void)g();
+}
+
+template <typename T>
+void template_function(T t, int n) {
+  int i = (int)t;
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: {{.*}}; use static_cast/const_cast/reinterpret_cast [
+  // CHECK-FIXES: int i = (int)t;
+  int j = (int)n;
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: redundant cast to the same type
+  // CHECK-FIXES: int j = n;
+}
+
+template <typename T>
+struct TemplateStruct {
+  void f(T t, int n) {
+    int k = (int)t;
+    // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: {{.*}}; use static_cast/const_cast/reinterpret_cast
+    // CHECK-FIXES: int k = (int)t;
+    int l = (int)n;
+    // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: redundant cast to the same type
+    // CHECK-FIXES: int l = n;
+  }
+};
+
+void test_templates() {
+  template_function(1, 42);
+  template_function(1.0, 42);
+  TemplateStruct<int>().f(1, 42);
+  TemplateStruct<double>().f(1.0, 42);
+}
+
+extern "C" {
+void extern_c_code(const char *cpc) {
+  const char *cpc2 = (const char*)cpc;
+  // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: redundant cast to the same type
+  // CHECK-FIXES: const char *cpc2 = cpc;
+  char *pc = (char*)cpc;
+}
+}
+
+#define CAST(type, value) (type)(value)
+void macros(double d) {
+  int i = CAST(int, d);
+}
+
+enum E { E1 = 1 };
+template <E e>
+struct A {
+  // Usage of template argument e = E1 is represented as (E)1 in the AST for
+  // some reason. We have a special treatment of this case to avoid warnings
+  // here.
+  static const E ee = e;
+};
+struct B : public A<E1> {};
+
+
+void overloaded_function();
+void overloaded_function(int);
+
+template<typename Fn>
+void g(Fn fn) {
+  fn();
+}
+
+void function_casts() {
+  typedef void (*FnPtrVoid)();
+  typedef void (&FnRefVoid)();
+  typedef void (&FnRefInt)(int);
+
+  g((void (*)())overloaded_function);
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: C-style casts are discouraged; use static_cast [
+  // CHECK-FIXES: g(static_cast<void (*)()>(overloaded_function));
+  g((void (*)())&overloaded_function);
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: C-style casts are discouraged; use static_cast [
+  // CHECK-FIXES: g(static_cast<void (*)()>(&overloaded_function));
+  g((void (&)())overloaded_function);
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: C-style casts are discouraged; use static_cast [
+  // CHECK-FIXES: g(static_cast<void (&)()>(overloaded_function));
+
+  g((FnPtrVoid)overloaded_function);
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: C-style casts are discouraged; use static_cast [
+  // CHECK-FIXES: g(static_cast<FnPtrVoid>(overloaded_function));
+  g((FnPtrVoid)&overloaded_function);
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: C-style casts are discouraged; use static_cast [
+  // CHECK-FIXES: g(static_cast<FnPtrVoid>(&overloaded_function));
+  g((FnRefVoid)overloaded_function);
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: C-style casts are discouraged; use static_cast [
+  // CHECK-FIXES: g(static_cast<FnRefVoid>(overloaded_function));
+
+  FnPtrVoid fn0 = (void (*)())&overloaded_function;
+  // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: C-style casts are discouraged; use static_cast [
+  // CHECK-FIXES: FnPtrVoid fn0 = static_cast<void (*)()>(&overloaded_function);
+  FnPtrVoid fn1 = (void (*)())overloaded_function;
+  // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: C-style casts are discouraged; use static_cast [
+  // CHECK-FIXES: FnPtrVoid fn1 = static_cast<void (*)()>(overloaded_function);
+  FnPtrVoid fn1a = (FnPtrVoid)overloaded_function;
+  // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: C-style casts are discouraged; use static_cast [
+  // CHECK-FIXES: FnPtrVoid fn1a = static_cast<FnPtrVoid>(overloaded_function);
+  FnRefInt fn2 = (void (&)(int))overloaded_function;
+  // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: C-style casts are discouraged; use static_cast [
+  // CHECK-FIXES: FnRefInt fn2 = static_cast<void (&)(int)>(overloaded_function);
+  auto fn3 = (void (*)())&overloaded_function;
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: C-style casts are discouraged; use static_cast [
+  // CHECK-FIXES: auto fn3 = static_cast<void (*)()>(&overloaded_function);
+  auto fn4 = (void (*)())overloaded_function;
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: C-style casts are discouraged; use static_cast [
+  // CHECK-FIXES: auto fn4 = static_cast<void (*)()>(overloaded_function);
+  auto fn5 = (void (&)(int))overloaded_function;
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: C-style casts are discouraged; use static_cast [
+  // CHECK-FIXES: auto fn5 = static_cast<void (&)(int)>(overloaded_function);
+
+  void (*fn6)() = (void (*)())&overloaded_function;
+  // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: C-style casts are discouraged; use static_cast [
+  // CHECK-FIXES: void (*fn6)() = static_cast<void (*)()>(&overloaded_function);
+  void (*fn7)() = (void (*)())overloaded_function;
+  // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: C-style casts are discouraged; use static_cast [
+  // CHECK-FIXES: void (*fn7)() = static_cast<void (*)()>(overloaded_function);
+  void (*fn8)() = (FnPtrVoid)overloaded_function;
+  // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: C-style casts are discouraged; use static_cast [
+  // CHECK-FIXES: void (*fn8)() = static_cast<FnPtrVoid>(overloaded_function);
+  void (&fn9)(int) = (void (&)(int))overloaded_function;
+  // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: C-style casts are discouraged; use static_cast [
+  // CHECK-FIXES: void (&fn9)(int) = static_cast<void (&)(int)>(overloaded_function);
+
+  void (*correct1)() = static_cast<void (*)()>(overloaded_function);
+  FnPtrVoid correct2 = static_cast<void (*)()>(&overloaded_function);
+  FnRefInt correct3 = static_cast<void (&)(int)>(overloaded_function);
+}
+
+struct S {
+    S(const char *);
+};
+struct ConvertibleToS {
+  operator S() const;
+};
+struct ConvertibleToSRef {
+  operator const S&() const;
+};
+
+void conversions() {
+  //auto s1 = (const S&)"";
+  // C HECK-MESSAGES: :[[@LINE-1]]:10: warning: C-style casts are discouraged; use static_cast [
+  // C HECK-FIXES: S s1 = static_cast<const S&>("");
+  auto s2 = (S)"";
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: C-style casts are discouraged; use constructor call syntax [
+  // CHECK-FIXES: auto s2 = S("");
+  auto s2a = (struct S)"";
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: C-style casts are discouraged; use static_cast [
+  // CHECK-FIXES: auto s2a = static_cast<struct S>("");
+  auto s2b = (const S)"";
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: C-style casts are discouraged; use static_cast [
+  // FIXME: This should be constructor call syntax: S("").
+  // CHECK-FIXES: auto s2b = static_cast<const S>("");
+  ConvertibleToS c;
+  auto s3 = (const S&)c;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: C-style casts are discouraged; use static_cast/const_cast/reinterpret_cast [
+  // CHECK-FIXES: auto s3 = (const S&)c;
+  // FIXME: This should be a static_cast.
+  // C HECK-FIXES: auto s3 = static_cast<const S&>(c);
+  auto s4 = (S)c;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: C-style casts are discouraged; use constructor call syntax [
+  // CHECK-FIXES: auto s4 = S(c);
+  ConvertibleToSRef cr;
+  auto s5 = (const S&)cr;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: C-style casts are discouraged; use static_cast/const_cast/reinterpret_cast [
+  // CHECK-FIXES: auto s5 = (const S&)cr;
+  // FIXME: This should be a static_cast.
+  // C HECK-FIXES: auto s5 = static_cast<const S&>(cr);
+  auto s6 = (S)cr;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: C-style casts are discouraged; use constructor call syntax [
+  // CHECK-FIXES: auto s6 = S(cr);
+}
diff --git a/test/clang-tidy/google-readability-namespace-comments.cpp b/test/clang-tidy/google-readability-namespace-comments.cpp
new file mode 100644 (file)
index 0000000..a9a6fdd
--- /dev/null
@@ -0,0 +1,40 @@
+// RUN: %check_clang_tidy %s google-readability-namespace-comments %t
+
+namespace n1 {
+namespace n2 {
+
+
+void f(); // So that the namespace isn't empty.
+
+
+// CHECK-MESSAGES: :[[@LINE+4]]:2: warning: namespace 'n2' not terminated with a closing comment [google-readability-namespace-comments]
+// CHECK-MESSAGES: :[[@LINE-7]]:11: note: namespace 'n2' starts here
+// CHECK-MESSAGES: :[[@LINE+2]]:3: warning: namespace 'n1' not terminated with
+// CHECK-MESSAGES: :[[@LINE-10]]:11: note: namespace 'n1' starts here
+}}
+// CHECK-FIXES: }  // namespace n2
+// CHECK-FIXES: }  // namespace n1
+
+
+namespace short1 {
+namespace short2 {
+// Namespaces covering 10 lines or fewer are exempt from this rule.
+
+
+
+
+
+}
+}
+
+namespace n3 {
+
+
+
+
+
+
+
+
+
+}; // namespace n3
diff --git a/test/clang-tidy/google-readability-todo.cpp b/test/clang-tidy/google-readability-todo.cpp
new file mode 100644 (file)
index 0000000..6b900aa
--- /dev/null
@@ -0,0 +1,26 @@
+// RUN: %check_clang_tidy %s google-readability-todo %t -- -config="{User: 'some user'}" --
+
+//   TODOfix this1
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: missing username/bug in TODO
+// CHECK-FIXES: // TODO(some user): fix this1
+
+//   TODO fix this2
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: missing username/bug in TODO
+// CHECK-FIXES: // TODO(some user): fix this2
+
+// TODO fix this3
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: missing username/bug in TODO
+// CHECK-FIXES: // TODO(some user): fix this3
+
+// TODO: fix this4
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: missing username/bug in TODO
+// CHECK-FIXES: // TODO(some user): fix this4
+
+//   TODO(clang)fix this5
+
+// TODO(foo):shave yaks
+// TODO(bar):
+// TODO(foo): paint bikeshed
+// TODO(b/12345): find the holy grail
+// TODO (b/12345): allow spaces before parentheses
+// TODO(asdf) allow missing semicolon
diff --git a/test/clang-tidy/google-runtime-int-std.cpp b/test/clang-tidy/google-runtime-int-std.cpp
new file mode 100644 (file)
index 0000000..c5d3112
--- /dev/null
@@ -0,0 +1,57 @@
+// RUN: %check_clang_tidy %s google-runtime-int %t -- \
+// RUN:   -config='{CheckOptions: [ \
+// RUN:     {key: google-runtime-int.UnsignedTypePrefix, value: "std::uint"}, \
+// RUN:     {key: google-runtime-int.SignedTypePrefix, value: "std::int"}, \
+// RUN:     {key: google-runtime-int.TypeSuffix, value: "_t"}, \
+// RUN:   ]}' -- -std=c++11
+
+long a();
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: consider replacing 'long' with 'std::int{{..}}_t'
+
+typedef unsigned long long uint64; // NOLINT
+
+long b(long = 1);
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: consider replacing 'long' with 'std::int{{..}}_t'
+// CHECK-MESSAGES: [[@LINE-2]]:8: warning: consider replacing 'long' with 'std::int{{..}}_t'
+
+template <typename T>
+void tmpl() {
+  T i;
+}
+
+short bar(const short, unsigned short) {
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: consider replacing 'short' with 'std::int16_t'
+// CHECK-MESSAGES: [[@LINE-2]]:17: warning: consider replacing 'short' with 'std::int16_t'
+// CHECK-MESSAGES: [[@LINE-3]]:24: warning: consider replacing 'unsigned short' with 'std::uint16_t'
+  long double foo = 42;
+  uint64 qux = 42;
+  unsigned short port;
+
+  const unsigned short bar = 0;
+// CHECK-MESSAGES: [[@LINE-1]]:9: warning: consider replacing 'unsigned short' with 'std::uint16_t'
+  long long *baar;
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: consider replacing 'long long' with 'std::int64_t'
+  const unsigned short &bara = bar;
+// CHECK-MESSAGES: [[@LINE-1]]:9: warning: consider replacing 'unsigned short' with 'std::uint16_t'
+  long const long moo = 1;
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: consider replacing 'long long' with 'std::int64_t'
+  long volatile long wat = 42;
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: consider replacing 'long long' with 'std::int64_t'
+  unsigned long y;
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: consider replacing 'unsigned long' with 'std::uint{{..}}_t'
+  unsigned long long **const *tmp;
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: consider replacing 'unsigned long long' with 'std::uint64_t'
+  unsigned long long **const *&z = tmp;
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: consider replacing 'unsigned long long' with 'std::uint64_t'
+  unsigned short porthole;
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: consider replacing 'unsigned short' with 'std::uint16_t'
+
+  uint64 cast = (short)42;
+// CHECK-MESSAGES: [[@LINE-1]]:18: warning: consider replacing 'short' with 'std::int16_t'
+
+#define l long
+  l x;
+
+  tmpl<short>();
+// CHECK-MESSAGES: [[@LINE-1]]:8: warning: consider replacing 'short' with 'std::int16_t'
+}
diff --git a/test/clang-tidy/google-runtime-int.c b/test/clang-tidy/google-runtime-int.c
new file mode 100644 (file)
index 0000000..8657e2d
--- /dev/null
@@ -0,0 +1,27 @@
+// RUN: clang-tidy -checks=-*,google-runtime-int %s -- -x c 2>&1 | not grep 'warning:\|error:'
+
+long a();
+
+long b(long x);
+
+short bar(const short q, unsigned short w) {
+  long double foo;
+  unsigned short port;
+
+  const unsigned short bar;
+  long long *baar;
+  const unsigned short bara;
+  long const long moo;
+  long volatile long wat;
+  unsigned long y;
+  unsigned long long **const *tmp;
+  unsigned short porthole;
+
+  unsigned cast;
+  cast = (short)42;
+  return q;
+}
+
+void qux() {
+  short port;
+}
diff --git a/test/clang-tidy/google-runtime-int.cpp b/test/clang-tidy/google-runtime-int.cpp
new file mode 100644 (file)
index 0000000..e51ca7d
--- /dev/null
@@ -0,0 +1,74 @@
+// RUN: %check_clang_tidy %s google-runtime-int %t
+
+long a();
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: consider replacing 'long' with 'int{{..}}'
+
+typedef unsigned long long uint64; // NOLINT
+
+long b(long = 1);
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: consider replacing 'long' with 'int{{..}}'
+// CHECK-MESSAGES: [[@LINE-2]]:8: warning: consider replacing 'long' with 'int{{..}}'
+
+template <typename T>
+void tmpl() {
+  T i;
+}
+
+short bar(const short, unsigned short) {
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: consider replacing 'short' with 'int16'
+// CHECK-MESSAGES: [[@LINE-2]]:17: warning: consider replacing 'short' with 'int16'
+// CHECK-MESSAGES: [[@LINE-3]]:24: warning: consider replacing 'unsigned short' with 'uint16'
+  long double foo = 42;
+  uint64 qux = 42;
+  unsigned short port;
+
+  const unsigned short bar = 0;
+// CHECK-MESSAGES: [[@LINE-1]]:9: warning: consider replacing 'unsigned short' with 'uint16'
+  long long *baar;
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: consider replacing 'long long' with 'int64'
+  const unsigned short &bara = bar;
+// CHECK-MESSAGES: [[@LINE-1]]:9: warning: consider replacing 'unsigned short' with 'uint16'
+  long const long moo = 1;
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: consider replacing 'long long' with 'int64'
+  long volatile long wat = 42;
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: consider replacing 'long long' with 'int64'
+  unsigned long y;
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: consider replacing 'unsigned long' with 'uint{{..}}'
+  unsigned long long **const *tmp;
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: consider replacing 'unsigned long long' with 'uint64'
+  unsigned long long **const *&z = tmp;
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: consider replacing 'unsigned long long' with 'uint64'
+  unsigned short porthole;
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: consider replacing 'unsigned short' with 'uint16'
+
+  uint64 cast = (short)42;
+// CHECK-MESSAGES: [[@LINE-1]]:18: warning: consider replacing 'short' with 'int16'
+
+#define l long
+  l x;
+
+  tmpl<short>();
+// CHECK-MESSAGES: [[@LINE-1]]:8: warning: consider replacing 'short' with 'int16'
+  return 0;
+}
+
+void p(unsigned short port);
+
+void qux() {
+  short port;
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: consider replacing 'short' with 'int16'
+}
+
+// FIXME: This shouldn't warn, as UD-literal operators require one of a handful
+// of types as an argument.
+struct some_value {};
+constexpr some_value operator"" _some_literal(unsigned long long int i);
+// CHECK-MESSAGES: [[@LINE-1]]:47: warning: consider replacing 'unsigned long long'
+
+struct A { A& operator=(const A&); };
+class B { A a[0]; };
+
+void fff() {
+  B a, b;
+  a = b;
+}
diff --git a/test/clang-tidy/google-runtime-member-string-references.cpp b/test/clang-tidy/google-runtime-member-string-references.cpp
new file mode 100644 (file)
index 0000000..64553c2
--- /dev/null
@@ -0,0 +1,49 @@
+// RUN: %check_clang_tidy %s google-runtime-member-string-references %t
+
+namespace std {
+template<typename T>
+  class basic_string {};
+
+typedef basic_string<char> string;
+}
+
+class string {};
+
+
+struct A {
+  const std::string &s;
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: const string& members are dangerous; it is much better to use alternatives, such as pointers or simple constants [google-runtime-member-string-references]
+};
+
+struct B {
+  std::string &s;
+};
+
+struct C {
+  const std::string s;
+};
+
+template <typename T>
+struct D {
+  D();
+  const T &s;
+  const std::string &s2;
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: const string& members are dangerous
+};
+
+D<std::string> d;
+
+struct AA {
+  const string &s;
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: const string& members are dangerous
+};
+
+struct BB {
+  string &s;
+};
+
+struct CC {
+  const string s;
+};
+
+D<string> dd;
diff --git a/test/clang-tidy/google-runtime-references.cpp b/test/clang-tidy/google-runtime-references.cpp
new file mode 100644 (file)
index 0000000..b9f84b6
--- /dev/null
@@ -0,0 +1,152 @@
+// RUN: %check_clang_tidy %s google-runtime-references %t -- \
+// RUN:   -extra-arg="-std=c++11" \
+// RUN:   -config="{CheckOptions: \
+// RUN:             [{key: google-runtime-references.WhiteListTypes, \
+// RUN:               value: 'whitelist::A; whitelist::B'}]}" --
+
+int a;
+int &b = a;
+int *c;
+void f1(int a);
+void f2(int *b);
+void f3(const int &c);
+void f4(int const &d);
+
+// Don't warn on implicit operator= in c++11 mode.
+class A {
+  virtual void f() {}
+};
+// Don't warn on rvalue-references.
+struct A2 {
+  A2(A2&&) = default;
+  void f(A2&&) {}
+};
+
+// Don't warn on iostream parameters.
+namespace xxx {
+class istream { };
+class ostringstream { };
+}
+void g1(xxx::istream &istr);
+void g1(xxx::ostringstream &istr);
+
+void g1(int &a);
+// CHECK-MESSAGES: [[@LINE-1]]:14: warning: non-const reference parameter 'a', make it const or use a pointer [google-runtime-references]
+
+struct s {};
+void g2(int a, int b, s c, s &d);
+// CHECK-MESSAGES: [[@LINE-1]]:31: warning: non-const reference parameter 'd', {{.*}}
+
+typedef int &ref;
+void g3(ref a);
+// CHECK-MESSAGES: [[@LINE-1]]:13: warning: non-const reference {{.*}}
+
+void g4(int &a, int &b, int &);
+// CHECK-MESSAGES: [[@LINE-1]]:14: warning: non-const reference parameter 'a', {{.*}}
+// CHECK-MESSAGES: [[@LINE-2]]:22: warning: non-const reference parameter 'b', {{.*}}
+// CHECK-MESSAGES: [[@LINE-3]]:30: warning: non-const reference parameter at index 2, {{.*}}
+
+class B {
+  B(B& a) {}
+// CHECK-MESSAGES: [[@LINE-1]]:8: warning: non-const reference {{.*}}
+  virtual void f(int &a) {}
+// CHECK-MESSAGES: [[@LINE-1]]:23: warning: non-const reference {{.*}}
+  void g(int &b);
+// CHECK-MESSAGES: [[@LINE-1]]:15: warning: non-const reference {{.*}}
+
+  // Don't warn on the parameter of stream extractors defined as members.
+  B& operator>>(int& val) { return *this; }
+};
+
+// Only warn on the first declaration of each function to reduce duplicate
+// warnings.
+void B::g(int &b) {}
+
+// Don't warn on the first parameter of stream inserters.
+A& operator<<(A& s, int&) { return s; }
+// CHECK-MESSAGES: [[@LINE-1]]:25: warning: non-const reference parameter at index 1, {{.*}}
+
+// Don't warn on either parameter of stream extractors. Both need to be
+// non-const references by convention.
+A& operator>>(A& input, int& val) { return input; }
+
+// Don't warn on lambdas.
+auto lambda = [] (int&) {};
+
+// Don't warn on typedefs, as we'll warn on the function itself.
+typedef int (*fp)(int &);
+
+// Don't warn on function references.
+typedef void F();
+void g5(const F& func) {}
+void g6(F& func) {}
+
+template<typename T>
+void g7(const T& t) {}
+
+template<typename T>
+void g8(T t) {}
+
+void f5() {
+  g5(f5);
+  g6(f5);
+  g7(f5);
+  g7<F&>(f5);
+  g8(f5);
+  g8<F&>(f5);
+}
+
+// Don't warn on dependent types.
+template<typename T>
+void g9(T& t) {}
+template<typename T>
+void g10(T t) {}
+
+void f6() {
+  int i;
+  float f;
+  g9<int>(i);
+  g9<const int>(i);
+  g9<int&>(i);
+  g10<int&>(i);
+  g10<float&>(f);
+}
+
+// Warn only on the overridden methods from the base class, as the child class
+// only implements the interface.
+class C : public B {
+  C();
+  virtual void f(int &a) {}
+};
+
+// Don't warn on operator<< with streams-like interface.
+A& operator<<(A& s, int) { return s; }
+
+// Don't warn on swap().
+void swap(C& c1, C& c2) {}
+
+// Don't warn on standalone operator++, operator--, operator+=, operator-=,
+// operator*=, etc. that all need non-const references to be functional.
+A& operator++(A& a) { return a; }
+A operator++(A& a, int) { return a; }
+A& operator--(A& a) { return a; }
+A operator--(A& a, int) { return a; }
+A& operator+=(A& a, const A& b) { return a; }
+A& operator-=(A& a, const A& b) { return a; }
+A& operator*=(A& a, const A& b) { return a; }
+A& operator/=(A& a, const A& b) { return a; }
+A& operator%=(A& a, const A& b) { return a; }
+A& operator<<=(A& a, const A& b) { return a; }
+A& operator>>=(A& a, const A& b) { return a; }
+A& operator|=(A& a, const A& b) { return a; }
+A& operator^=(A& a, const A& b) { return a; }
+A& operator&=(A& a, const A& b) { return a; }
+
+namespace whitelist {
+class A {};
+class B {};
+void f7(A &);
+void f8(B &);
+}
+void f9(whitelist::A &);
+void f10(whitelist::B &);
diff --git a/test/clang-tidy/hicpp-no-assembler.cpp b/test/clang-tidy/hicpp-no-assembler.cpp
new file mode 100644 (file)
index 0000000..d08ea74
--- /dev/null
@@ -0,0 +1,12 @@
+// RUN: %check_clang_tidy %s hicpp-no-assembler %t
+
+__asm__(".symver foo, bar@v");
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: do not use inline assembler in safety-critical code [hicpp-no-assembler]
+
+static int s asm("spam");
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: do not use inline assembler in safety-critical code [hicpp-no-assembler]
+
+void f() {
+  __asm("mov al, 2");
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not use inline assembler in safety-critical code [hicpp-no-assembler]
+}
diff --git a/test/clang-tidy/line-filter.cpp b/test/clang-tidy/line-filter.cpp
new file mode 100644 (file)
index 0000000..ad980cf
--- /dev/null
@@ -0,0 +1,27 @@
+// RUN: clang-tidy -checks='-*,google-explicit-constructor' -line-filter='[{"name":"line-filter.cpp","lines":[[18,18],[22,22]]},{"name":"header1.h","lines":[[1,2]]},{"name":"header2.h"},{"name":"header3.h"}]' -header-filter='header[12]\.h$' %s -- -I %S/Inputs/line-filter 2>&1 | FileCheck %s
+
+#include "header1.h"
+// CHECK-NOT: header1.h:{{.*}} warning
+// CHECK: header1.h:1:12: warning: single-argument constructors must be marked explicit
+// CHECK: header1.h:2:12: warning: single-argument constructors {{.*}}
+// CHECK-NOT: header1.h:{{.*}} warning
+
+#include "header2.h"
+// CHECK: header2.h:1:12: warning: single-argument constructors {{.*}}
+// CHECK: header2.h:2:12: warning: single-argument constructors {{.*}}
+// CHECK: header2.h:3:12: warning: single-argument constructors {{.*}}
+// CHECK-NOT: header2.h:{{.*}} warning
+
+#include "header3.h"
+// CHECK-NOT: header3.h:{{.*}} warning
+
+class A { A(int); };
+// CHECK: :[[@LINE-1]]:11: warning: single-argument constructors {{.*}}
+class B { B(int); };
+// CHECK-NOT: :[[@LINE-1]]:{{.*}} warning
+class C { C(int); };
+// CHECK: :[[@LINE-1]]:11: warning: single-argument constructors {{.*}}
+
+// CHECK-NOT: warning:
+
+// CHECK: Suppressed 3 warnings (1 in non-user code, 2 due to line filter)
diff --git a/test/clang-tidy/list-checks.cpp b/test/clang-tidy/list-checks.cpp
new file mode 100644 (file)
index 0000000..674c118
--- /dev/null
@@ -0,0 +1,4 @@
+// RUN: mkdir -p %T/clang-tidy/list-checks/
+// RUN: echo '{Checks: "-*,google-*"}' > %T/clang-tidy/.clang-tidy
+// RUN: cd %T/clang-tidy/list-checks
+// RUN: clang-tidy -list-checks | grep "^ *google-"
diff --git a/test/clang-tidy/llvm-include-order.cpp b/test/clang-tidy/llvm-include-order.cpp
new file mode 100644 (file)
index 0000000..7272353
--- /dev/null
@@ -0,0 +1,42 @@
+// RUN: %check_clang_tidy %s llvm-include-order %t -- -- -isystem %S/Inputs/Headers
+
+// CHECK-MESSAGES: [[@LINE+2]]:1: warning: #includes are not sorted properly
+#include "j.h"
+#include "gtest/foo.h"
+#include "i.h"
+#include <s.h>
+#include "llvm/a.h"
+#include "clang/b.h"
+#include "clang-c/c.h" // hi
+#include "llvm-c/d.h" // -c
+
+// CHECK-FIXES: #include "j.h"
+// CHECK-FIXES-NEXT: #include "i.h"
+// CHECK-FIXES-NEXT: #include "clang-c/c.h" // hi
+// CHECK-FIXES-NEXT: #include "clang/b.h"
+// CHECK-FIXES-NEXT: #include "llvm-c/d.h" // -c
+// CHECK-FIXES-NEXT: #include "llvm/a.h"
+// CHECK-FIXES-NEXT: #include "gtest/foo.h"
+// CHECK-FIXES-NEXT: #include <s.h>
+
+#include "b.h"
+#ifdef FOO
+#include "a.h"
+#endif
+
+// CHECK-FIXES: #include "b.h"
+// CHECK-FIXES-NEXT: #ifdef FOO
+// CHECK-FIXES-NEXT: #include "a.h"
+// CHECK-FIXES-NEXT: #endif
+
+// CHECK-MESSAGES: [[@LINE+1]]:1: warning: #includes are not sorted properly
+#include "b.h"
+#include "a.h"
+
+// CHECK-FIXES: #include "a.h"
+// CHECK-FIXES-NEXT: #include "b.h"
+
+// CHECK-MESSAGES-NOT: [[@LINE+1]]:1: warning: #includes are not sorted properly
+#include "cross-file-c.h"
+// This line number should correspond to the position of the #include in cross-file-c.h
+#include "cross-file-a.h"
diff --git a/test/clang-tidy/llvm-twine-local.cpp b/test/clang-tidy/llvm-twine-local.cpp
new file mode 100644 (file)
index 0000000..06eb761
--- /dev/null
@@ -0,0 +1,64 @@
+// RUN: %check_clang_tidy %s llvm-twine-local %t
+
+namespace llvm {
+class Twine {
+public:
+  Twine(const char *);
+  Twine(int);
+  Twine();
+  Twine &operator+(const Twine &);
+};
+}
+
+using namespace llvm;
+
+void foo(const Twine &x);
+
+static Twine Moo = Twine("bark") + "bah";
+// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: twine variables are prone to use-after-free bugs
+// CHECK-MESSAGES: note: FIX-IT applied suggested code changes
+// CHECK-FIXES: static std::string Moo = (Twine("bark") + "bah").str();
+
+int main() {
+  const Twine t = Twine("a") + "b" + Twine(42);
+// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: twine variables are prone to use-after-free bugs
+// CHECK-MESSAGES: note: FIX-IT applied suggested code changes
+// CHECK-FIXES: std::string t = (Twine("a") + "b" + Twine(42)).str();
+  foo(Twine("a") + "b");
+
+  Twine Prefix = false ? "__INT_FAST" : "__UINT_FAST";
+// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: twine variables are prone to use-after-free bugs
+// CHECK-MESSAGES: note: FIX-IT applied suggested code changes
+// CHECK-FIXES: const char * Prefix = false ? "__INT_FAST" : "__UINT_FAST";
+
+  const Twine t2 = Twine();
+// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: twine variables are prone to use-after-free bugs
+// CHECK-MESSAGES: note: FIX-IT applied suggested code changes
+// CHECK-FIXES: std::string t2 = (Twine()).str();
+  foo(Twine() + "b");
+
+  const Twine t3 = Twine(42);
+// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: twine variables are prone to use-after-free bugs
+// CHECK-MESSAGES: note: FIX-IT applied suggested code changes
+// CHECK-FIXES: std::string t3 = (Twine(42)).str();
+
+  const Twine t4 = Twine(42) + "b";
+// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: twine variables are prone to use-after-free bugs
+// CHECK-MESSAGES: note: FIX-IT applied suggested code changes
+// CHECK-FIXES: std::string t4 = (Twine(42) + "b").str();
+
+  const Twine t5 = Twine() + "b";
+// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: twine variables are prone to use-after-free bugs
+// CHECK-MESSAGES: note: FIX-IT applied suggested code changes
+// CHECK-FIXES: std::string t5 = (Twine() + "b").str();
+
+  const Twine t6 = true ? Twine() : Twine(42);
+// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: twine variables are prone to use-after-free bugs
+// CHECK-MESSAGES: note: FIX-IT applied suggested code changes
+// CHECK-FIXES: std::string t6 = (true ? Twine() : Twine(42)).str();
+
+  const Twine t7 = false ? Twine() : Twine("b");
+// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: twine variables are prone to use-after-free bugs
+// CHECK-MESSAGES: note: FIX-IT applied suggested code changes
+// CHECK-FIXES: std::string t7 = (false ? Twine() : Twine("b")).str();
+}
diff --git a/test/clang-tidy/macros.cpp b/test/clang-tidy/macros.cpp
new file mode 100644 (file)
index 0000000..fa4f32a
--- /dev/null
@@ -0,0 +1,7 @@
+// RUN: clang-tidy -checks='-*,google-explicit-constructor' %s -- | FileCheck %s
+
+#define Q(name) class name { name(int i); }
+
+Q(A);
+// CHECK: :[[@LINE-1]]:3: warning: single-argument constructors must be marked explicit
+// CHECK: :3:30: note: expanded from macro 'Q'
diff --git a/test/clang-tidy/misc-argument-comment-gmock.cpp b/test/clang-tidy/misc-argument-comment-gmock.cpp
new file mode 100644 (file)
index 0000000..56a7b14
--- /dev/null
@@ -0,0 +1,108 @@
+// RUN: %check_clang_tidy %s misc-argument-comment %t
+
+namespace testing {
+namespace internal {
+
+template <typename F>
+struct Function;
+
+template <typename R>
+struct Function<R()> {
+  typedef R Result;
+};
+
+template <typename R, typename A1>
+struct Function<R(A1)>
+    : Function<R()> {
+  typedef A1 Argument1;
+};
+
+template <typename R, typename A1, typename A2>
+struct Function<R(A1, A2)>
+    : Function<R(A1)> {
+  typedef A2 Argument2;
+};
+
+} // namespace internal
+
+template <typename F>
+class MockSpec {
+ public:
+  void f();
+};
+
+template <typename T>
+class Matcher {
+ public:
+  explicit Matcher();
+  Matcher(T value);
+};
+
+} // namespace testing
+
+#define GMOCK_RESULT_(tn, ...) \
+    tn ::testing::internal::Function<__VA_ARGS__>::Result
+#define GMOCK_ARG_(tn, N, ...) \
+    tn ::testing::internal::Function<__VA_ARGS__>::Argument##N
+#define GMOCK_MATCHER_(tn, N, ...) \
+    const ::testing::Matcher<GMOCK_ARG_(tn, N, __VA_ARGS__)>&
+#define GMOCK_METHOD2_(tn, constness, ct, Method, ...)            \
+  GMOCK_RESULT_(tn, __VA_ARGS__)                                  \
+  ct Method(                                                      \
+      GMOCK_ARG_(tn, 1, __VA_ARGS__) gmock_a1,                    \
+      GMOCK_ARG_(tn, 2, __VA_ARGS__) gmock_a2) constness;         \
+  ::testing::MockSpec<__VA_ARGS__>                                \
+      gmock_##Method(GMOCK_MATCHER_(tn, 1, __VA_ARGS__) gmock_a1, \
+                     GMOCK_MATCHER_(tn, 2, __VA_ARGS__) gmock_a2) constness
+#define MOCK_METHOD2(m, ...) GMOCK_METHOD2_(, , , m, __VA_ARGS__)
+#define MOCK_CONST_METHOD2(m, ...) GMOCK_METHOD2_(, const, , m, __VA_ARGS__)
+#define GMOCK_EXPECT_CALL_IMPL_(obj, call) \
+    ((obj).gmock_##call).f()
+#define EXPECT_CALL(obj, call) GMOCK_EXPECT_CALL_IMPL_(obj, call)
+
+class Base {
+ public:
+  virtual void Method(int param_one_base, int param_two_base);
+};
+class Derived : public Base {
+ public:
+  virtual void Method(int param_one, int param_two);
+  virtual void Method2(int p_one, int p_two) const;
+};
+class MockDerived : public Derived {
+ public:
+  MOCK_METHOD2(Method, void(int a, int b));
+  MOCK_CONST_METHOD2(Method2, void(int c, int d));
+};
+
+class MockStandalone {
+ public:
+  MOCK_METHOD2(Method, void(int aaa, int bbb));
+};
+
+void test_gmock_expectations() {
+  MockDerived m;
+  EXPECT_CALL(m, Method(/*param_one=*/1, /*param_tw=*/2));
+// CHECK-MESSAGES: [[@LINE-1]]:42: warning: argument name 'param_tw' in comment does not match parameter name 'param_two'
+// CHECK-FIXES:   EXPECT_CALL(m, Method(/*param_one=*/1, /*param_two=*/2));
+  EXPECT_CALL(m, Method2(/*p_on=*/3, /*p_two=*/4));
+// CHECK-MESSAGES: [[@LINE-1]]:26: warning: argument name 'p_on' in comment does not match parameter name 'p_one'
+// CHECK-FIXES:   EXPECT_CALL(m, Method2(/*p_one=*/3, /*p_two=*/4));
+
+  #define PARAM1 11
+  #define PARAM2 22
+  EXPECT_CALL(m, Method2(/*p_on1=*/PARAM1, /*p_tw2=*/PARAM2));
+// CHECK-MESSAGES: [[@LINE-1]]:26: warning: argument name 'p_on1' in comment does not match parameter name 'p_one'
+// CHECK-MESSAGES: [[@LINE-2]]:44: warning: argument name 'p_tw2' in comment does not match parameter name 'p_two'
+// CHECK-FIXES:   EXPECT_CALL(m, Method2(/*p_one=*/PARAM1, /*p_two=*/PARAM2));
+
+  MockStandalone m2;
+  EXPECT_CALL(m2, Method(/*aaa=*/5, /*bbc=*/6));
+}
+
+void test_gmock_direct_calls() {
+  MockDerived m;
+  m.Method(/*param_one=*/1, /*param_tw=*/2);
+// CHECK-MESSAGES: [[@LINE-1]]:29: warning: argument name 'param_tw' in comment does not match parameter name 'param_two'
+// CHECK-FIXES:   m.Method(/*param_one=*/1, /*param_two=*/2);
+}
diff --git a/test/clang-tidy/misc-argument-comment-strict.cpp b/test/clang-tidy/misc-argument-comment-strict.cpp
new file mode 100644 (file)
index 0000000..d37b869
--- /dev/null
@@ -0,0 +1,19 @@
+// RUN: %check_clang_tidy %s misc-argument-comment %t -- \
+// RUN:   -config="{CheckOptions: [{key: StrictMode, value: 1}]}" --
+
+void f(int _with_underscores_);
+void g(int x_);
+void ignores_underscores() {
+  f(/*With_Underscores=*/0);
+// CHECK-MESSAGES: [[@LINE-1]]:5: warning: argument name 'With_Underscores' in comment does not match parameter name '_with_underscores_'
+// CHECK-FIXES: f(/*_with_underscores_=*/0);
+  f(/*with_underscores=*/1);
+// CHECK-MESSAGES: [[@LINE-1]]:5: warning: argument name 'with_underscores' in comment does not match parameter name '_with_underscores_'
+// CHECK-FIXES: f(/*_with_underscores_=*/1);
+  f(/*_With_Underscores_=*/2);
+// CHECK-MESSAGES: [[@LINE-1]]:5: warning: argument name '_With_Underscores_' in comment does not match parameter name '_with_underscores_'
+// CHECK-FIXES: f(/*_with_underscores_=*/2);
+  g(/*X=*/3);
+// CHECK-MESSAGES: [[@LINE-1]]:5: warning: argument name 'X' in comment does not match parameter name 'x_'
+// CHECK-FIXES: g(/*x_=*/3);
+}
diff --git a/test/clang-tidy/misc-argument-comment.cpp b/test/clang-tidy/misc-argument-comment.cpp
new file mode 100644 (file)
index 0000000..e20ba10
--- /dev/null
@@ -0,0 +1,55 @@
+// RUN: %check_clang_tidy %s misc-argument-comment %t
+
+// FIXME: clang-tidy should provide a -verify mode to make writing these checks
+// easier and more accurate.
+
+void ffff(int xxxx, int yyyy);
+
+void f(int x, int y);
+void g() {
+  // CHECK-MESSAGES: [[@LINE+4]]:5: warning: argument name 'y' in comment does not match parameter name 'x'
+  // CHECK-MESSAGES: :[[@LINE-3]]:12: note: 'x' declared here
+  // CHECK-MESSAGES: [[@LINE+2]]:14: warning: argument name 'z' in comment does not match parameter name 'y'
+  // CHECK-MESSAGES: :[[@LINE-5]]:19: note: 'y' declared here
+  f(/*y=*/0, /*z=*/0);
+  // CHECK-FIXES: {{^}}  f(/*y=*/0, /*z=*/0);
+
+  ffff(0 /*aaaa=*/, /*bbbb*/ 0); // Unsupported formats.
+}
+
+struct Closure {};
+
+template <typename T1, typename T2>
+Closure *NewCallback(void (*f)(T1, T2), T1 arg1, T2 arg2) { return nullptr; }
+
+template <typename T1, typename T2>
+Closure *NewPermanentCallback(void (*f)(T1, T2), T1 arg1, T2 arg2) { return nullptr; }
+
+void h() {
+  (void)NewCallback(&ffff, /*xxxx=*/11, /*yyyy=*/22);
+  (void)NewPermanentCallback(&ffff, /*xxxx=*/11, /*yyyy=*/22);
+}
+
+template<typename... Args>
+void variadic(Args&&... args);
+
+template<typename... Args>
+void variadic2(int zzz, Args&&... args);
+
+void templates() {
+  variadic(/*xxx=*/0, /*yyy=*/1);
+  variadic2(/*zzU=*/0, /*xxx=*/1, /*yyy=*/2);
+  // CHECK-MESSAGES: [[@LINE-1]]:13: warning: argument name 'zzU' in comment does not match parameter name 'zzz'
+  // CHECK-FIXES: variadic2(/*zzz=*/0, /*xxx=*/1, /*yyy=*/2);
+}
+
+#define FALSE 0
+void qqq(bool aaa);
+void f() { qqq(/*bbb=*/FALSE); }
+// CHECK-MESSAGES: [[@LINE-1]]:16: warning: argument name 'bbb' in comment does not match parameter name 'aaa'
+// CHECK-FIXES: void f() { qqq(/*bbb=*/FALSE); }
+
+void f(bool _with_underscores_);
+void ignores_underscores() {
+  f(/*With_Underscores=*/false);
+}
diff --git a/test/clang-tidy/misc-assert-side-effect.cpp b/test/clang-tidy/misc-assert-side-effect.cpp
new file mode 100644 (file)
index 0000000..8cb4ebf
--- /dev/null
@@ -0,0 +1,114 @@
+// RUN: %check_clang_tidy %s misc-assert-side-effect %t -- -config="{CheckOptions: [{key: misc-assert-side-effect.CheckFunctionCalls, value: 1}, {key: misc-assert-side-effect.AssertMacros, value: 'assert,assert2,my_assert,convoluted_assert,msvc_assert'}]}" -- -fexceptions
+
+//===--- assert definition block ------------------------------------------===//
+int abort() { return 0; }
+
+#ifdef NDEBUG
+#define assert(x) 1
+#else
+#define assert(x)                                                              \
+  if (!(x))                                                                    \
+  (void)abort()
+#endif
+
+void print(...);
+#define assert2(e) (__builtin_expect(!(e), 0) ?                                \
+                       print (#e, __FILE__, __LINE__) : (void)0)
+
+#ifdef NDEBUG
+#define my_assert(x) 1
+#else
+#define my_assert(x)                                                           \
+  ((void)((x) ? 1 : abort()))
+#endif
+
+#ifdef NDEBUG
+#define not_my_assert(x) 1
+#else
+#define not_my_assert(x)                                                       \
+  if (!(x))                                                                    \
+  (void)abort()
+#endif
+
+#define real_assert(x) ((void)((x) ? 1 : abort()))
+#define wrap1(x) real_assert(x)
+#define wrap2(x) wrap1(x)
+#define convoluted_assert(x) wrap2(x)
+
+#define msvc_assert(expression) (void)(                                        \
+            (!!(expression)) ||                                                \
+            (abort(), 0)                                                       \
+        )
+
+
+//===----------------------------------------------------------------------===//
+
+class MyClass {
+public:
+  bool badFunc(int a, int b) { return a * b > 0; }
+  bool goodFunc(int a, int b) const { return a * b > 0; }
+
+  MyClass &operator=(const MyClass &rhs) { return *this; }
+
+  int operator-() { return 1; }
+
+  operator bool() const { return true; }
+
+  void operator delete(void *p) {}
+};
+
+bool freeFunction() {
+  return true;
+}
+
+int main() {
+
+  int X = 0;
+  bool B = false;
+  assert(X == 1);
+
+  assert(X = 1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found assert() with side effect [misc-assert-side-effect]
+  my_assert(X = 1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found my_assert() with side effect
+  convoluted_assert(X = 1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found convoluted_assert() with side effect
+  not_my_assert(X = 1);
+
+  assert(++X);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found assert() with side effect
+  assert(!B);
+
+  assert(B || true);
+
+  assert(freeFunction());
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found assert() with side effect
+
+  MyClass mc;
+  assert(mc.badFunc(0, 1));
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found assert() with side effect
+  assert(mc.goodFunc(0, 1));
+
+  MyClass mc2;
+  assert(mc2 = mc);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found assert() with side effect
+
+  assert(-mc > 0);
+
+  MyClass *mcp;
+  assert(mcp = new MyClass);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found assert() with side effect
+
+  assert((delete mcp, false));
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found assert() with side effect
+
+  assert((throw 1, false));
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found assert() with side effect
+
+  assert2(1 == 2 - 1);
+
+  msvc_assert(mc2 = mc);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found msvc_assert() with side effect
+
+  return 0;
+}
diff --git a/test/clang-tidy/misc-bool-pointer-implicit-conversion.cpp b/test/clang-tidy/misc-bool-pointer-implicit-conversion.cpp
new file mode 100644 (file)
index 0000000..bb71ce1
--- /dev/null
@@ -0,0 +1,82 @@
+// RUN: %check_clang_tidy %s misc-bool-pointer-implicit-conversion %t
+
+bool *SomeFunction();
+void SomeOtherFunction(bool*);
+bool F();
+void G(bool);
+
+
+template <typename T>
+void t(T b) {
+  if (b) {
+  }
+}
+
+void foo() {
+  bool *b = SomeFunction();
+  if (b) {
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: dubious check of 'bool *' against 'nullptr'
+// CHECK-FIXES: if (*b) {
+  }
+
+  if (F() && b) {
+// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: dubious check of 'bool *' against 'nullptr'
+// CHECK-FIXES: if (F() && *b) {
+  }
+
+  // TODO: warn here.
+  if (b) {
+    G(b);
+  }
+
+#define TESTMACRO if (b || F())
+
+  TESTMACRO {
+  }
+
+  t(b);
+
+  if (!b) {
+    // no-warning
+  }
+
+  if (SomeFunction()) {
+    // no-warning
+  }
+
+  bool *c = SomeFunction();
+  if (c) {
+    (void)c;
+    (void)*c; // no-warning
+  }
+
+  if (c) {
+    *c = true; // no-warning
+  }
+
+  if (c) {
+    c[0] = false; // no-warning
+  }
+
+  if (c) {
+    SomeOtherFunction(c); // no-warning
+  }
+
+  if (c) {
+    delete[] c; // no-warning
+  }
+
+  if (c) {
+    *(c) = false; // no-warning
+  }
+
+  struct {
+    bool *b;
+  } d = { SomeFunction() };
+
+  if (d.b)
+    (void)*d.b; // no-warning
+
+#define CHECK(b) if (b) {}
+  CHECK(c)
+}
diff --git a/test/clang-tidy/misc-dangling-handle.cpp b/test/clang-tidy/misc-dangling-handle.cpp
new file mode 100644 (file)
index 0000000..08115a3
--- /dev/null
@@ -0,0 +1,191 @@
+// RUN: %check_clang_tidy %s misc-dangling-handle %t -- \
+// RUN:   -config="{CheckOptions: \
+// RUN:             [{key: misc-dangling-handle.HandleClasses, \
+// RUN:               value: 'std::basic_string_view; ::llvm::StringRef;'}]}" \
+// RUN:   -- -std=c++11
+
+namespace std {
+
+template <typename T>
+class vector {
+ public:
+  using const_iterator = const T*;
+  using iterator = T*;
+  using size_type = int;
+
+  void assign(size_type count, const T& value);
+  iterator insert(const_iterator pos, const T& value);
+  iterator insert(const_iterator pos, T&& value);
+  iterator insert(const_iterator pos, size_type count, const T& value);
+  void push_back(const T&);
+  void push_back(T&&);
+  void resize(size_type count, const T& value);
+};
+
+template <typename, typename>
+class pair {};
+
+template <typename T>
+class set {
+ public:
+  using const_iterator = const T*;
+  using iterator = T*;
+
+  std::pair<iterator, bool> insert(const T& value);
+  std::pair<iterator, bool> insert(T&& value);
+  iterator insert(const_iterator hint, const T& value);
+  iterator insert(const_iterator hint, T&& value);
+};
+
+template <typename Key, typename Value>
+class map {
+ public:
+  using value_type = pair<Key, Value>;
+  value_type& operator[](const Key& key);
+  value_type& operator[](Key&& key);
+};
+
+class basic_string {
+ public:
+  basic_string();
+  basic_string(const char*);
+  ~basic_string();
+};
+
+typedef basic_string string;
+
+class basic_string_view {
+ public:
+  basic_string_view(const char*);
+  basic_string_view(const basic_string&);
+};
+
+typedef basic_string_view string_view;
+
+}  // namespace std
+
+namespace llvm {
+
+class StringRef {
+ public:
+  StringRef();
+  StringRef(const char*);
+  StringRef(const std::string&);
+};
+
+}  // namespace llvm
+
+std::string ReturnsAString();
+
+void Positives() {
+  std::string_view view1 = std::string();
+  // CHECK-MESSAGES: [[@LINE-1]]:20: warning: std::basic_string_view outlives its value [misc-dangling-handle]
+
+  std::string_view view_2 = ReturnsAString();
+  // CHECK-MESSAGES: [[@LINE-1]]:20: warning: std::basic_string_view outlives
+
+  view1 = std::string();
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: std::basic_string_view outlives
+
+  const std::string& str_ref = "";
+  std::string_view view3 = true ? "A" : str_ref;
+  // CHECK-MESSAGES: [[@LINE-1]]:20: warning: std::basic_string_view outlives
+  view3 = true ? "A" : str_ref;
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: std::basic_string_view outlives
+
+  std::string_view view4(ReturnsAString());
+  // CHECK-MESSAGES: [[@LINE-1]]:20: warning: std::basic_string_view outlives
+}
+
+void OtherTypes() {
+  llvm::StringRef ref = std::string();
+  // CHECK-MESSAGES: [[@LINE-1]]:19: warning: llvm::StringRef outlives its value
+}
+
+const char static_array[] = "A";
+std::string_view ReturnStatements(int i, std::string value_arg,
+                                  const std::string &ref_arg) {
+  const char array[] = "A";
+  const char* ptr = "A";
+  std::string s;
+  static std::string ss;
+  switch (i) {
+    // Bad cases
+    case 0:
+      return array;  // refers to local
+      // CHECK-MESSAGES: [[@LINE-1]]:7: warning: std::basic_string_view outliv
+    case 1:
+      return s;  // refers to local
+      // CHECK-MESSAGES: [[@LINE-1]]:7: warning: std::basic_string_view outliv
+    case 2:
+      return std::string();  // refers to temporary
+      // CHECK-MESSAGES: [[@LINE-1]]:7: warning: std::basic_string_view outliv
+    case 3:
+      return value_arg;  // refers to by-value arg
+      // CHECK-MESSAGES: [[@LINE-1]]:7: warning: std::basic_string_view outliv
+
+    // Ok cases
+    case 100:
+      return ss;  // refers to static
+    case 101:
+      return static_array;  // refers to static
+    case 102:
+      return ptr;  // pointer is ok
+    case 103:
+      return ref_arg;  // refers to by-ref arg
+  }
+
+  struct S {
+    std::string_view view() { return value; }
+    std::string value;
+  };
+
+  (void)[&]()->std::string_view {
+    // This should not warn. The string is bound by reference.
+    return s;
+  };
+  (void)[=]() -> std::string_view {
+    // This should not warn. The reference is valid as long as the lambda.
+    return s;
+  };
+  (void)[=]() -> std::string_view {
+    // FIXME: This one should warn. We are returning a reference to a local
+    // lambda variable.
+    std::string local;
+    return local;
+  };
+  return "";
+}
+
+void Containers() {
+  std::vector<std::string_view> v;
+  v.assign(3, std::string());
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: std::basic_string_view outlives
+  v.insert(nullptr, std::string());
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: std::basic_string_view outlives
+  v.insert(nullptr, 3, std::string());
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: std::basic_string_view outlives
+  v.push_back(std::string());
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: std::basic_string_view outlives
+  v.resize(3, std::string());
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: std::basic_string_view outlives
+
+  std::set<std::string_view> s;
+  s.insert(std::string());
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: std::basic_string_view outlives
+  s.insert(nullptr, std::string());
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: std::basic_string_view outlives
+
+  std::map<std::string_view, int> m;
+  m[std::string()];
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: std::basic_string_view outlives
+}
+
+void TakesAStringView(std::string_view);
+
+void Negatives(std::string_view default_arg = ReturnsAString()) {
+  std::string str;
+  std::string_view view = str;
+
+  TakesAStringView(std::string());
+}
diff --git a/test/clang-tidy/misc-definitions-in-headers-1z.hpp b/test/clang-tidy/misc-definitions-in-headers-1z.hpp
new file mode 100644 (file)
index 0000000..79fb7bc
--- /dev/null
@@ -0,0 +1,10 @@
+// RUN: %check_clang_tidy %s misc-definitions-in-headers %t -- -- -std=c++1z
+
+class CE {
+  constexpr static int i = 5; // OK: inline variable definition.
+};
+
+inline int i = 5; // OK: inline variable definition.
+
+int b = 1;
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: variable 'b' defined in a header file; variable definitions in header files can lead to ODR violations [misc-definitions-in-headers]
diff --git a/test/clang-tidy/misc-definitions-in-headers.hpp b/test/clang-tidy/misc-definitions-in-headers.hpp
new file mode 100644 (file)
index 0000000..5e83e68
--- /dev/null
@@ -0,0 +1,181 @@
+// RUN: %check_clang_tidy %s misc-definitions-in-headers %t
+
+int f() {
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: function 'f' defined in a header file; function definitions in header files can lead to ODR violations [misc-definitions-in-headers]
+// CHECK-FIXES: inline int f() {
+  return 1;
+}
+
+class CA {
+  void f1() {} // OK: inline class member function definition.
+  void f2();
+  template<typename T>
+  T f3() {
+    T a = 1;
+    return a;
+  }
+  template<typename T>
+  struct CAA {
+    struct CAB {
+      void f4();
+    };
+  };
+};
+
+void CA::f2() { }
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: function 'f2' defined in a header file;
+// CHECK-FIXES: inline void CA::f2() {
+
+template <>
+int CA::f3() {
+// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: full function template specialization 'f3<int>' defined in a header file;
+// CHECK-FIXES: inline int CA::f3() {
+  int a = 1;
+  return a;
+}
+
+template <typename T>
+void CA::CAA<T>::CAB::f4() {
+// OK: member function definition of a nested template class in a class.
+}
+
+template <typename T>
+struct CB {
+  void f1();
+  struct CCA {
+    void f2(T a);
+  };
+  struct CCB;  // OK: forward declaration.
+  static int a; // OK: class static data member declaration.
+};
+
+template <typename T>
+void CB<T>::f1() { // OK: Member function definition of a class template.
+}
+
+template <typename T>
+void CB<T>::CCA::f2(T a) {
+// OK: member function definition of a nested class in a class template.
+}
+
+template <typename T>
+struct CB<T>::CCB {
+  void f3();
+};
+
+template <typename T>
+void CB<T>::CCB::f3() {
+// OK: member function definition of a nested class in a class template.
+}
+
+template <typename T>
+int CB<T>::a = 2; // OK: static data member definition of a class template.
+
+template class CB<int>; // OK: explicitly instantiated static data member of a class template.
+inline int callCB() {
+  CB<double> cb; // OK: implicitly instantiated static data member of a class template.
+  return cb.a;
+}
+
+template <typename T>
+T tf() { // OK: template function definition.
+  T a;
+  return a;
+}
+
+
+namespace NA {
+  int f() { return 1; }
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: function 'f' defined in a header file;
+// CHECK-FIXES: inline int f() { return 1; }
+}
+
+template <typename T>
+T f3() {
+  T a = 1;
+  return a;
+}
+
+template <>
+int f3() {
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: full function template specialization 'f3<int>' defined in a header file;
+// CHECK-FIXES: inline int f3() {
+  int a = 1;
+  return a;
+}
+
+int f5(); // OK: function declaration.
+inline int f6() { return 1; } // OK: inline function definition.
+namespace {
+  int f7() { return 1; }
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: function 'f7' defined in a header file;
+}
+
+int f8() = delete; // OK: the function being marked delete is not callable.
+
+template <typename T>
+int f9(T t) { return 1; }
+
+inline void callF9() { f9(1); } // OK: implicitly instantiated function.
+template int f9(double); // OK: explicitly instantiated function.
+
+int a = 1;
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: variable 'a' defined in a header file; variable definitions in header files can lead to ODR violations [misc-definitions-in-headers]
+CA a1;
+// CHECK-MESSAGES: :[[@LINE-1]]:4: warning: variable 'a1' defined in a header file;
+
+namespace NB {
+  int b = 1;
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: variable 'b' defined in a header file;
+  const int c = 1; // OK: internal linkage variable definition.
+}
+
+class CC {
+  static int d; // OK: class static data member declaration.
+};
+
+int CC::d = 1;
+// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: variable 'd' defined in a header file;
+
+const char* ca = "foo";
+// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: variable 'ca' defined in a header file;
+
+namespace {
+  int e = 2;
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: variable 'e' defined in a header file;
+}
+
+const char* const g = "foo"; // OK: internal linkage variable definition.
+static int h = 1; // OK: internal linkage variable definition.
+const int i = 1; // OK: internal linkage variable definition.
+extern int j; // OK: internal linkage variable definition.
+
+template <typename T, typename U>
+struct CD {
+  int f();
+};
+
+template <typename T>
+struct CD<T, int> {
+  int f();
+};
+
+template <>
+struct CD<int, int> {
+  int f();
+};
+
+int CD<int, int>::f() {
+// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: function 'f' defined in a header file;
+// CHECK-FIXES: inline int CD<int, int>::f() {
+  return 0;
+}
+
+template <typename T>
+int CD<T, int>::f() { // OK: partial template specialization.
+  return 0;
+}
+
+constexpr int k = 1; // OK: constexpr variable has internal linkage.
+
+constexpr int f10() { return 0; } // OK: constexpr function definition.
diff --git a/test/clang-tidy/misc-fold-init-type.cpp b/test/clang-tidy/misc-fold-init-type.cpp
new file mode 100644 (file)
index 0000000..43ba95c
--- /dev/null
@@ -0,0 +1,158 @@
+// RUN: %check_clang_tidy %s misc-fold-init-type %t
+
+namespace std {
+template <class InputIt, class T>
+T accumulate(InputIt first, InputIt last, T init);
+
+template <class InputIt, class T>
+T reduce(InputIt first, InputIt last, T init);
+template <class ExecutionPolicy, class InputIt, class T>
+T reduce(ExecutionPolicy &&policy,
+         InputIt first, InputIt last, T init);
+
+struct parallel_execution_policy {};
+constexpr parallel_execution_policy par{};
+
+template <class InputIt1, class InputIt2, class T>
+T inner_product(InputIt1 first1, InputIt1 last1,
+                InputIt2 first2, T value);
+
+template <class ExecutionPolicy, class InputIt1, class InputIt2, class T>
+T inner_product(ExecutionPolicy &&policy, InputIt1 first1, InputIt1 last1,
+                InputIt2 first2, T value);
+
+} // namespace std
+
+struct FloatIterator {
+  typedef float value_type;
+};
+template <typename ValueType>
+struct TypedefTemplateIterator { typedef ValueType value_type; };
+template <typename ValueType>
+struct UsingTemplateIterator { using value_type = ValueType; };
+template <typename ValueType>
+struct DependentTypedefTemplateIterator { typedef typename ValueType::value_type value_type; };
+template <typename ValueType>
+struct DependentUsingTemplateIterator : public TypedefTemplateIterator<ValueType> { using typename TypedefTemplateIterator<ValueType>::value_type; };
+using TypedeffedIterator = FloatIterator;
+
+// Positives.
+
+int accumulatePositive1() {
+  float a[1] = {0.5f};
+  return std::accumulate(a, a + 1, 0);
+  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'float' into type 'int'
+}
+
+int accumulatePositive2() {
+  FloatIterator it;
+  return std::accumulate(it, it, 0);
+  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'float' into type 'int'
+}
+
+int accumulatePositive3() {
+  double a[1] = {0.0};
+  return std::accumulate(a, a + 1, 0.0f);
+  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'double' into type 'float'
+}
+
+int accumulatePositive4() {
+  TypedefTemplateIterator<unsigned> it;
+  return std::accumulate(it, it, 0);
+  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'unsigned int' into type 'int'
+}
+
+int accumulatePositive5() {
+  UsingTemplateIterator<unsigned> it;
+  return std::accumulate(it, it, 0);
+  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'unsigned int' into type 'int'
+}
+
+int accumulatePositive6() {
+  DependentTypedefTemplateIterator<UsingTemplateIterator<unsigned>> it;
+  return std::accumulate(it, it, 0);
+  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'unsigned int' into type 'int'
+}
+
+int accumulatePositive7() {
+  TypedeffedIterator it;
+  return std::accumulate(it, it, 0);
+  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'float' into type 'int'
+}
+
+int accumulatePositive8() {
+  DependentUsingTemplateIterator<unsigned> it;
+  return std::accumulate(it, it, 0);
+  // FIXME: this one should trigger too.
+}
+
+int reducePositive1() {
+  float a[1] = {0.5f};
+  return std::reduce(a, a + 1, 0);
+  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'float' into type 'int'
+}
+
+int reducePositive2() {
+  float a[1] = {0.5f};
+  return std::reduce(std::par, a, a + 1, 0);
+  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'float' into type 'int'
+}
+
+int innerProductPositive1() {
+  float a[1] = {0.5f};
+  int b[1] = {1};
+  return std::inner_product(std::par, a, a + 1, b, 0);
+  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'float' into type 'int'
+}
+
+int innerProductPositive2() {
+  float a[1] = {0.5f};
+  int b[1] = {1};
+  return std::inner_product(std::par, a, a + 1, b, 0);
+  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: folding type 'float' into type 'int'
+}
+
+// Negatives.
+
+int negative1() {
+  float a[1] = {0.5f};
+  // This is OK because types match.
+  return std::accumulate(a, a + 1, 0.0);
+}
+
+int negative2() {
+  float a[1] = {0.5f};
+  // This is OK because double is bigger than float.
+  return std::accumulate(a, a + 1, 0.0);
+}
+
+int negative3() {
+  float a[1] = {0.5f};
+  // This is OK because the user explicitly specified T.
+  return std::accumulate<float *, float>(a, a + 1, 0);
+}
+
+int negative4() {
+  TypedefTemplateIterator<unsigned> it;
+  // For now this is OK.
+  return std::accumulate(it, it, 0.0);
+}
+
+int negative5() {
+  float a[1] = {0.5f};
+  float b[1] = {1.0f};
+  return std::inner_product(std::par, a, a + 1, b, 0.0f);
+}
+
+namespace blah {
+namespace std {
+template <class InputIt, class T>
+T accumulate(InputIt, InputIt, T); // We should not care about this one.
+}
+
+int negative5() {
+  float a[1] = {0.5f};
+  // Note that this is using blah::std::accumulate.
+  return std::accumulate(a, a + 1, 0);
+}
+}
diff --git a/test/clang-tidy/misc-forward-declaration-namespace.cpp b/test/clang-tidy/misc-forward-declaration-namespace.cpp
new file mode 100644 (file)
index 0000000..2940acd
--- /dev/null
@@ -0,0 +1,163 @@
+// RUN: %check_clang_tidy %s misc-forward-declaration-namespace %t
+
+namespace {
+// This is a declaration in a wrong namespace.
+class T_A;
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration 'T_A' is never referenced, but a declaration with the same name found in another namespace 'na' [misc-forward-declaration-namespace]
+// CHECK-MESSAGES: note: a declaration of 'T_A' is found here
+// CHECK-MESSAGES: :[[@LINE-3]]:7: warning: no definition found for 'T_A', but a definition with the same name 'T_A' found in another namespace '(global)' [misc-forward-declaration-namespace]
+// CHECK-MESSAGES: note: a definition of 'T_A' is found here
+}
+
+namespace na {
+// This is a declaration in a wrong namespace.
+class T_A;
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration 'T_A' is never referenced, but a declaration with the same name found in another namespace '(anonymous)'
+// CHECK-MESSAGES: note: a declaration of 'T_A' is found here
+// CHECK-MESSAGES: :[[@LINE-3]]:7: warning: no definition found for 'T_A', but a definition with the same name 'T_A' found in another namespace '(global)'
+// CHECK-MESSAGES: note: a definition of 'T_A' is found here
+}
+
+class T_A;
+
+class T_A {
+  int x;
+};
+
+class NESTED;
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: no definition found for 'NESTED', but a definition with the same name 'NESTED' found in another namespace '(anonymous namespace)::nq::(anonymous)'
+// CHECK-MESSAGES: note: a definition of 'NESTED' is found here
+
+namespace {
+namespace nq {
+namespace {
+class NESTED {};
+}
+}
+}
+
+namespace na {
+class T_B;
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration 'T_B' is never referenced, but a declaration with the same name found in another namespace 'nb'
+// CHECK-MESSAGES: note: a declaration of 'T_B' is found here
+// CHECK-MESSAGES: :[[@LINE-3]]:7: warning: no definition found for 'T_B', but a definition with the same name 'T_B' found in another namespace 'nb'
+// CHECK-MESSAGES: note: a definition of 'T_B' is found here
+}
+
+namespace nb {
+class T_B;
+}
+
+namespace nb {
+class T_B {
+  int x;
+};
+}
+
+namespace na {
+class T_B;
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration 'T_B' is never referenced, but a declaration with the same name found in another namespace 'nb'
+// CHECK-MESSAGES: note: a declaration of 'T_B' is found here
+// CHECK-MESSAGES: :[[@LINE-3]]:7: warning: no definition found for 'T_B', but a definition with the same name 'T_B' found in another namespace 'nb'
+// CHECK-MESSAGES: note: a definition of 'T_B' is found here
+}
+
+// A simple forward declaration. Although it is never used, but no declaration
+// with the same name is found in other namespace.
+class OUTSIDER;
+
+namespace na {
+// This class is referenced declaration, we don't generate warning.
+class OUTSIDER_1;
+}
+
+void f(na::OUTSIDER_1);
+
+namespace nc {
+// This class is referenced as friend in OOP.
+class OUTSIDER_1;
+
+class OOP {
+  friend struct OUTSIDER_1;
+};
+}
+
+namespace nd {
+class OUTSIDER_1;
+void f(OUTSIDER_1 *);
+}
+
+namespace nb {
+class OUTSIDER_1;
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration 'OUTSIDER_1' is never referenced, but a declaration with the same name found in another namespace 'na'
+// CHECK-MESSAGES: note: a declaration of 'OUTSIDER_1' is found here
+}
+
+
+namespace na {
+template<typename T>
+class T_C;
+}
+
+namespace nb {
+// FIXME: this is an error, but we don't consider template class declaration
+// now.
+template<typename T>
+class T_C;
+}
+
+namespace na {
+template<typename T>
+class T_C {
+  int x;
+};
+}
+
+namespace na {
+
+template <typename T>
+class T_TEMP {
+  template <typename _Tp1>
+  struct rebind { typedef T_TEMP<_Tp1> other; };
+};
+
+// We ignore class template specialization.
+template class T_TEMP<char>;
+}
+
+namespace nb {
+
+template <typename T>
+class T_TEMP_1 {
+  template <typename _Tp1>
+  struct rebind { typedef T_TEMP_1<_Tp1> other; };
+};
+
+// We ignore class template specialization.
+extern template class T_TEMP_1<char>;
+}
+
+namespace nd {
+class D;
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: declaration 'D' is never referenced, but a declaration with the same name found in another namespace 'nd::ne'
+// CHECK-MESSAGES: note: a declaration of 'D' is found here
+}
+
+namespace nd {
+namespace ne {
+class D;
+}
+}
+
+int f(nd::ne::D &d);
+
+
+// This should be ignored by the check.
+template <typename... Args>
+class Observer {
+  class Impl;
+};
+
+template <typename... Args>
+class Observer<Args...>::Impl {
+};
diff --git a/test/clang-tidy/misc-forwarding-reference-overload.cpp b/test/clang-tidy/misc-forwarding-reference-overload.cpp
new file mode 100644 (file)
index 0000000..a309466
--- /dev/null
@@ -0,0 +1,145 @@
+// RUN: %check_clang_tidy %s misc-forwarding-reference-overload %t -- -- -std=c++14
+
+namespace std {
+template <bool B, class T = void> struct enable_if { typedef T type; };
+
+template <class T> struct enable_if<true, T> { typedef T type; };
+
+template <bool B, class T = void>
+using enable_if_t = typename enable_if<B, T>::type;
+
+template <class T> struct enable_if_nice { typedef T type; };
+} // namespace std
+
+namespace foo {
+template <class T> struct enable_if { typedef T type; };
+} // namespace foo
+
+template <typename T> constexpr bool just_true = true;
+
+class Test1 {
+public:
+  template <typename T> Test1(T &&n);
+  // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: constructor accepting a forwarding reference can hide the copy and move constructors [misc-forwarding-reference-overload]
+
+  template <typename T> Test1(T &&n, int i = 5, ...);
+  // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: constructor accepting a forwarding reference can hide the copy and move constructors
+
+  template <typename T, typename U = typename std::enable_if_nice<T>::type>
+  Test1(T &&n);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor accepting a forwarding reference can hide the copy and move constructors
+
+  template <typename T>
+  Test1(T &&n, typename foo::enable_if<long>::type i = 5, ...);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor accepting a forwarding reference can hide the copy and move constructors
+
+  Test1(const Test1 &other) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: note: copy constructor declared here
+
+  Test1(Test1 &other) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: note: copy constructor declared here
+
+  Test1(Test1 &&other) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: note: move constructor declared here
+};
+
+template <typename U> class Test2 {
+public:
+  // Two parameters without default value, can't act as copy / move constructor.
+  template <typename T, class V> Test2(T &&n, V &&m, int i = 5, ...);
+
+  // Guarded with enable_if.
+  template <typename T>
+  Test2(T &&n, int i = 5,
+        std::enable_if_t<sizeof(int) < sizeof(long), int> a = 5, ...);
+
+  // Guarded with enable_if.
+  template <typename T, typename X = typename std::enable_if<
+                            sizeof(int) < sizeof(long), double>::type &>
+  Test2(T &&n);
+
+  // Guarded with enable_if.
+  template <typename T>
+  Test2(T &&n, typename std::enable_if<just_true<T>>::type **a = nullptr);
+
+  // Guarded with enable_if.
+  template <typename T, typename X = std::enable_if_t<just_true<T>> *&&>
+  Test2(T &&n, double d = 0.0);
+
+  // Not a forwarding reference parameter.
+  template <typename T> Test2(const T &&n);
+
+  // Not a forwarding reference parameter.
+  Test2(int &&x);
+
+  // Two parameters without default value, can't act as copy / move constructor.
+  template <typename T> Test2(T &&n, int x);
+
+  // Not a forwarding reference parameter.
+  template <typename T> Test2(U &&n);
+};
+
+// The copy and move constructors are both disabled.
+class Test3 {
+public:
+  template <typename T> Test3(T &&n);
+
+  template <typename T> Test3(T &&n, int I = 5, ...);
+
+  Test3(const Test3 &rhs) = delete;
+
+private:
+  Test3(Test3 &&rhs);
+};
+
+// Both the copy and the (compiler generated) move constructors can be hidden.
+class Test4 {
+public:
+  template <typename T> Test4(T &&n);
+  // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: constructor accepting a forwarding reference can hide the copy and move constructors
+
+  Test4(const Test4 &rhs);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: note: copy constructor declared here
+};
+
+// Nothing can be hidden, the copy constructor is implicitly deleted.
+class Test5 {
+public:
+  template <typename T> Test5(T &&n);
+
+  Test5(Test5 &&rhs) = delete;
+};
+
+// Only the move constructor can be hidden.
+class Test6 {
+public:
+  template <typename T> Test6(T &&n);
+  // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: constructor accepting a forwarding reference can hide the move constructor
+
+  Test6(Test6 &&rhs);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: note: move constructor declared here
+private:
+  Test6(const Test6 &rhs);
+};
+
+// Do not dereference a null BaseType.
+template <class _Callable> class result_of;
+template <class _Fp, class ..._Args> class result_of<_Fp(_Args...)> { };
+template <class _Tp> using result_of_t = typename result_of<_Tp>::type;
+
+template <class... _Types> struct __overload;
+template <class _Tp, class... _Types>
+struct __overload<_Tp, _Types...> : __overload<_Types...> {
+  using __overload<_Types...>::operator();
+};
+
+template <class _Tp, class... _Types>
+using __best_match_t = typename result_of_t<__overload<_Types...>(_Tp&&)>::type;
+
+template <class... _Types>
+class variant {
+public:
+  template <class _Arg, class _Tp = __best_match_t<_Arg, _Types...> >
+  constexpr variant(_Arg&& __arg) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: constructor accepting a forwarding reference can hide the copy and move constructors
+};
diff --git a/test/clang-tidy/misc-inaccurate-erase.cpp b/test/clang-tidy/misc-inaccurate-erase.cpp
new file mode 100644 (file)
index 0000000..71b68a6
--- /dev/null
@@ -0,0 +1,100 @@
+// RUN: %check_clang_tidy %s misc-inaccurate-erase %t
+
+namespace std {
+template <typename T> struct vec_iterator {
+  T ptr;
+  vec_iterator operator++(int);
+
+  template <typename X>
+  vec_iterator(const vec_iterator<X> &); // Omit enable_if<...>.
+};
+
+template <typename T> struct vector {
+  typedef vec_iterator<T*> iterator;
+
+  iterator begin();
+  iterator end();
+
+  void erase(iterator);
+  void erase(iterator, iterator);
+};
+
+template <typename T> struct vector_with_const_iterator {
+  typedef vec_iterator<T*> iterator;
+  typedef vec_iterator<const T*> const_iterator;
+
+  iterator begin();
+  iterator end();
+
+  void erase(const_iterator);
+  void erase(const_iterator, const_iterator);
+};
+
+template <typename FwIt, typename T>
+FwIt remove(FwIt begin, FwIt end, const T &val);
+
+template <typename FwIt, typename Func>
+FwIt remove_if(FwIt begin, FwIt end, Func f);
+
+template <typename FwIt> FwIt unique(FwIt begin, FwIt end);
+
+template <typename T> struct unique_ptr {};
+} // namespace std
+
+struct custom_iter {};
+struct custom_container {
+  void erase(...);
+  custom_iter begin();
+  custom_iter end();
+};
+
+template <typename T> void g() {
+  T t;
+  t.erase(std::remove(t.begin(), t.end(), 10));
+  // CHECK-FIXES: {{^  }}t.erase(std::remove(t.begin(), t.end(), 10));{{$}}
+
+  std::vector<int> v;
+  v.erase(remove(v.begin(), v.end(), 10));
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this call will remove at most one
+  // CHECK-FIXES: {{^  }}v.erase(remove(v.begin(), v.end(), 10), v.end());{{$}}
+}
+
+#define ERASE(x, y) x.erase(remove(x.begin(), x.end(), y))
+// CHECK-FIXES: #define ERASE(x, y) x.erase(remove(x.begin(), x.end(), y))
+
+int main() {
+  std::vector<int> v;
+
+  v.erase(remove(v.begin(), v.end(), 10));
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this call will remove at most one item even when multiple items should be removed [misc-inaccurate-erase]
+  // CHECK-FIXES: {{^  }}v.erase(remove(v.begin(), v.end(), 10), v.end());{{$}}
+  v.erase(remove(v.begin(), v.end(), 20), v.end());
+
+  auto *p = &v;
+  p->erase(remove(p->begin(), p->end(), 11));
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this call will remove at most one
+  // CHECK-FIXES: {{^  }}p->erase(remove(p->begin(), p->end(), 11), p->end());{{$}}
+
+  std::vector_with_const_iterator<int> v2;
+  v2.erase(remove(v2.begin(), v2.end(), 12));
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this call will remove at most one
+  // CHECK-FIXES: {{^  }}v2.erase(remove(v2.begin(), v2.end(), 12), v2.end());{{$}}
+
+  // Fix is not trivial.
+  auto it = v.end();
+  v.erase(remove(v.begin(), it, 10));
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this call will remove at most one
+  // CHECK-FIXES: {{^  }}v.erase(remove(v.begin(), it, 10));{{$}}
+
+  g<std::vector<int>>();
+  g<custom_container>();
+
+  ERASE(v, 15);
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: this call will remove at most one
+  // CHECK-FIXES: {{^  }}ERASE(v, 15);{{$}}
+
+  std::vector<std::unique_ptr<int>> vupi;
+  auto iter = vupi.begin();
+  vupi.erase(iter++);
+  // CHECK-FIXES: {{^  }}vupi.erase(iter++);{{$}}
+}
diff --git a/test/clang-tidy/misc-incorrect-roundings.cpp b/test/clang-tidy/misc-incorrect-roundings.cpp
new file mode 100644 (file)
index 0000000..ae8638d
--- /dev/null
@@ -0,0 +1,86 @@
+// RUN: %check_clang_tidy %s misc-incorrect-roundings %t
+
+void b(int x) {}
+
+void f1() {
+  float f;
+  double d;
+  long double ld;
+  int x;
+
+  x = (d + 0.5);
+  // CHECK-MESSAGES: [[@LINE-1]]:7: warning: casting (double + 0.5) to integer leads to incorrect rounding; consider using lround (#include <cmath>) instead [misc-incorrect-roundings]
+  x = (d + 0.5f);
+  // CHECK-MESSAGES: [[@LINE-1]]:7: warning: casting (double + 0.5)
+  x = (f + 0.5);
+  // CHECK-MESSAGES: [[@LINE-1]]:7: warning: casting (double + 0.5)
+  x = (f + 0.5f);
+  // CHECK-MESSAGES: [[@LINE-1]]:7: warning: casting (double + 0.5)
+  x = (0.5 + d);
+  // CHECK-MESSAGES: [[@LINE-1]]:7: warning: casting (double + 0.5)
+  x = (0.5f + d);
+  // CHECK-MESSAGES: [[@LINE-1]]:7: warning: casting (double + 0.5)
+  x = (0.5 + ld);
+  // CHECK-MESSAGES: [[@LINE-1]]:7: warning: casting (double + 0.5)
+  x = (0.5f + ld);
+  // CHECK-MESSAGES: [[@LINE-1]]:7: warning: casting (double + 0.5)
+  x = (0.5 + f);
+  // CHECK-MESSAGES: [[@LINE-1]]:7: warning: casting (double + 0.5)
+  x = (0.5f + f);
+  // CHECK-MESSAGES: [[@LINE-1]]:7: warning: casting (double + 0.5)
+  x = (int)(d + 0.5);
+  // CHECK-MESSAGES: [[@LINE-1]]:12: warning: casting (double + 0.5)
+  x = (int)(d + 0.5f);
+  // CHECK-MESSAGES: [[@LINE-1]]:12: warning: casting (double + 0.5)
+  x = (int)(ld + 0.5);
+  // CHECK-MESSAGES: [[@LINE-1]]:12: warning: casting (double + 0.5)
+  x = (int)(ld + 0.5f);
+  // CHECK-MESSAGES: [[@LINE-1]]:12: warning: casting (double + 0.5)
+  x = (int)(f + 0.5);
+  // CHECK-MESSAGES: [[@LINE-1]]:12: warning: casting (double + 0.5)
+  x = (int)(f + 0.5f);
+  // CHECK-MESSAGES: [[@LINE-1]]:12: warning: casting (double + 0.5)
+  x = (int)(0.5 + d);
+  // CHECK-MESSAGES: [[@LINE-1]]:12: warning: casting (double + 0.5)
+  x = (int)(0.5f + d);
+  // CHECK-MESSAGES: [[@LINE-1]]:12: warning: casting (double + 0.5)
+  x = (int)(0.5 + ld);
+  // CHECK-MESSAGES: [[@LINE-1]]:12: warning: casting (double + 0.5)
+  x = (int)(0.5f + ld);
+  // CHECK-MESSAGES: [[@LINE-1]]:12: warning: casting (double + 0.5)
+  x = (int)(0.5 + f);
+  // CHECK-MESSAGES: [[@LINE-1]]:12: warning: casting (double + 0.5)
+  x = (int)(0.5f + f);
+  // CHECK-MESSAGES: [[@LINE-1]]:12: warning: casting (double + 0.5)
+  x = static_cast<int>(d + 0.5);
+  // CHECK-MESSAGES: [[@LINE-1]]:24: warning: casting (double + 0.5)
+  x = static_cast<int>(d + 0.5f);
+  // CHECK-MESSAGES: [[@LINE-1]]:24: warning: casting (double + 0.5)
+  x = static_cast<int>(ld + 0.5);
+  // CHECK-MESSAGES: [[@LINE-1]]:24: warning: casting (double + 0.5)
+  x = static_cast<int>(ld + 0.5f);
+  // CHECK-MESSAGES: [[@LINE-1]]:24: warning: casting (double + 0.5)
+  x = static_cast<int>(f + 0.5);
+  // CHECK-MESSAGES: [[@LINE-1]]:24: warning: casting (double + 0.5)
+  x = static_cast<int>(f + 0.5f);
+  // CHECK-MESSAGES: [[@LINE-1]]:24: warning: casting (double + 0.5)
+  x = static_cast<int>(0.5 + d);
+  // CHECK-MESSAGES: [[@LINE-1]]:24: warning: casting (double + 0.5)
+  x = static_cast<int>(0.5f + d);
+  // CHECK-MESSAGES: [[@LINE-1]]:24: warning: casting (double + 0.5)
+  x = static_cast<int>(0.5 + ld);
+  // CHECK-MESSAGES: [[@LINE-1]]:24: warning: casting (double + 0.5)
+  x = static_cast<int>(0.5f + ld);
+  // CHECK-MESSAGES: [[@LINE-1]]:24: warning: casting (double + 0.5)
+  x = static_cast<int>(0.5 + f);
+  // CHECK-MESSAGES: [[@LINE-1]]:24: warning: casting (double + 0.5)
+  x = static_cast<int>(0.5f + f);
+  // CHECK-MESSAGES: [[@LINE-1]]:24: warning: casting (double + 0.5)
+
+  // Don't warn if constant is not 0.5.
+  x = (int)(d + 0.6);
+  x = (int)(0.6 + d);
+
+  // Don't warn if binary operator is not directly beneath cast.
+  x = (int)(1 + (0.5 + f));
+}
diff --git a/test/clang-tidy/misc-inefficient-algorithm.cpp b/test/clang-tidy/misc-inefficient-algorithm.cpp
new file mode 100644 (file)
index 0000000..75ca7d6
--- /dev/null
@@ -0,0 +1,162 @@
+// RUN: %check_clang_tidy %s misc-inefficient-algorithm %t
+
+namespace std {
+template <typename T> struct less {
+  bool operator()(const T &lhs, const T &rhs) { return lhs < rhs; }
+};
+
+template <typename T> struct greater {
+  bool operator()(const T &lhs, const T &rhs) { return lhs > rhs; }
+};
+
+struct iterator_type {};
+
+template <typename K, typename Cmp = less<K>> struct set {
+  typedef iterator_type iterator;
+  iterator find(const K &k);
+  unsigned count(const K &k);
+
+  iterator begin();
+  iterator end();
+  iterator begin() const;
+  iterator end() const;
+};
+
+struct other_iterator_type {};
+
+template <typename K, typename V, typename Cmp = less<K>> struct map {
+  typedef other_iterator_type iterator;
+  iterator find(const K &k);
+  unsigned count(const K &k);
+
+  iterator begin();
+  iterator end();
+  iterator begin() const;
+  iterator end() const;
+};
+
+template <typename K, typename V> struct multimap : map<K, V> {};
+template <typename K> struct unordered_set : set<K> {};
+template <typename K, typename V> struct unordered_map : map<K, V> {};
+template <typename K> struct unordered_multiset : set<K> {};
+template <typename K, typename V> struct unordered_multimap : map<K, V> {};
+
+template <typename K, typename Cmp = less<K>> struct multiset : set<K, Cmp> {};
+
+template <typename FwIt, typename K> FwIt find(FwIt, FwIt, const K &);
+
+template <typename FwIt, typename K, typename Cmp>
+FwIt find(FwIt, FwIt, const K &, Cmp);
+
+template <typename FwIt, typename Pred> FwIt find_if(FwIt, FwIt, Pred);
+
+template <typename FwIt, typename K> FwIt count(FwIt, FwIt, const K &);
+
+template <typename FwIt, typename K> FwIt lower_bound(FwIt, FwIt, const K &);
+
+template <typename FwIt, typename K, typename Ord>
+FwIt lower_bound(FwIt, FwIt, const K &, Ord);
+}
+
+#define FIND_IN_SET(x) find(x.begin(), x.end(), 10)
+// CHECK-FIXES: #define FIND_IN_SET(x) find(x.begin(), x.end(), 10)
+
+template <typename T> void f(const T &t) {
+  std::set<int> s;
+  find(s.begin(), s.end(), 46);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this STL algorithm call should be
+  // CHECK-FIXES: {{^  }}s.find(46);{{$}}
+
+  find(t.begin(), t.end(), 46);
+  // CHECK-FIXES: {{^  }}find(t.begin(), t.end(), 46);{{$}}
+}
+
+int main() {
+  std::set<int> s;
+  auto it = std::find(s.begin(), s.end(), 43);
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: this STL algorithm call should be replaced with a container method [misc-inefficient-algorithm]
+  // CHECK-FIXES: {{^  }}auto it = s.find(43);{{$}}
+  auto c = count(s.begin(), s.end(), 43);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: this STL algorithm call should be
+  // CHECK-FIXES: {{^  }}auto c = s.count(43);{{$}}
+
+#define SECOND(x, y, z) y
+  SECOND(q,std::count(s.begin(), s.end(), 22),w);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: this STL algorithm call should be
+  // CHECK-FIXES: {{^  }}SECOND(q,s.count(22),w);{{$}}
+
+  it = find_if(s.begin(), s.end(), [](int) { return false; });
+
+  std::multiset<int> ms;
+  find(ms.begin(), ms.end(), 46);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this STL algorithm call should be
+  // CHECK-FIXES: {{^  }}ms.find(46);{{$}}
+
+  const std::multiset<int> &msref = ms;
+  find(msref.begin(), msref.end(), 46);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this STL algorithm call should be
+  // CHECK-FIXES: {{^  }}msref.find(46);{{$}}
+
+  std::multiset<int> *msptr = &ms;
+  find(msptr->begin(), msptr->end(), 46);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this STL algorithm call should be
+  // CHECK-FIXES: {{^  }}msptr->find(46);{{$}}
+
+  it = std::find(s.begin(), s.end(), 43, std::greater<int>());
+  // CHECK-MESSAGES: :[[@LINE-1]]:42: warning: different comparers used in the algorithm and the container [misc-inefficient-algorithm]
+
+  FIND_IN_SET(s);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this STL algorithm call should be
+  // CHECK-FIXES: {{^  }}FIND_IN_SET(s);{{$}}
+
+  f(s);
+
+  std::unordered_set<int> us;
+  lower_bound(us.begin(), us.end(), 10);
+  // CHECK-FIXES: {{^  }}lower_bound(us.begin(), us.end(), 10);{{$}}
+  find(us.begin(), us.end(), 10);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this STL algorithm call should be
+  // CHECK-FIXES: {{^  }}us.find(10);{{$}}
+
+  std::unordered_multiset<int> ums;
+  find(ums.begin(), ums.end(), 10);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this STL algorithm call should be
+  // CHECK-FIXES: {{^  }}ums.find(10);{{$}}
+
+  std::map<int, int> intmap;
+  find(intmap.begin(), intmap.end(), 46);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this STL algorithm call should be
+  // CHECK-FIXES: {{^  }}find(intmap.begin(), intmap.end(), 46);{{$}}
+
+  std::multimap<int, int> intmmap;
+  find(intmmap.begin(), intmmap.end(), 46);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this STL algorithm call should be
+  // CHECK-FIXES: {{^  }}find(intmmap.begin(), intmmap.end(), 46);{{$}}
+
+  std::unordered_map<int, int> umap;
+  find(umap.begin(), umap.end(), 46);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this STL algorithm call should be
+  // CHECK-FIXES: {{^  }}find(umap.begin(), umap.end(), 46);{{$}}
+
+  std::unordered_multimap<int, int> ummap;
+  find(ummap.begin(), ummap.end(), 46);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this STL algorithm call should be
+  // CHECK-FIXES: {{^  }}find(ummap.begin(), ummap.end(), 46);{{$}}
+}
+
+struct Value {
+  int value;
+};
+
+struct Ordering {
+  bool operator()(const Value &lhs, const Value &rhs) const {
+    return lhs.value < rhs.value;
+  }
+  bool operator()(int lhs, const Value &rhs) const { return lhs < rhs.value; }
+};
+
+void g(std::set<Value, Ordering> container, int value) {
+  lower_bound(container.begin(), container.end(), value, Ordering());
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this STL algorithm call should be
+  // CHECK-FIXES: {{^  }}lower_bound(container.begin(), container.end(), value, Ordering());{{$}}
+}
diff --git a/test/clang-tidy/misc-lambda-function-name.cpp b/test/clang-tidy/misc-lambda-function-name.cpp
new file mode 100644 (file)
index 0000000..cc5aa2b
--- /dev/null
@@ -0,0 +1,41 @@
+// RUN: %check_clang_tidy %s misc-lambda-function-name %t
+
+void Foo(const char* a, const char* b, int c) {}
+
+#define FUNC_MACRO Foo(__func__, "", 0)
+#define FUNCTION_MACRO Foo(__FUNCTION__, "", 0)
+#define EMBED_IN_ANOTHER_MACRO1 FUNC_MACRO
+
+void Positives() {
+  [] { __func__; }();
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: inside a lambda, '__func__' expands to the name of the function call operator; consider capturing the name of the enclosing function explicitly [misc-lambda-function-name]
+  [] { __FUNCTION__; }();
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: inside a lambda, '__FUNCTION__' expands to the name of the function call operator; consider capturing the name of the enclosing function explicitly [misc-lambda-function-name]
+  [] { FUNC_MACRO; }();
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: inside a lambda, '__func__' expands to the name of the function call operator; consider capturing the name of the enclosing function explicitly [misc-lambda-function-name]
+  [] { FUNCTION_MACRO; }();
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: inside a lambda, '__FUNCTION__' expands to the name of the function call operator; consider capturing the name of the enclosing function explicitly [misc-lambda-function-name]
+  [] { EMBED_IN_ANOTHER_MACRO1; }();
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: inside a lambda, '__func__' expands to the name of the function call operator; consider capturing the name of the enclosing function explicitly [misc-lambda-function-name]
+}
+
+#define FUNC_MACRO_WITH_FILE_AND_LINE Foo(__func__, __FILE__, __LINE__)
+#define FUNCTION_MACRO_WITH_FILE_AND_LINE Foo(__FUNCTION__, __FILE__, __LINE__)
+#define EMBED_IN_ANOTHER_MACRO2 FUNC_MACRO_WITH_FILE_AND_LINE
+
+void Negatives() {
+  __func__;
+  __FUNCTION__;
+
+  // __PRETTY_FUNCTION__ should not trigger a warning because its value is
+  // actually potentially useful.
+  __PRETTY_FUNCTION__;
+  [] { __PRETTY_FUNCTION__; }();
+
+  // Don't warn if __func__/__FUNCTION is used inside a macro that also uses
+  // __FILE__ and __LINE__, on the assumption that __FILE__ and __LINE__ will
+  // be useful even if __func__/__FUNCTION__ is not.
+  [] { FUNC_MACRO_WITH_FILE_AND_LINE; }();
+  [] { FUNCTION_MACRO_WITH_FILE_AND_LINE; }();
+  [] { EMBED_IN_ANOTHER_MACRO2; }();
+}
diff --git a/test/clang-tidy/misc-macro-parentheses-cmdline.cpp b/test/clang-tidy/misc-macro-parentheses-cmdline.cpp
new file mode 100644 (file)
index 0000000..f7e8088
--- /dev/null
@@ -0,0 +1,10 @@
+// RUN: %check_clang_tidy %s misc-macro-parentheses %t -- -- -DVAL=0+0
+
+// The previous command-line is producing warnings and fixes with the source
+// locations from a virtual buffer. VAL is replaced by '0+0'.
+// Fixes could not be applied and should not be reported.
+int foo() { return VAL; }
+
+#define V 0+0
+int bar() { return V; }
+// CHECK-FIXES: #define V (0+0)
diff --git a/test/clang-tidy/misc-macro-parentheses.cpp b/test/clang-tidy/misc-macro-parentheses.cpp
new file mode 100644 (file)
index 0000000..11d7c7b
--- /dev/null
@@ -0,0 +1,49 @@
+// RUN: %check_clang_tidy %s misc-macro-parentheses %t
+
+#define BAD1              -1
+// CHECK-MESSAGES: :[[@LINE-1]]:27: warning: macro replacement list should be enclosed in parentheses [misc-macro-parentheses]
+#define BAD2              1+2
+// CHECK-MESSAGES: :[[@LINE-1]]:28: warning: macro replacement list should be enclosed in parentheses [misc-macro-parentheses]
+#define BAD3(A)           (A+1)
+// CHECK-MESSAGES: :[[@LINE-1]]:28: warning: macro argument should be enclosed in parentheses [misc-macro-parentheses]
+#define BAD4(x)           ((unsigned char)(x & 0xff))
+// CHECK-MESSAGES: :[[@LINE-1]]:44: warning: macro argument should be enclosed in parentheses [misc-macro-parentheses]
+#define BAD5(X)           A*B=(C*)X+2
+// CHECK-MESSAGES: :[[@LINE-1]]:35: warning: macro argument should be enclosed in parentheses [misc-macro-parentheses]
+
+#define GOOD1             1
+#define GOOD2             (1+2)
+#define GOOD3(A)          #A
+#define GOOD4(A,B)        A ## B
+#define GOOD5(T)          ((T*)0)
+#define GOOD6(B)          "A" B "C"
+#define GOOD7(b)          A b
+#define GOOD8(a)          a B
+#define GOOD9(type)       (type(123))
+#define GOOD10(car, ...)  car
+#define GOOD11            a[b+c]
+#define GOOD12(x)         a[x]
+#define GOOD13(x)         a.x
+#define GOOD14(x)         a->x
+#define GOOD15(x)         ({ int a = x; a+4; })
+#define GOOD16(x)         a_ ## x, b_ ## x = c_ ## x - 1,
+#define GOOD17            case 123: x=4+5; break;
+#define GOOD18(x)         ;x;
+#define GOOD19            ;-2;
+#define GOOD20            void*
+#define GOOD21(a)         case Fred::a:
+#define GOOD22(a)         if (verbose) return a;
+#define GOOD23(type)      (type::Field)
+#define GOOD24(t)         std::set<t> s
+#define GOOD25(t)         std::set<t,t,t> s
+#define GOOD26(x)         (a->*x)
+#define GOOD27(x)         (a.*x)
+#define GOOD28(x)         namespace x {int b;}
+#define GOOD29(...)       std::cout << __VA_ARGS__;
+#define GOOD30(args...)   std::cout << args;
+#define GOOD31(X)         A*X=2
+#define GOOD32(X)         std::vector<X>
+
+// These are allowed for now..
+#define MAYBE1            *12.34
+#define MAYBE2            <<3
diff --git a/test/clang-tidy/misc-macro-repeated-side-effects.c b/test/clang-tidy/misc-macro-repeated-side-effects.c
new file mode 100644 (file)
index 0000000..7b01cc6
--- /dev/null
@@ -0,0 +1,106 @@
+// RUN: %check_clang_tidy %s misc-macro-repeated-side-effects %t
+
+#define badA(x,y)  ((x)+((x)+(y))+(y))
+void bad(int ret, int a, int b) {
+  ret = badA(a++, b);
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: side effects in the 1st macro argument 'x' are repeated in macro expansion [misc-macro-repeated-side-effects]
+  ret = badA(++a, b);
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: side effects in the 1st macro argument 'x'
+  ret = badA(a--, b);
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: side effects in the 1st macro argument 'x'
+  ret = badA(--a, b);
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: side effects in the 1st macro argument 'x'
+  ret = badA(a, b++);
+  // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: side effects in the 2nd macro argument 'y'
+  ret = badA(a, ++b);
+  // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: side effects in the 2nd macro argument 'y'
+  ret = badA(a, b--);
+  // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: side effects in the 2nd macro argument 'y'
+  ret = badA(a, --b);
+  // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: side effects in the 2nd macro argument 'y'
+}
+
+
+#define MIN(A,B)     ((A) < (B) ? (A) : (B))                        // single ?:
+#define LIMIT(X,A,B) ((X) < (A) ? (A) : ((X) > (B) ? (B) : (X)))    // two ?:
+void question(int x) {
+  MIN(x++, 12);
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: side effects in the 1st macro argument 'A'
+  MIN(34, x++);
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: side effects in the 2nd macro argument 'B'
+  LIMIT(x++, 0, 100);
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: side effects in the 1st macro argument 'X'
+  LIMIT(20, x++, 100);
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: side effects in the 2nd macro argument 'A'
+  LIMIT(20, 0, x++);
+  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: side effects in the 3rd macro argument 'B'
+}
+
+// False positive: Repeated side effects is intentional.
+// It is hard to know when it's done by intention so right now we warn.
+#define UNROLL(A)    {A A}
+void fp1(int i) {
+  UNROLL({ i++; });
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: side effects in the 1st macro argument 'A'
+}
+
+// Do not produce a false positive on a strchr() macro. Explanation; Currently the '?'
+// triggers the test to bail out, because it cannot evaluate __builtin_constant_p(c).
+#  define strchrs(s, c) \
+  (__extension__ (__builtin_constant_p (c) && !__builtin_constant_p (s)              \
+                 && (c) == '\0'                                              \
+                 ? (char *) __rawmemchr (s, c)                               \
+                 : __builtin_strchr (s, c)))
+char* __rawmemchr(char* a, char b) {
+  return a;
+}
+void pass(char* pstr, char ch) {
+  strchrs(pstr, ch++); // No error.
+}
+
+// Check large arguments (t=20, u=21).
+#define largeA(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, x, y, z) \
+  ((a) + (a) + (b) + (b) + (c) + (c) + (d) + (d) + (e) + (e) + (f) + (f) + (g) + (g) +    \
+   (h) + (h) + (i) + (i) + (j) + (j) + (k) + (k) + (l) + (l) + (m) + (m) + (n) + (n) +    \
+   (o) + (o) + (p) + (p) + (q) + (q) + (r) + (r) + (s) + (s) + (t) + (t) + (u) + (u) +    \
+   (v) + (v) + (x) + (x) + (y) + (y) + (z) + (z))
+void large(int a) {
+  largeA(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, a++, 0, 0, 0, 0, 0, 0);
+  // CHECK-MESSAGES: :[[@LINE-1]]:64: warning: side effects in the 19th macro argument 's'
+  largeA(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, a++, 0, 0, 0, 0, 0);
+  // CHECK-MESSAGES: :[[@LINE-1]]:67: warning: side effects in the 20th macro argument 't'
+  largeA(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, a++, 0, 0, 0, 0);
+  // CHECK-MESSAGES: :[[@LINE-1]]:70: warning: side effects in the 21st macro argument 'u'
+}
+
+// Passing macro argument as argument to __builtin_constant_p and macros.
+#define builtinbad(x)      (__builtin_constant_p(x) + (x) + (x))
+#define builtingood1(x)    (__builtin_constant_p(x) + (x))
+#define builtingood2(x)    ((__builtin_constant_p(x) && (x)) || (x))
+#define macrobad(x)        (builtingood1(x) + (x) + (x))
+#define macrogood(x)       (builtingood1(x) + (x))
+void builtins(int ret, int a) {
+  ret += builtinbad(a++);
+  // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: side effects in the 1st macro argument 'x'
+
+  ret += builtingood1(a++);
+  ret += builtingood2(a++);
+
+  ret += macrobad(a++);
+  // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: side effects in the 1st macro argument 'x'
+
+  ret += macrogood(a++);
+}
+
+// Bail out for conditionals.
+#define condB(x,y)  if(x) {x=y;} else {x=y + 1;}
+void conditionals(int a, int b)
+{
+  condB(a, b++);
+}
+
+void log(const char *s, int v);
+#define LOG(val) log(#val, (val))
+void test_log(int a) {
+  LOG(a++);
+}
diff --git a/test/clang-tidy/misc-misplaced-const.c b/test/clang-tidy/misc-misplaced-const.c
new file mode 100644 (file)
index 0000000..cccf3a4
--- /dev/null
@@ -0,0 +1,45 @@
+// RUN: %check_clang_tidy %s misc-misplaced-const %t
+
+typedef int plain_i;
+typedef int *ip;
+typedef const int *cip;
+
+typedef void (*func_ptr)(void);
+
+void func(void) {
+  // ok
+  const int *i0 = 0;
+  const plain_i *i1 = 0;
+  const cip i2 = 0; // const applies to both pointer and pointee.
+
+  // Not ok
+  const ip i3 = 0;
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: 'i3' declared with a const-qualified typedef type; results in the type being 'int *const' instead of 'const int *'
+
+  ip const i4 = 0;
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: 'i4' declared with a const-qualified
+
+  const volatile ip i5 = 0;
+  // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: 'i5' declared with a const-qualified typedef type; results in the type being 'int *const volatile' instead of 'const int *volatile'
+}
+
+void func2(const plain_i *i1,
+           const cip i2,
+           const ip i3,
+           // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: 'i3' declared with a const-qualified
+           const int *i4) {
+}
+
+struct S {
+  const int *i0;
+  const plain_i *i1;
+  const cip i2;
+  const ip i3;
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: 'i3' declared with a const-qualified
+};
+
+// Function pointers should not be diagnosed because a function
+// pointer type can never be const.
+void func3(const func_ptr fp) {
+  const func_ptr fp2 = fp;
+}
diff --git a/test/clang-tidy/misc-misplaced-const.cpp b/test/clang-tidy/misc-misplaced-const.cpp
new file mode 100644 (file)
index 0000000..d7ea893
--- /dev/null
@@ -0,0 +1,44 @@
+// RUN: %check_clang_tidy %s misc-misplaced-const %t
+
+typedef int plain_i;
+typedef int *ip;
+typedef const int *cip;
+
+void func() {
+  if (const int *i = 0)
+    ;
+  if (const plain_i *i = 0)
+    ;
+  if (const cip i = 0)
+    ;
+
+  // CHECK-MESSAGES: :[[@LINE+1]]:16: warning: 'i' declared with a const-qualified typedef type; results in the type being 'int *const' instead of 'const int *'
+  if (const ip i = 0)
+    ;
+}
+
+template <typename Ty>
+struct S {
+  const Ty *i;
+  const Ty &i2;
+};
+
+template struct S<int>;
+template struct S<ip>; // ok
+template struct S<cip>;
+
+template <typename Ty>
+struct U {
+  const Ty *i;
+  const Ty &i2;
+};
+
+template struct U<int *>; // ok
+
+struct T {
+  typedef void (T::*PMF)();
+
+  void f() {
+    const PMF val = &T::f; // ok
+  }
+};
diff --git a/test/clang-tidy/misc-misplaced-widening-cast-explicit-only.cpp b/test/clang-tidy/misc-misplaced-widening-cast-explicit-only.cpp
new file mode 100644 (file)
index 0000000..6b236a7
--- /dev/null
@@ -0,0 +1,58 @@
+// RUN: %check_clang_tidy %s misc-misplaced-widening-cast %t -- -config="{CheckOptions: [{key: misc-misplaced-widening-cast.CheckImplicitCasts, value: 0}]}" --
+
+void func(long arg) {}
+
+void assign(int a, int b) {
+  long l;
+
+  l = a * b;
+  l = (long)(a * b);
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: either cast from 'int' to 'long' is ineffective, or there is loss of precision before the conversion [misc-misplaced-widening-cast]
+  l = (long)a * b;
+
+  l = a << 8;
+  l = (long)(a << 8);
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: either cast from 'int' to 'long'
+  l = (long)b << 8;
+
+  l = static_cast<long>(a * b);
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: either cast from 'int' to 'long'
+}
+
+void compare(int a, int b, long c) {
+  bool l;
+
+  l = a * b == c;
+  l = c == a * b;
+  l = (long)(a * b) == c;
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: either cast from 'int' to 'long'
+  l = c == (long)(a * b);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: either cast from 'int' to 'long'
+  l = (long)a * b == c;
+  l = c == (long)a * b;
+}
+
+void init(unsigned int n) {
+  long l1 = n << 8;
+  long l2 = (long)(n << 8);
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: either cast from 'unsigned int' to 'long'
+  long l3 = (long)n << 8;
+}
+
+void call(unsigned int n) {
+  func(n << 8);
+  func((long)(n << 8));
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: either cast from 'unsigned int' to 'long'
+  func((long)n << 8);
+}
+
+long ret(int a) {
+  if (a < 0) {
+    return a * 1000;
+  } else if (a > 0) {
+    return (long)(a * 1000);
+    // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: either cast from 'int' to 'long'
+  } else {
+    return (long)a * 1000;
+  }
+}
diff --git a/test/clang-tidy/misc-misplaced-widening-cast-implicit-enabled.cpp b/test/clang-tidy/misc-misplaced-widening-cast-implicit-enabled.cpp
new file mode 100644 (file)
index 0000000..9e7cd81
--- /dev/null
@@ -0,0 +1,101 @@
+// RUN: %check_clang_tidy %s misc-misplaced-widening-cast %t -- -config="{CheckOptions: [{key: misc-misplaced-widening-cast.CheckImplicitCasts, value: 1}]}" --
+
+void func(long arg) {}
+
+void assign(int a, int b) {
+  long l;
+
+  l = a * b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: either cast from 'int' to 'long' is ineffective, or there is loss of precision before the conversion [misc-misplaced-widening-cast]
+  l = (long)(a * b);
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: either cast from 'int' to 'long'
+  l = (long)a * b;
+
+  l = a << 8;
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: either cast from 'int' to 'long'
+  l = (long)(a << 8);
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: either cast from 'int' to 'long'
+  l = (long)b << 8;
+
+  l = static_cast<long>(a * b);
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: either cast from 'int' to 'long'
+}
+
+void compare(int a, int b, long c) {
+  bool l;
+
+  l = a * b == c;
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: either cast from 'int' to 'long'
+  l = c == a * b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: either cast from 'int' to 'long'
+  l = (long)(a * b) == c;
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: either cast from 'int' to 'long'
+  l = c == (long)(a * b);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: either cast from 'int' to 'long'
+  l = (long)a * b == c;
+  l = c == (long)a * b;
+}
+
+void init(unsigned int n) {
+  long l1 = n << 8;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: either cast from 'unsigned int' to 'long'
+  long l2 = (long)(n << 8);
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: either cast from 'unsigned int' to 'long'
+  long l3 = (long)n << 8;
+}
+
+void call(unsigned int n) {
+  func(n << 8);
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: either cast from 'unsigned int' to 'long'
+  func((long)(n << 8));
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: either cast from 'unsigned int' to 'long'
+  func((long)n << 8);
+}
+
+long ret(int a) {
+  if (a < 0) {
+    return a * 1000;
+    // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: either cast from 'int' to 'long'
+  } else if (a > 0) {
+    return (long)(a * 1000);
+    // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: either cast from 'int' to 'long'
+  } else {
+    return (long)a * 1000;
+  }
+}
+
+void dontwarn1(unsigned char a, int i, unsigned char *p) {
+  long l;
+  // The result is a 9 bit value, there is no truncation in the implicit cast.
+  l = (long)(a + 15);
+  // The result is a 12 bit value, there is no truncation in the implicit cast.
+  l = (long)(a << 4);
+  // The result is a 3 bit value, there is no truncation in the implicit cast.
+  l = (long)((i % 5) + 1);
+  // The result is a 16 bit value, there is no truncation in the implicit cast.
+  l = (long)(((*p) << 8) + *(p + 1));
+}
+
+template <class T> struct DontWarn2 {
+  void assign(T a, T b) {
+    long l;
+    l = (long)(a * b);
+  }
+};
+DontWarn2<int> DW2;
+
+// Cast is not suspicious when casting macro.
+#define A  (X<<2)
+long macro1(int X) {
+  return (long)A;
+}
+
+// Don't warn about cast in macro.
+#define B(X,Y)   (long)(X*Y)
+long macro2(int x, int y) {
+  return B(x,y);
+}
+
+void floatingpoint(float a, float b) {
+  double d = (double)(a * b); // Currently we don't warn for this.
+}
diff --git a/test/clang-tidy/misc-move-const-arg.cpp b/test/clang-tidy/misc-move-const-arg.cpp
new file mode 100644 (file)
index 0000000..29b2b92
--- /dev/null
@@ -0,0 +1,176 @@
+// RUN: %check_clang_tidy %s misc-move-const-arg %t
+
+namespace std {
+template <typename> struct remove_reference;
+
+template <typename _Tp> struct remove_reference { typedef _Tp type; };
+
+template <typename _Tp> struct remove_reference<_Tp &> { typedef _Tp type; };
+
+template <typename _Tp> struct remove_reference<_Tp &&> { typedef _Tp type; };
+
+template <typename _Tp>
+constexpr typename std::remove_reference<_Tp>::type &&move(_Tp &&__t);
+
+} // namespace std
+
+class A {
+public:
+  A() {}
+  A(const A &rhs) {}
+  A(A &&rhs) {}
+};
+
+int f1() {
+  return std::move(42);
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: std::move of the expression of the trivially-copyable type 'int' has no effect; remove std::move() [misc-move-const-arg]
+  // CHECK-FIXES: return 42;
+}
+
+int f2(int x2) {
+  return std::move(x2);
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: std::move of the variable 'x2' of the trivially-copyable type 'int'
+  // CHECK-FIXES: return x2;
+}
+
+int *f3(int *x3) {
+  return std::move(x3);
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: std::move of the variable 'x3' of the trivially-copyable type 'int *'
+  // CHECK-FIXES: return x3;
+}
+
+A f4(A x4) { return std::move(x4); }
+
+A f5(const A x5) {
+  return std::move(x5);
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: std::move of the const variable 'x5' has no effect; remove std::move() or make the variable non-const [misc-move-const-arg]
+  // CHECK-FIXES: return x5;
+}
+
+template <typename T> T f6(const T x6) { return std::move(x6); }
+
+void f7() { int a = f6(10); }
+
+#define M1(x) x
+void f8() {
+  const A a;
+  M1(A b = std::move(a);)
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: std::move of the const variable 'a' has no effect; remove std::move() or make the variable non-const
+  // CHECK-FIXES: M1(A b = a;)
+}
+
+#define M2(x) std::move(x)
+int f9() { return M2(1); }
+
+template <typename T> T f10(const int x10) {
+  return std::move(x10);
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: std::move of the const variable 'x10' of the trivially-copyable type 'const int' has no effect; remove std::move() [misc-move-const-arg]
+  // CHECK-FIXES: return x10;
+}
+void f11() {
+  f10<int>(1);
+  f10<double>(1);
+}
+
+class NoMoveSemantics {
+ public:
+  NoMoveSemantics();
+  NoMoveSemantics(const NoMoveSemantics &);
+
+  NoMoveSemantics &operator=(const NoMoveSemantics &);
+};
+
+void callByConstRef(const NoMoveSemantics &);
+void callByConstRef(int i, const NoMoveSemantics &);
+
+void moveToConstReferencePositives() {
+  NoMoveSemantics obj;
+
+  // Basic case.
+  callByConstRef(std::move(obj));
+  // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: passing result of std::move() as
+  // CHECK-FIXES: callByConstRef(obj);
+
+  // Also works for second argument.
+  callByConstRef(1, std::move(obj));
+  // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: passing result of std::move() as
+  // CHECK-FIXES: callByConstRef(1, obj);
+
+  // Works if std::move() applied to a temporary.
+  callByConstRef(std::move(NoMoveSemantics()));
+  // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: passing result of std::move() as
+  // CHECK-FIXES: callByConstRef(NoMoveSemantics());
+
+  // Works if calling a copy constructor.
+  NoMoveSemantics other(std::move(obj));
+  // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: passing result of std::move() as
+  // CHECK-FIXES: NoMoveSemantics other(obj);
+
+  // Works if calling assignment operator.
+  other = std::move(obj);
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: passing result of std::move() as
+  // CHECK-FIXES: other = obj;
+}
+
+class MoveSemantics {
+ public:
+  MoveSemantics();
+  MoveSemantics(MoveSemantics &&);
+
+  MoveSemantics &operator=(MoveSemantics &&);
+};
+
+void callByValue(MoveSemantics);
+
+void callByRValueRef(MoveSemantics &&);
+
+template <class T>
+void templateFunction(T obj) {
+  T other = std::move(obj);
+}
+
+#define M3(T, obj)            \
+  do {                        \
+    T other = std::move(obj); \
+  } while (true)
+
+#define CALL(func) (func)()
+
+void moveToConstReferenceNegatives() {
+  // No warning when actual move takes place.
+  MoveSemantics move_semantics;
+  callByValue(std::move(move_semantics));
+  callByRValueRef(std::move(move_semantics));
+  MoveSemantics other(std::move(move_semantics));
+  other = std::move(move_semantics);
+
+  // No warning if std::move() not used.
+  NoMoveSemantics no_move_semantics;
+  callByConstRef(no_move_semantics);
+
+  // No warning if instantiating a template.
+  templateFunction(no_move_semantics);
+
+  // No warning inside of macro expansions.
+  M3(NoMoveSemantics, no_move_semantics);
+
+  // No warning inside of macro expansion, even if the macro expansion is inside
+  // a lambda that is, in turn, an argument to a macro.
+  CALL([no_move_semantics] { M3(NoMoveSemantics, no_move_semantics); });
+
+  auto lambda = [] {};
+  auto lambda2 = std::move(lambda);
+}
+
+class MoveOnly {
+public:
+  MoveOnly(const MoveOnly &other) = delete;
+  MoveOnly &operator=(const MoveOnly &other) = delete;
+  MoveOnly(MoveOnly &&other) = default;
+  MoveOnly &operator=(MoveOnly &&other) = default;
+};
+template <class T>
+void Q(T);
+void moveOnlyNegatives(MoveOnly val) {
+  Q(std::move(val));
+}
diff --git a/test/clang-tidy/misc-move-constructor-init.cpp b/test/clang-tidy/misc-move-constructor-init.cpp
new file mode 100644 (file)
index 0000000..104c17a
--- /dev/null
@@ -0,0 +1,154 @@
+// RUN: %check_clang_tidy %s misc-move-constructor-init,modernize-pass-by-value %t -- \
+// RUN: -config='{CheckOptions: \
+// RUN:  [{key: modernize-pass-by-value.ValuesOnly, value: 1}]}' \
+// RUN: -- -std=c++11 -isystem %S/Inputs/Headers
+
+#include <s.h>
+
+// CHECK-FIXES: #include <utility>
+
+template <class T> struct remove_reference      {typedef T type;};
+template <class T> struct remove_reference<T&>  {typedef T type;};
+template <class T> struct remove_reference<T&&> {typedef T type;};
+
+template <typename T>
+typename remove_reference<T>::type&& move(T&& arg) {
+  return static_cast<typename remove_reference<T>::type&&>(arg);
+}
+
+struct C {
+  C() = default;
+  C(const C&) = default;
+};
+
+struct B {
+  B() {}
+  B(const B&) {}
+  B(B &&) {}
+};
+
+struct D : B {
+  D() : B() {}
+  D(const D &RHS) : B(RHS) {}
+  // CHECK-MESSAGES: :[[@LINE+3]]:16: warning: move constructor initializes base class by calling a copy constructor [misc-move-constructor-init]
+  // CHECK-MESSAGES: 26:3: note: copy constructor being called
+  // CHECK-MESSAGES: 27:3: note: candidate move constructor here
+  D(D &&RHS) : B(RHS) {}
+};
+
+struct E : B {
+  E() : B() {}
+  E(const E &RHS) : B(RHS) {}
+  E(E &&RHS) : B(move(RHS)) {} // ok
+};
+
+struct F {
+  C M;
+
+  F(F &&) : M(C()) {} // ok
+};
+
+struct G {
+  G() = default;
+  G(const G&) = default;
+  G(G&&) = delete;
+};
+
+struct H : G {
+  H() = default;
+  H(const H&) = default;
+  H(H &&RHS) : G(RHS) {} // ok
+};
+
+struct I {
+  I(const I &) = default; // suppresses move constructor creation
+};
+
+struct J : I {
+  J(J &&RHS) : I(RHS) {} // ok
+};
+
+struct K {}; // Has implicit copy and move constructors, is trivially copyable
+struct L : K {
+  L(L &&RHS) : K(RHS) {} // ok
+};
+
+struct M {
+  B Mem;
+  // CHECK-MESSAGES: :[[@LINE+1]]:16: warning: move constructor initializes class member by calling a copy constructor [misc-move-constructor-init]
+  M(M &&RHS) : Mem(RHS.Mem) {}
+};
+
+struct N {
+  B Mem;
+  N(N &&RHS) : Mem(move(RHS.Mem)) {}
+};
+
+struct O {
+  O(O&& other) : b(other.b) {} // ok
+  const B b;
+};
+
+struct P {
+  P(O&& other) : b(other.b) {} // ok
+  B b;
+};
+
+struct Movable {
+  Movable(Movable &&) = default;
+  Movable(const Movable &) = default;
+  Movable &operator=(const Movable &) = default;
+  ~Movable() {}
+};
+
+struct TriviallyCopyable {
+  TriviallyCopyable() = default;
+  TriviallyCopyable(TriviallyCopyable &&) = default;
+  TriviallyCopyable(const TriviallyCopyable &) = default;
+};
+
+struct Positive {
+  Positive(Movable M) : M_(M) {}
+  // CHECK-MESSAGES: [[@LINE-1]]:12: warning: pass by value and use std::move [modernize-pass-by-value]
+  // CHECK-FIXES: Positive(Movable M) : M_(std::move(M)) {}
+  Movable M_;
+};
+
+struct NegativeMultipleInitializerReferences {
+  NegativeMultipleInitializerReferences(Movable M) : M_(M), n_(M) {}
+  Movable M_;
+  Movable n_;
+};
+
+struct NegativeReferencedInConstructorBody {
+  NegativeReferencedInConstructorBody(Movable M) : M_(M) { M_ = M; }
+  Movable M_;
+};
+
+struct NegativeParamTriviallyCopyable {
+  NegativeParamTriviallyCopyable(TriviallyCopyable T) : T_(T) {}
+  NegativeParamTriviallyCopyable(int I) : I_(I) {}
+
+  TriviallyCopyable T_;
+  int I_;
+};
+
+struct NegativeNotPassedByValue {
+  // This const ref constructor isn't warned about because the ValuesOnly option is set.
+  NegativeNotPassedByValue(const Movable &M) : M_(M) {}
+  NegativeNotPassedByValue(const Movable M) : M_(M) {}
+  NegativeNotPassedByValue(Movable &M) : M_(M) {}
+  NegativeNotPassedByValue(Movable *M) : M_(*M) {}
+  NegativeNotPassedByValue(const Movable *M) : M_(*M) {}
+  Movable M_;
+};
+
+struct Immovable {
+  Immovable(const Immovable &) = default;
+  Immovable(Immovable &&) = delete;
+};
+
+struct NegativeImmovableParameter {
+  NegativeImmovableParameter(Immovable I) : I_(I) {}
+  Immovable I_;
+};
diff --git a/test/clang-tidy/misc-move-forwarding-reference.cpp b/test/clang-tidy/misc-move-forwarding-reference.cpp
new file mode 100644 (file)
index 0000000..a0307dd
--- /dev/null
@@ -0,0 +1,125 @@
+// RUN: %check_clang_tidy %s misc-move-forwarding-reference %t -- -- -std=c++14 -fno-delayed-template-parsing
+
+namespace std {
+template <typename> struct remove_reference;
+
+template <typename _Tp> struct remove_reference { typedef _Tp type; };
+
+template <typename _Tp> struct remove_reference<_Tp &> { typedef _Tp type; };
+
+template <typename _Tp> struct remove_reference<_Tp &&> { typedef _Tp type; };
+
+template <typename _Tp>
+constexpr typename std::remove_reference<_Tp>::type &&move(_Tp &&__t);
+
+} // namespace std
+
+// Standard case.
+template <typename T, typename U> void f1(U &&SomeU) {
+  T SomeT(std::move(SomeU));
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: forwarding reference passed to
+  // CHECK-FIXES: T SomeT(std::forward<U>(SomeU));
+}
+
+// Ignore parentheses around the argument to std::move().
+template <typename T, typename U> void f2(U &&SomeU) {
+  T SomeT(std::move((SomeU)));
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: forwarding reference passed to
+  // CHECK-FIXES: T SomeT(std::forward<U>((SomeU)));
+}
+
+// Handle the case correctly where std::move() is being used through a using
+// declaration.
+template <typename T, typename U> void f3(U &&SomeU) {
+  using std::move;
+  T SomeT(move(SomeU));
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: forwarding reference passed to
+  // CHECK-FIXES: T SomeT(std::forward<U>(SomeU));
+}
+
+// Handle the case correctly where a global specifier is prepended to
+// std::move().
+template <typename T, typename U> void f4(U &&SomeU) {
+  T SomeT(::std::move(SomeU));
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: forwarding reference passed to
+  // CHECK-FIXES: T SomeT(::std::forward<U>(SomeU));
+}
+
+// Create a correct fix if there are spaces around the scope resolution
+// operator.
+template <typename T, typename U> void f5(U &&SomeU) {
+  {
+    T SomeT(::  std  ::  move(SomeU));
+    // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: forwarding reference passed to
+    // CHECK-FIXES: T SomeT(::std::forward<U>(SomeU));
+  }
+  {
+    T SomeT(std  ::  move(SomeU));
+    // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: forwarding reference passed to
+    // CHECK-FIXES: T SomeT(std::forward<U>(SomeU));
+  }
+}
+
+// Ignore const rvalue reference parameters.
+template <typename T, typename U> void f6(const U &&SomeU) {
+  T SomeT(std::move(SomeU));
+}
+
+// Ignore the case where the argument to std::move() is a lambda parameter (and
+// thus not actually a parameter of the function template).
+template <typename T, typename U> void f7() {
+  [](U &&SomeU) { T SomeT(std::move(SomeU)); };
+}
+
+// Ignore the case where the argument is a lvalue reference.
+template <typename T, typename U> void f8(U &SomeU) {
+  T SomeT(std::move(SomeU));
+}
+
+// Ignore the case where the template parameter is a class template parameter
+// (i.e. no template argument deduction is taking place).
+template <typename T, typename U> class SomeClass {
+  void f(U &&SomeU) { T SomeT(std::move(SomeU)); }
+};
+
+// Ignore the case where the function parameter in the template isn't an rvalue
+// reference but the template argument is explicitly set to be an rvalue
+// reference.
+class A {};
+template <typename T> void foo(T);
+void f8() {
+  A a;
+  foo<A &&>(std::move(a));
+}
+
+// A warning is output, but no fix is suggested, if a macro is used to rename
+// std::move.
+#define MOVE(x) std::move((x))
+template <typename T, typename U> void f9(U &&SomeU) {
+  T SomeT(MOVE(SomeU));
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: forwarding reference passed to
+}
+
+// Same result if the argument is passed outside of the macro.
+#undef MOVE
+#define MOVE std::move
+template <typename T, typename U> void f10(U &&SomeU) {
+  T SomeT(MOVE(SomeU));
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: forwarding reference passed to
+}
+
+// Same result if the macro does not include the "std" namespace.
+#undef MOVE
+#define MOVE move
+template <typename T, typename U> void f11(U &&SomeU) {
+  T SomeT(std::MOVE(SomeU));
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: forwarding reference passed to
+}
+
+// Handle the case correctly where the forwarding reference is a parameter of a
+// generic lambda.
+template <typename T> void f12() {
+  [] (auto&& x) { T SomeT(std::move(x)); };
+  // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: forwarding reference passed to
+  // CHECK-FIXES: [] (auto&& x) { T SomeT(std::forward<decltype(x)>(x)); }
+}
diff --git a/test/clang-tidy/misc-multiple-statement-macro.cpp b/test/clang-tidy/misc-multiple-statement-macro.cpp
new file mode 100644 (file)
index 0000000..c943ee7
--- /dev/null
@@ -0,0 +1,85 @@
+// RUN: %check_clang_tidy %s misc-multiple-statement-macro %t
+
+void F();
+
+#define BAD_MACRO(x) \
+  F();               \
+  F()
+
+#define GOOD_MACRO(x) \
+  do {                \
+    F();              \
+    F();              \
+  } while (0)
+
+#define GOOD_MACRO2(x) F()
+
+#define GOOD_MACRO3(x) F();
+
+#define MACRO_ARG_MACRO(X) \
+  if (54)                  \
+  X(2)
+
+#define ALL_IN_MACRO(X) \
+  if (43)               \
+    F();                \
+  F()
+
+#define GOOD_NESTED(x)   \
+  if (x)            \
+    GOOD_MACRO3(x); \
+  F();
+
+#define IF(x) if(x)
+
+void positives() {
+  if (1)
+    BAD_MACRO(1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: multiple statement macro used without braces; some statements will be unconditionally executed [misc-multiple-statement-macro]
+  if (1) {
+  } else
+    BAD_MACRO(1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: multiple statement macro used
+  while (1)
+    BAD_MACRO(1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: multiple statement macro used
+  for (;;)
+    BAD_MACRO(1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: multiple statement macro used
+
+  MACRO_ARG_MACRO(BAD_MACRO);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: multiple statement macro used
+  MACRO_ARG_MACRO(F(); int);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: multiple statement macro used
+  IF(1) BAD_MACRO(1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: multiple statement macro used
+}
+
+void negatives() {
+  if (1) {
+    BAD_MACRO(1);
+  } else {
+    BAD_MACRO(1);
+  }
+  while (1) {
+    BAD_MACRO(1);
+  }
+  for (;;) {
+    BAD_MACRO(1);
+  }
+
+  if (1)
+    GOOD_MACRO(1);
+  if (1) {
+    GOOD_MACRO(1);
+  }
+  if (1)
+    GOOD_MACRO2(1);
+  if (1)
+    GOOD_MACRO3(1);
+
+  MACRO_ARG_MACRO(GOOD_MACRO);
+  ALL_IN_MACRO(1);
+
+  IF(1) GOOD_MACRO(1);
+}
diff --git a/test/clang-tidy/misc-new-delete-overloads-sized-dealloc.cpp b/test/clang-tidy/misc-new-delete-overloads-sized-dealloc.cpp
new file mode 100644 (file)
index 0000000..2ba60e4
--- /dev/null
@@ -0,0 +1,20 @@
+// RUN: %check_clang_tidy %s misc-new-delete-overloads %t -- -- -std=c++14 -fsized-deallocation
+
+typedef decltype(sizeof(int)) size_t;
+
+struct S {
+  // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: declaration of 'operator delete' has no matching declaration of 'operator new' at the same scope [misc-new-delete-overloads]
+  void operator delete(void *ptr, size_t) noexcept; // not a placement delete
+};
+
+struct T {
+  // Because we have enabled sized deallocations explicitly, this new/delete
+  // pair matches.
+  void *operator new(size_t size) noexcept;
+  void operator delete(void *ptr, size_t) noexcept; // ok because sized deallocation is enabled
+};
+
+// While we're here, check that global operator delete with no operator new
+// is also matched.
+// CHECK-MESSAGES: :[[@LINE+1]]:6: warning: declaration of 'operator delete' has no matching declaration of 'operator new' at the same scope
+void operator delete(void *ptr) noexcept;
diff --git a/test/clang-tidy/misc-new-delete-overloads.cpp b/test/clang-tidy/misc-new-delete-overloads.cpp
new file mode 100644 (file)
index 0000000..3e60537
--- /dev/null
@@ -0,0 +1,81 @@
+// RUN: %check_clang_tidy %s misc-new-delete-overloads %t -- -- -std=c++14
+
+typedef decltype(sizeof(int)) size_t;
+
+struct S {
+  // CHECK-MESSAGES: :[[@LINE+1]]:9: warning: declaration of 'operator new' has no matching declaration of 'operator delete' at the same scope [misc-new-delete-overloads]
+  void *operator new(size_t size) noexcept;
+  // CHECK-MESSAGES: :[[@LINE+1]]:9: warning: declaration of 'operator new[]' has no matching declaration of 'operator delete[]' at the same scope
+  void *operator new[](size_t size) noexcept;
+};
+
+// CHECK-MESSAGES: :[[@LINE+1]]:7: warning: declaration of 'operator new' has no matching declaration of 'operator delete' at the same scope
+void *operator new(size_t size) noexcept(false);
+
+struct T {
+  // Sized deallocations are not enabled by default, and so this new/delete pair
+  // does not match. However, we expect only one warning, for the new, because
+  // the operator delete is a placement delete and we do not warn on mismatching
+  // placement operations.
+  // CHECK-MESSAGES: :[[@LINE+1]]:9: warning: declaration of 'operator new' has no matching declaration of 'operator delete' at the same scope
+  void *operator new(size_t size) noexcept;
+  void operator delete(void *ptr, size_t) noexcept; // ok only if sized deallocation is enabled
+};
+
+struct U {
+  void *operator new(size_t size) noexcept;
+  void operator delete(void *ptr) noexcept;
+
+  void *operator new[](size_t) noexcept;
+  void operator delete[](void *) noexcept;
+};
+
+struct Z {
+  // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: declaration of 'operator delete' has no matching declaration of 'operator new' at the same scope
+  void operator delete(void *ptr) noexcept;
+  // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: declaration of 'operator delete[]' has no matching declaration of 'operator new[]' at the same scope
+  void operator delete[](void *ptr) noexcept;
+};
+
+struct A {
+  void *operator new(size_t size, Z) noexcept; // ok, placement new
+};
+
+struct B {
+  void operator delete(void *ptr, A) noexcept; // ok, placement delete
+};
+
+// It is okay to have a class with an inaccessible free store operator.
+struct C {
+  void *operator new(size_t, A) noexcept; // ok, placement new
+private:
+  void operator delete(void *) noexcept;
+};
+
+// It is also okay to have a class with a delete free store operator.
+struct D {
+  void *operator new(size_t, A) noexcept; // ok, placement new
+  void operator delete(void *) noexcept = delete;
+};
+
+struct E : U {
+  void *operator new(size_t) noexcept; // okay, we inherit operator delete from U
+};
+
+struct F : S {
+  // CHECK-MESSAGES: :[[@LINE+1]]:9: warning: declaration of 'operator new' has no matching declaration of 'operator delete' at the same scope
+  void *operator new(size_t) noexcept;
+};
+
+class G {
+  void operator delete(void *) noexcept;
+};
+
+struct H : G {
+  // CHECK-MESSAGES: :[[@LINE+1]]:9: warning: declaration of 'operator new' has no matching declaration of 'operator delete' at the same scope
+  void *operator new(size_t) noexcept; // base class operator is inaccessible
+};
+
+template <typename Base> struct Derived : Base {
+  void operator delete(void *);
+};
diff --git a/test/clang-tidy/misc-noexcept-move-constructor.cpp b/test/clang-tidy/misc-noexcept-move-constructor.cpp
new file mode 100644 (file)
index 0000000..b8154ec
--- /dev/null
@@ -0,0 +1,54 @@
+// RUN: %check_clang_tidy %s misc-noexcept-move-constructor %t
+
+class A {
+  A(A &&);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: move constructors should be marked noexcept [misc-noexcept-move-constructor]
+  A &operator=(A &&);
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: move assignment operators should
+};
+
+struct B {
+  static constexpr bool kFalse = false;
+  B(B &&) noexcept(kFalse);
+  // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: noexcept specifier on the move constructor evaluates to 'false' [misc-noexcept-move-constructor]
+};
+
+class OK {};
+
+void f() {
+  OK a;
+  a = OK();
+}
+
+class OK1 {
+ public:
+  OK1();
+  OK1(const OK1 &);
+  OK1(OK1&&) noexcept;
+  OK1 &operator=(OK1 &&) noexcept;
+  void f();
+  void g() noexcept;
+};
+
+class OK2 {
+  static constexpr bool kTrue = true;
+
+public:
+  OK2(OK2 &&) noexcept(true) {}
+  OK2 &operator=(OK2 &&) noexcept(kTrue) { return *this; }
+};
+
+struct OK3 {
+  OK3(OK3 &&) noexcept(false) {}
+  OK3 &operator=(OK3 &&) = delete;
+};
+
+struct OK4 {
+  OK4(OK4 &&) noexcept = default;
+  OK4 &operator=(OK4 &&) noexcept = default;
+};
+
+struct OK5 {
+  OK5(OK5 &&) noexcept(true) = default;
+  OK5 &operator=(OK5 &&) noexcept(true) = default;
+};
diff --git a/test/clang-tidy/misc-non-copyable-objects.c b/test/clang-tidy/misc-non-copyable-objects.c
new file mode 100644 (file)
index 0000000..ac6198e
--- /dev/null
@@ -0,0 +1,43 @@
+// RUN: %check_clang_tidy %s misc-non-copyable-objects %t
+
+typedef struct FILE {} FILE;
+typedef struct pthread_cond_t {} pthread_cond_t;
+typedef int pthread_mutex_t;
+
+// CHECK-MESSAGES: :[[@LINE+1]]:13: warning: 'f' declared as type 'FILE', which is unsafe to copy; did you mean 'FILE *'? [misc-non-copyable-objects]
+void g(FILE f);
+// CHECK-MESSAGES: :[[@LINE+1]]:24: warning: 'm' declared as type 'pthread_mutex_t', which is unsafe to copy; did you mean 'pthread_mutex_t *'?
+void h(pthread_mutex_t m);
+// CHECK-MESSAGES: :[[@LINE+1]]:23: warning: 'c' declared as type 'pthread_cond_t', which is unsafe to copy; did you mean 'pthread_cond_t *'?
+void i(pthread_cond_t c);
+
+struct S {
+  pthread_cond_t c; // ok
+  // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: 'f' declared as type 'FILE', which is unsafe to copy; did you mean 'FILE *'?
+  FILE f;
+};
+
+void func(FILE *f) {
+  // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: 'f1' declared as type 'FILE', which is unsafe to copy; did you mean 'FILE *'?
+  FILE f1; // match
+  // CHECK-MESSAGES: :[[@LINE+2]]:8: warning: 'f2' declared as type 'FILE', which is unsafe to copy; did you mean 'FILE *'?
+  // CHECK-MESSAGES: :[[@LINE+1]]:13: warning: expression has opaque data structure type 'FILE'; type should only be used as a pointer and not dereferenced
+  FILE f2 = *f;
+  // CHECK-MESSAGES: :[[@LINE+1]]:15: warning: 'f3' declared as type 'FILE', which is unsafe to copy; did you mean 'FILE *'?
+  struct FILE f3;
+  // CHECK-MESSAGES: :[[@LINE+1]]:16: warning: expression has opaque data structure type 'FILE'; type should only be used as a pointer and not dereferenced
+  (void)sizeof(*f);
+  (void)sizeof(FILE);
+  // CHECK-MESSAGES: :[[@LINE+1]]:5: warning: expression has opaque data structure type 'FILE'; type should only be used as a pointer and not dereferenced
+  g(*f);
+
+  pthread_mutex_t m; // ok
+  h(m); // ok
+
+  pthread_cond_t c; // ok
+  i(c); // ok
+
+  pthread_mutex_t *m1 = &m; // ok
+  // CHECK-MESSAGES: :[[@LINE+1]]:5: warning: expression has opaque data structure type 'pthread_mutex_t'; type should only be used as a pointer and not dereferenced
+  h(*m1);
+}
diff --git a/test/clang-tidy/misc-non-copyable-objects.cpp b/test/clang-tidy/misc-non-copyable-objects.cpp
new file mode 100644 (file)
index 0000000..3d716f4
--- /dev/null
@@ -0,0 +1,26 @@
+// RUN: %check_clang_tidy %s misc-non-copyable-objects %t
+
+namespace std {
+typedef struct FILE {} FILE;
+}
+using namespace std;
+
+// CHECK-MESSAGES: :[[@LINE+1]]:18: warning: 'f' declared as type 'FILE', which is unsafe to copy; did you mean 'FILE *'? [misc-non-copyable-objects]
+void g(std::FILE f);
+
+struct S {
+  // CHECK-MESSAGES: :[[@LINE+1]]:10: warning: 'f' declared as type 'FILE', which is unsafe to copy; did you mean 'FILE *'?
+  ::FILE f;
+};
+
+void func(FILE *f) {
+  // CHECK-MESSAGES: :[[@LINE+1]]:13: warning: 'f1' declared as type 'FILE', which is unsafe to copy; did you mean 'FILE *'?
+  std::FILE f1; // match
+  // CHECK-MESSAGES: :[[@LINE+2]]:10: warning: 'f2' declared as type 'FILE', which is unsafe to copy; did you mean 'FILE *'?
+  // CHECK-MESSAGES: :[[@LINE+1]]:15: warning: expression has opaque data structure type 'FILE'; type should only be used as a pointer and not dereferenced
+  ::FILE f2 = *f; // match, match
+  // CHECK-MESSAGES: :[[@LINE+1]]:15: warning: 'f3' declared as type 'FILE', which is unsafe to copy; did you mean 'FILE *'?
+  struct FILE f3; // match
+  // CHECK-MESSAGES: :[[@LINE+1]]:16: warning: expression has opaque data structure type 'FILE'; type should only be used as a pointer and not dereferenced
+  (void)sizeof(*f); // match
+}
diff --git a/test/clang-tidy/misc-redundant-expression.cpp b/test/clang-tidy/misc-redundant-expression.cpp
new file mode 100644 (file)
index 0000000..e865ba0
--- /dev/null
@@ -0,0 +1,489 @@
+// RUN: %check_clang_tidy %s misc-redundant-expression %t -- -- -std=c++11
+
+typedef __INT64_TYPE__ I64;
+
+struct Point {
+  int x;
+  int y;
+  int a[5];
+} P;
+
+extern Point P1;
+extern Point P2;
+
+extern int foo(int x);
+extern int bar(int x);
+extern int bat(int x, int y);
+
+int Test(int X, int Y) {
+  if (X - X) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both side of operator are equivalent [misc-redundant-expression]
+  if (X / X) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both side of operator are equivalent
+  if (X % X) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both side of operator are equivalent
+
+  if (X & X) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both side of operator are equivalent
+  if (X | X) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both side of operator are equivalent
+  if (X ^ X) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both side of operator are equivalent
+
+  if (X < X) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both side of operator are equivalent
+  if (X <= X) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both side of operator are equivalent
+  if (X > X) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both side of operator are equivalent
+  if (X >= X) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both side of operator are equivalent
+
+  if (X && X) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both side of operator are equivalent
+  if (X || X) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both side of operator are equivalent
+
+  if (X != (((X)))) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both side of operator are equivalent
+
+  if (X + 1 == X + 1) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: both side of operator are equivalent
+  if (X + 1 != X + 1) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: both side of operator are equivalent
+  if (X + 1 <= X + 1) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: both side of operator are equivalent
+  if (X + 1 >= X + 1) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: both side of operator are equivalent
+
+  if ((X != 1 || Y != 1) && (X != 1 || Y != 1)) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: both side of operator are equivalent
+  if (P.a[X - P.x] != P.a[X - P.x]) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: both side of operator are equivalent
+
+  if ((int)X < (int)X) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: both side of operator are equivalent
+
+  if ( + "dummy" == + "dummy") return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: both side of operator are equivalent
+  if (L"abc" == L"abc") return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: both side of operator are equivalent
+
+  if (foo(0) - 2 < foo(0) - 2) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: both side of operator are equivalent
+  if (foo(bar(0)) < (foo(bar((0))))) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: both side of operator are equivalent
+
+  if (P1.x < P2.x && P1.x < P2.x) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: both side of operator are equivalent
+  if (P2.a[P1.x + 2] < P2.x && P2.a[(P1.x) + (2)] < (P2.x)) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: both side of operator are equivalent
+
+  return 0;
+}
+
+int Valid(int X, int Y) {
+  if (X != Y) return 1;
+  if (X == Y + 0) return 1;
+  if (P.x == P.y) return 1;
+  if (P.a[P.x] < P.a[P.y]) return 1;
+  if (P.a[0] < P.a[1]) return 1;
+
+  if (P.a[0] < P.a[0ULL]) return 1;
+  if (0 < 0ULL) return 1;
+  if ((int)0 < (int)0ULL) return 1;
+
+  if (++X != ++X) return 1;
+  if (P.a[X]++ != P.a[X]++) return 1;
+  if (P.a[X++] != P.a[X++]) return 1;
+
+  if ("abc" == "ABC") return 1;
+  if (foo(bar(0)) < (foo(bat(0, 1)))) return 1;
+  return 0;
+}
+
+int TestConditional(int x, int y) {
+  int k = 0;
+  k += (y < 0) ? x : x;
+  // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: 'true' and 'false' expression are equivalent
+  k += (y < 0) ? x + 1 : x + 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: 'true' and 'false' expression are equivalent
+  return k;
+}
+
+struct MyStruct {
+  int x;
+} Q;
+bool operator==(const MyStruct& lhs, const MyStruct& rhs) { return lhs.x == rhs.x; }
+bool TestOperator(const MyStruct& S) {
+  if (S == Q) return false;
+  return S == S;
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: both side of overloaded operator are equivalent
+}
+
+#define LT(x, y) (void)((x) < (y))
+#define COND(x, y, z) ((x)?(y):(z))
+#define EQUALS(x, y) (x) == (y)
+
+int TestMacro(int X, int Y) {
+  LT(0, 0);
+  LT(1, 0);
+  LT(X, X);
+  LT(X+1, X + 1);
+  COND(X < Y, X, X);
+  EQUALS(Q, Q);
+}
+
+int TestFalsePositive(int* A, int X, float F) {
+  // Produced by bison.
+  X = A[(2) - (2)];
+  X = A['a' - 'a'];
+
+  // Testing NaN.
+  if (F != F && F == F) return 1;
+  return 0;
+}
+
+int TestBannedMacros() {
+#define EAGAIN 3
+#define NOT_EAGAIN 3
+  if (EAGAIN == 0 | EAGAIN == 0) return 0;
+  if (NOT_EAGAIN == 0 | NOT_EAGAIN == 0) return 0;
+  // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: both side of operator are equivalent
+}
+
+struct MyClass {
+static const int Value = 42;
+};
+template <typename T, typename U>
+void TemplateCheck() {
+  static_assert(T::Value == U::Value, "should be identical");
+  static_assert(T::Value == T::Value, "should be identical");
+  // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: both side of overloaded operator are equivalent
+}
+void TestTemplate() { TemplateCheck<MyClass, MyClass>(); }
+
+int TestArithmetic(int X, int Y) {
+  if (X + 1 == X) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: logical expression is always false
+  if (X + 1 != X) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: logical expression is always true
+  if (X - 1 == X) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: logical expression is always false
+  if (X - 1 != X) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: logical expression is always true
+
+  if (X + 1LL == X) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: logical expression is always false
+  if (X + 1ULL == X) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: logical expression is always false
+
+  if (X == X + 1) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: logical expression is always false
+  if (X != X + 1) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: logical expression is always true
+  if (X == X - 1) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: logical expression is always false
+  if (X != X - 1) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: logical expression is always true
+
+  if (X != X - 1U) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: logical expression is always true
+  if (X != X - 1LL) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: logical expression is always true
+
+  if ((X+X) != (X+X) - 1) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: logical expression is always true
+
+  if (X + 1 == X + 2) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: logical expression is always false
+  if (X + 1 != X + 2) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: logical expression is always true
+
+  if (X - 1 == X - 2) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: logical expression is always false
+  if (X - 1 != X - 2) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: logical expression is always true
+
+  if (X + 1 == X - -1) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: logical expression is always true
+  if (X + 1 != X - -1) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: logical expression is always false
+  if (X + 1 == X - -2) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: logical expression is always false
+  if (X + 1 != X - -2) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: logical expression is always true
+
+  if (X + 1 == X - (~0)) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: logical expression is always true
+  if (X + 1 == X - (~0U)) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: logical expression is always true
+  if (X + 1 == X - (~0ULL)) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: logical expression is always true
+
+  // Should not match.
+  if (X + 0.5 == X) return 1;
+  if (X + 1 == Y) return 1;
+  if (X + 1 == Y + 1) return 1;
+  if (X + 1 == Y + 2) return 1;
+
+  return 0;
+}
+
+int TestBitwise(int X) {
+  if ((X & 0xFF) == 0xF00) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: logical expression is always false
+  if ((X & 0xFF) != 0xF00) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: logical expression is always true
+  if ((X | 0xFF) == 0xF00) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: logical expression is always false
+  if ((X | 0xFF) != 0xF00) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: logical expression is always true
+
+  if ((X | 0xFFULL) != 0xF00) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: logical expression is always true
+  if ((X | 0xFF) != 0xF00ULL) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: logical expression is always true
+
+  if ((0xFF & X) == 0xF00) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: logical expression is always false
+  if ((0xFF & X) != 0xF00) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: logical expression is always true
+  if ((0xFF & X) == 0xF00) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: logical expression is always false
+  if ((0xFF & X) != 0xF00) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: logical expression is always true
+
+  if ((0xFFLL & X) == 0xF00) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: logical expression is always false
+  if ((0xFF & X) == 0xF00ULL) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: logical expression is always false
+
+  return 0;
+}
+
+int TestRelational(int X, int Y) {
+  if (X == 10 && X != 10) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: logical expression is always false
+  if (X == 10 && (X != 10)) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: logical expression is always false
+  if (X == 10 && !(X == 10)) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: logical expression is always false
+  if (!(X != 10) && !(X == 10)) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: logical expression is always false
+
+  if (X == 10ULL && X != 10ULL) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: logical expression is always false
+  if (!(X != 10U) && !(X == 10)) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: logical expression is always false
+  if (!(X != 10LL) && !(X == 10)) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: logical expression is always false
+  if (!(X != 10ULL) && !(X == 10)) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: logical expression is always false
+
+  if (X == 0 && X) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: logical expression is always false
+  if (X != 0 && !X) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: logical expression is always false
+  if (X && !X) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: logical expression is always false
+
+  if (X && !!X) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: equivalent expression on both side of logical operator
+  if (X != 0 && X) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: equivalent expression on both side of logical operator
+  if (X != 0 && !!X) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: equivalent expression on both side of logical operator
+  if (X == 0 && !X) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: equivalent expression on both side of logical operator
+
+  if (X == 10 && X > 10) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: logical expression is always false
+  if (X == 10 && X < 10) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: logical expression is always false
+  if (X < 10 && X > 10) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: logical expression is always false
+  if (X <= 10 && X > 10) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: logical expression is always false
+  if (X < 10 && X >= 10) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: logical expression is always false
+  if (X < 10 && X == 10) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: logical expression is always false
+
+  if (X > 5 && X <= 5) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: logical expression is always false
+  if (X > -5 && X <= -5) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: logical expression is always false
+
+  if (X < 10 || X >= 10) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: logical expression is always true
+  if (X <= 10 || X > 10) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: logical expression is always true
+  if (X <= 10 || X >= 11) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: logical expression is always true
+  if (X != 7 || X != 14) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: logical expression is always true
+
+  if (X < 7 && X < 6) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: expression is redundant
+  if (X < 7 && X < 7) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: both side of operator are equivalent
+  if (X < 7 && X < 8) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: expression is redundant
+
+  if (X < 7 && X <= 5) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: expression is redundant
+  if (X < 7 && X <= 6) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: equivalent expression on both side of logical operator
+  if (X < 7 && X <= 7) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: expression is redundant
+  if (X < 7 && X <= 8) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: expression is redundant
+
+  if (X <= 7 && X < 6) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: expression is redundant
+  if (X <= 7 && X < 7) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: expression is redundant
+  if (X <= 7 && X < 8) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: equivalent expression on both side of logical operator
+
+  if (X <= 7 && X <= 5) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: expression is redundant
+  if (X <= 7 && X <= 6) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: expression is redundant
+  if (X <= 7 && X <= 7) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: both side of operator are equivalent
+  if (X <= 7 && X <= 8) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: expression is redundant
+
+  if (X == 11 && X > 10) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: expression is redundant
+  if (X == 11 && X < 12) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: expression is redundant
+  if (X > 10 && X == 11) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: expression is redundant
+  if (X < 12 && X == 11) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: expression is redundant
+
+  if (X != 11 && X == 42) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: expression is redundant
+  if (X != 11 && X > 11) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: expression is redundant
+  if (X != 11 && X < 11) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: expression is redundant
+  if (X != 11 && X < 8) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: expression is redundant
+  if (X != 11 && X > 14) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: expression is redundant
+
+  if (X < 7 || X < 6) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: expression is redundant
+  if (X < 7 || X < 7) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: both side of operator are equivalent
+  if (X < 7 || X < 8) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: expression is redundant
+
+  // Should not match.
+  if (X == 10 && Y == 10) return 1;
+  if (X != 10 && X != 12) return 1;
+  if (X == 10 || X == 12) return 1;
+  if (X < 10 || X > 12) return 1;
+  if (X > 10 && X < 12) return 1;
+  if (X < 10 || X >= 12) return 1;
+  if (X > 10 && X <= 12) return 1;
+  if (X <= 10 || X > 12) return 1;
+  if (X >= 10 && X < 12) return 1;
+  if (X <= 10 || X >= 12) return 1;
+  if (X >= 10 && X <= 12) return 1;
+  if (X >= 10 && X <= 11) return 1;
+  if (X >= 10 && X < 11) return 1;
+  if (X > 10 && X <= 11) return 1;
+  if (X > 10 && X != 11) return 1;
+  if (X >= 10 && X <= 10) return 1;
+  if (X <= 10 && X >= 10) return 1;
+  if (!X && !Y) return 1;
+  if (!X && Y) return 1;
+  if (!X && Y == 0) return 1;
+  if (X == 10 && Y != 10) return 1;
+  if (X < 0 || X > 0) return 1;
+
+  return 0;
+}
+
+int TestValidExpression(int X) {
+  if (X - 1 == 1 - X) return 1;
+  if (2 * X == X) return 1;
+  if ((X << 1) == X) return 1;
+
+  return 0;
+}
+
+enum Color { Red, Yellow, Green };
+int TestRelatiopnalWithEnum(enum Color C) {
+  if (C == Red && C == Yellow) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: logical expression is always false
+  if (C == Red && C != Red) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: logical expression is always false
+  if (C != Red || C != Yellow) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: logical expression is always true
+
+  // Should not match.
+  if (C == Red || C == Yellow) return 1;
+  if (C != Red && C != Yellow) return 1;
+
+  return 0;
+}
+
+template<class T>
+int TestRelationalTemplated(int X) {
+  // This test causes a corner case with |isIntegerConstantExpr| where the type
+  // is dependant. There is an assert failing when evaluating
+  // sizeof(<incomplet-type>).
+  if (sizeof(T) == 4 || sizeof(T) == 8) return 1;
+
+  if (X + 0 == -X) return 1;
+  if (X + 0 < X) return 1;
+
+  return 0;
+}
+
+int TestWithSignedUnsigned(int X) {
+  if (X + 1 == X + 1ULL) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: logical expression is always true
+  if ((X & 0xFFU) == 0xF00) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: logical expression is always false
+  if ((X & 0xFF) == 0xF00U) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: logical expression is always false
+  if ((X & 0xFFU) == 0xF00U) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: logical expression is always false
+
+  return 0;
+}
+
+int TestWithLong(int X, I64 Y) {
+  if (X + 0ULL == -X) return 1;
+  if (Y + 0 == -Y) return 1;
+  if (Y <= 10 && X >= 10LL) return 1;
+  if (Y <= 10 && X >= 10ULL) return 1;
+  if (X <= 10 || X > 12LL) return 1;
+  if (X <= 10 || X > 12ULL) return 1;
+  if (Y <= 10 || Y > 12) return 1;
+
+  return 0;
+}
+
+int TestWithMinMaxInt(int X) {
+  if (X <= X + 0xFFFFFFFFU) return 1;
+  if (X <= X + 0x7FFFFFFF) return 1;
+  if (X <= X + 0x80000000) return 1;
+  if (X <= 0xFFFFFFFFU && X > 0) return 1;
+  if (X <= 0xFFFFFFFFU && X > 0U) return 1;
+
+  if (X + 0x80000000 == X - 0x80000000) return 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: logical expression is always true
+
+  if (X > 0x7FFFFFFF || X < ((-0x7FFFFFFF)-1)) return 1;
+  if (X <= 0x7FFFFFFF && X >= ((-0x7FFFFFFF)-1)) return 1;
+
+  return 0;
+}
diff --git a/test/clang-tidy/misc-sizeof-container.cpp b/test/clang-tidy/misc-sizeof-container.cpp
new file mode 100644 (file)
index 0000000..472587e
--- /dev/null
@@ -0,0 +1,103 @@
+// RUN: %check_clang_tidy %s misc-sizeof-container %t -- -- -std=c++11 -target x86_64-unknown-unknown
+
+namespace std {
+
+typedef unsigned int size_t;
+
+template <typename T>
+struct basic_string {
+  size_t size() const;
+};
+
+template <typename T>
+basic_string<T> operator+(const basic_string<T> &, const T *);
+
+typedef basic_string<char> string;
+
+template <typename T>
+struct vector {
+  size_t size() const;
+};
+
+// std::bitset<> is not a container. sizeof() is reasonable for it.
+template <size_t N>
+struct bitset {
+  size_t size() const;
+};
+
+// std::array<> is, well, an array. sizeof() is reasonable for it.
+template <typename T, size_t N>
+struct array {
+  size_t size() const;
+};
+
+class fake_container1 {
+  size_t size() const; // non-public
+};
+
+struct fake_container2 {
+  size_t size(); // non-const
+};
+
+}
+
+using std::size_t;
+
+#define ARRAYSIZE(a) \
+  ((sizeof(a) / sizeof(*(a))) / static_cast<size_t>(!(sizeof(a) % sizeof(*(a)))))
+
+#define ARRAYSIZE2(a) \
+  (((sizeof(a)) / (sizeof(*(a)))) / static_cast<size_t>(!((sizeof(a)) % (sizeof(*(a))))))
+
+struct string {
+  std::size_t size() const;
+};
+
+template<typename T>
+void g(T t) {
+  (void)sizeof(t);
+}
+
+void f() {
+  string s1;
+  std::string s2;
+  std::vector<int> v;
+
+  int a = 42 + sizeof(s1);
+// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: sizeof() doesn't return the size of the container; did you mean .size()? [misc-sizeof-container]
+  a = 123 * sizeof(s2);
+// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: sizeof() doesn't return the size
+  a = 45 + sizeof(s2 + "asdf");
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: sizeof() doesn't return the size
+  a = sizeof(v);
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: sizeof() doesn't return the size
+  a = sizeof(std::vector<int>{});
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: sizeof() doesn't return the size
+
+  a = sizeof(a);
+  a = sizeof(int);
+  a = sizeof(std::string);
+  a = sizeof(std::vector<int>);
+
+  g(s1);
+  g(s2);
+  g(v);
+
+  std::fake_container1 fake1;
+  std::fake_container2 fake2;
+  std::bitset<7> std_bitset;
+  std::array<int, 3> std_array;
+
+  a = sizeof(fake1);
+  a = sizeof(fake2);
+  a = sizeof(std_bitset);
+  a = sizeof(std_array);
+
+
+  std::string arr[3];
+  a = ARRAYSIZE(arr);
+  a = ARRAYSIZE2(arr);
+  a = sizeof(arr) / sizeof(arr[0]);
+
+  (void)a;
+}
diff --git a/test/clang-tidy/misc-sizeof-expression.cpp b/test/clang-tidy/misc-sizeof-expression.cpp
new file mode 100644 (file)
index 0000000..f9d798d
--- /dev/null
@@ -0,0 +1,186 @@
+// RUN: %check_clang_tidy %s misc-sizeof-expression %t
+
+class C {
+  int size() { return sizeof(this); }
+  // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: suspicious usage of 'sizeof(this)'
+};
+
+#define LEN 8
+
+int X;
+extern int A[10];
+extern short B[10];
+
+#pragma pack(1)
+struct  S { char a, b, c; };
+
+int Test1(const char* ptr) {
+  int sum = 0;
+  sum += sizeof(LEN);
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(K)'
+  sum += sizeof(LEN + 1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(K)'
+  sum += sizeof(sum, LEN);
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(..., ...)'
+  sum += sizeof(sizeof(X));
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(sizeof(...))'
+  sum += sizeof(LEN + sizeof(X));
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(sizeof(...))'
+  sum += sizeof(LEN + LEN + sizeof(X));
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(sizeof(...))'
+  sum += sizeof(LEN + (LEN + sizeof(X)));
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(sizeof(...))'
+  sum += sizeof(LEN + -sizeof(X));
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(sizeof(...))'
+  sum += sizeof(LEN + - + -sizeof(X));
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(sizeof(...))'
+  sum += sizeof(char) / sizeof(char);
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of sizeof pointer 'sizeof(T)/sizeof(T)'
+  sum += sizeof(A) / sizeof(S);
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(...)/sizeof(...)'; numerator is not a multiple of denominator
+  sum += sizeof(char) / sizeof(int);
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(...)/sizeof(...)'; numerator is not a multiple of denominator
+  sum += sizeof(char) / sizeof(A);
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(...)/sizeof(...)'; numerator is not a multiple of denominator
+  sum += sizeof(B[0]) / sizeof(A);
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(...)/sizeof(...)'; numerator is not a multiple of denominator
+  sum += sizeof(ptr) / sizeof(char);
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of sizeof pointer 'sizeof(T*)/sizeof(T)'
+  sum += sizeof(ptr) / sizeof(ptr[0]);
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of sizeof pointer 'sizeof(T*)/sizeof(T)'
+  sum += sizeof(ptr) / sizeof(char*);
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of sizeof pointer 'sizeof(P*)/sizeof(Q*)'
+  sum += sizeof(ptr) / sizeof(void*);
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of sizeof pointer 'sizeof(P*)/sizeof(Q*)'
+  sum += sizeof(ptr) / sizeof(const void volatile*);
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of sizeof pointer 'sizeof(P*)/sizeof(Q*)'
+  sum += sizeof(ptr) / sizeof(char);
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of sizeof pointer 'sizeof(T*)/sizeof(T)'
+  sum += sizeof(ptr) / sizeof(ptr[0]);
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of sizeof pointer 'sizeof(T*)/sizeof(T)'
+  sum += sizeof(int) * sizeof(char);
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious 'sizeof' by 'sizeof' multiplication
+  sum += sizeof(ptr) * sizeof(ptr[0]);
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious 'sizeof' by 'sizeof' multiplication
+  sum += sizeof(int) * (2 * sizeof(char));
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious 'sizeof' by 'sizeof' multiplication
+  sum += (2 * sizeof(char)) * sizeof(int);
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious 'sizeof' by 'sizeof' multiplication
+  if (sizeof(A) < 0x100000) sum += 42;
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: suspicious comparison of 'sizeof(expr)' to a constant
+  if (sizeof(A) <= 0xFFFFFFFEU) sum += 42;
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: suspicious comparison of 'sizeof(expr)' to a constant
+  return sum;
+}
+
+typedef char MyChar;
+typedef const MyChar MyConstChar;
+
+int CE0 = sizeof sizeof(char);
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: suspicious usage of 'sizeof(sizeof(...))'
+int CE1 = sizeof +sizeof(char);
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: suspicious usage of 'sizeof(sizeof(...))'
+int CE2 = sizeof sizeof(const char*);
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: suspicious usage of 'sizeof(sizeof(...))'
+int CE3 = sizeof sizeof(const volatile char* const*);
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: suspicious usage of 'sizeof(sizeof(...))'
+int CE4 = sizeof sizeof(MyConstChar);
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: suspicious usage of 'sizeof(sizeof(...))'
+
+int Test2(MyConstChar* A) {
+  int sum = 0;
+  sum += sizeof(MyConstChar) / sizeof(char);
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of sizeof pointer 'sizeof(T)/sizeof(T)'
+  sum += sizeof(MyConstChar) / sizeof(MyChar);
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of sizeof pointer 'sizeof(T)/sizeof(T)'
+  sum += sizeof(A[0]) / sizeof(char);
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of sizeof pointer 'sizeof(T)/sizeof(T)'
+  return sum;
+}
+
+template <int T>
+int Foo() { int A[T]; return sizeof(T); }
+// CHECK-MESSAGES: :[[@LINE-1]]:30: warning: suspicious usage of 'sizeof(K)'
+template <typename T>
+int Bar() { T A[5]; return sizeof(A[0]) / sizeof(T); }
+// CHECK-MESSAGES: :[[@LINE-1]]:28: warning: suspicious usage of sizeof pointer 'sizeof(T)/sizeof(T)'
+int Test3() { return Foo<42>() + Bar<char>(); }
+
+static const char* kABC = "abc";
+static const wchar_t* kDEF = L"def";
+int Test4(const char A[10]) {
+  int sum = 0;
+  sum += sizeof(kABC);
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(char*)'
+  sum += sizeof(kDEF);
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(char*)'
+  return sum;
+}
+
+int Test5() {
+  typedef int Array10[10];
+
+  struct MyStruct {
+    Array10 arr;
+    Array10* ptr;
+  };
+  typedef const MyStruct TMyStruct;
+
+  static TMyStruct kGlocalMyStruct = {};
+  static TMyStruct volatile * kGlocalMyStructPtr = &kGlocalMyStruct;
+
+  MyStruct S;
+  Array10 A10;
+
+  int sum = 0;
+  sum += sizeof(&S.arr);
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(A*)'; pointer to aggregate
+  sum += sizeof(&kGlocalMyStruct.arr);
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(A*)'; pointer to aggregate
+  sum += sizeof(&kGlocalMyStructPtr->arr);
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(A*)'; pointer to aggregate
+  sum += sizeof(S.arr + 0);
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(A*)'; pointer to aggregate
+  sum += sizeof(+ S.arr);
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(A*)'; pointer to aggregate
+  sum += sizeof((int*)S.arr);
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(A*)'; pointer to aggregate
+
+  sum += sizeof(S.ptr);
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(A*)'; pointer to aggregate
+  sum += sizeof(kGlocalMyStruct.ptr);
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(A*)'; pointer to aggregate
+  sum += sizeof(kGlocalMyStructPtr->ptr);
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(A*)'; pointer to aggregate
+
+  sum += sizeof(&kGlocalMyStruct);
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(A*)'; pointer to aggregate
+  sum += sizeof(&S);
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(A*)'; pointer to aggregate
+  sum += sizeof(&A10);
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious usage of 'sizeof(A*)'; pointer to aggregate
+
+  return sum;
+}
+
+int ValidExpressions() {
+  int A[] = {1, 2, 3, 4};
+  static const char str[] = "hello";
+  static const char* ptr[] { "aaa", "bbb", "ccc" };
+  int sum = 0;
+  if (sizeof(A) < 10)
+    sum += sizeof(A);
+  sum += sizeof(int);
+  sum += sizeof(A[sizeof(A) / sizeof(int)]);
+  sum += sizeof(&A[sizeof(A) / sizeof(int)]);
+  sum += sizeof(sizeof(0));  // Special case: sizeof size_t.
+  sum += sizeof(void*);
+  sum += sizeof(void const *);
+  sum += sizeof(void const *) / 4;
+  sum += sizeof(str);
+  sum += sizeof(str) / sizeof(char);
+  sum += sizeof(str) / sizeof(str[0]);
+  sum += sizeof(ptr) / sizeof(ptr[0]);
+  sum += sizeof(ptr) / sizeof(*(ptr));
+  return sum;
+}
diff --git a/test/clang-tidy/misc-static-assert.c b/test/clang-tidy/misc-static-assert.c
new file mode 100644 (file)
index 0000000..e3e8304
--- /dev/null
@@ -0,0 +1,27 @@
+// RUN: %check_clang_tidy %s misc-static-assert %t -- -- -std=c11
+// RUN: clang-tidy %s -checks=-*,misc-static-assert -- -std=c99 | count 0
+
+void abort() {}
+#ifdef NDEBUG
+#define assert(x) 1
+#else
+#define assert(x)                                                              \
+  if (!(x))                                                                    \
+  abort()
+#endif
+
+void f(void) {
+  int x = 1;
+  assert(x == 0);
+  // CHECK-FIXES: {{^  }}assert(x == 0);
+
+  #define static_assert(x, msg) _Static_assert(x, msg)
+  assert(11 == 5 + 6);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found assert() that could be
+  // CHECK-FIXES: {{^  }}static_assert(11 == 5 + 6, "");
+  #undef static_assert
+
+  assert(10 == 5 + 5);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found assert() that could be
+  // CHECK-FIXES: {{^  }}static_assert(10 == 5 + 5, "");
+}
diff --git a/test/clang-tidy/misc-static-assert.cpp b/test/clang-tidy/misc-static-assert.cpp
new file mode 100644 (file)
index 0000000..85ae053
--- /dev/null
@@ -0,0 +1,143 @@
+// RUN: %check_clang_tidy %s misc-static-assert %t
+
+void abort() {}
+#ifdef NDEBUG
+#define assert(x) 1
+#else
+#define assert(x)                                                              \
+  if (!(x))                                                                    \
+  abort()
+#endif
+
+void print(...);
+
+#define ZERO_MACRO 0
+
+#define False false
+#define FALSE 0
+
+#define my_macro() assert(0 == 1)
+// CHECK-FIXES: #define my_macro() assert(0 == 1)
+
+constexpr bool myfunc(int a, int b) { return a * b == 0; }
+
+typedef __SIZE_TYPE__ size_t;
+extern "C" size_t strlen(const char *s);
+
+class A {
+public:
+  bool method() { return true; }
+};
+
+class B {
+public:
+  constexpr bool method() { return true; }
+};
+
+template <class T> void doSomething(T t) {
+  assert(myfunc(1, 2));
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found assert() that could be replaced by static_assert() [misc-static-assert]
+  // CHECK-FIXES: {{^  }}static_assert(myfunc(1, 2), "");
+
+  assert(t.method());
+  // CHECK-FIXES: {{^  }}assert(t.method());
+
+  assert(sizeof(T) == 123);
+}
+
+int main() {
+  my_macro();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found assert() that could be
+  // CHECK-FIXES: {{^  }}my_macro();
+
+  assert(myfunc(1, 2) && (3 == 4));
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found assert() that could be
+  // CHECK-FIXES: {{^  }}static_assert(myfunc(1, 2) && (3 == 4), "");
+
+  int x = 1;
+  assert(x == 0);
+  // CHECK-FIXES: {{^  }}assert(x == 0);
+
+  A a;
+  B b;
+
+  doSomething<A>(a);
+  doSomething<B>(b);
+
+  assert(false);
+  // CHECK-FIXES: {{^  }}assert(false);
+
+  assert(False);
+  // CHECK-FIXES: {{^  }}assert(False);
+  assert(FALSE);
+  // CHECK-FIXES: {{^  }}assert(FALSE);
+
+  assert(ZERO_MACRO);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found assert() that could be
+  // CHECK-FIXES: {{^  }}static_assert(ZERO_MACRO, "");
+
+  assert(!"Don't report me!");
+  // CHECK-FIXES: {{^  }}assert(!"Don't report me!");
+
+  assert(0 && "Don't report me!");
+  // CHECK-FIXES: {{^  }}assert(0 && "Don't report me!");
+
+  assert(false && "Don't report me!");
+  // CHECK-FIXES: {{^  }}assert(false && "Don't report me!");
+
+#define NULL ((void*)0)
+  assert(NULL && "Don't report me!");
+  // CHECK-FIXES: {{^  }}assert(NULL && "Don't report me!");
+
+  assert(NULL == "Don't report me!");
+  // CHECK-FIXES: {{^  }}assert(NULL == "Don't report me!");
+
+  assert("Don't report me!" == NULL);
+  // CHECK-FIXES: {{^  }}assert("Don't report me!" == NULL);
+
+  assert(0 == "Don't report me!");
+  // CHECK-FIXES: {{^  }}assert(0 == "Don't report me!");
+
+#define NULL ((unsigned int)0)
+  assert(NULL && "Report me!");
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found assert() that could be
+  // CHECK-FIXES: {{^  }}static_assert(NULL , "Report me!");
+
+#define NULL __null
+  assert(__null == "Don't report me!");
+  // CHECK-FIXES: {{^  }}assert(__null == "Don't report me!");
+  assert(NULL == "Don't report me!");
+  // CHECK-FIXES: {{^  }}assert(NULL == "Don't report me!");
+#undef NULL
+
+  assert(ZERO_MACRO && "Report me!");
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found assert() that could be
+  // CHECK-FIXES: {{^  }}static_assert(ZERO_MACRO , "Report me!");
+
+  assert(0);
+
+#define false false
+  assert(false);
+
+#define false 0
+  assert(false);
+#undef false
+
+  assert(10==5 && "Report me!");
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found assert() that could be
+  // CHECK-FIXES: {{^  }}static_assert(10==5 , "Report me!");
+
+  assert(strlen("12345") == 5);
+  // CHECK-FIXES: {{^  }}assert(strlen("12345") == 5);
+
+#define assert(e) (__builtin_expect(!(e), 0) ? print (#e, __FILE__, __LINE__) : (void)0)
+  assert(false);
+  // CHECK-FIXES: {{^  }}assert(false);
+
+  assert(10 == 5 + 5);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found assert() that could be
+  // CHECK-FIXES: {{^  }}static_assert(10 == 5 + 5, "");
+#undef assert
+
+  return 0;
+}
diff --git a/test/clang-tidy/misc-string-compare.cpp b/test/clang-tidy/misc-string-compare.cpp
new file mode 100644 (file)
index 0000000..9b6ef1c
--- /dev/null
@@ -0,0 +1,119 @@
+// RUN: %check_clang_tidy %s misc-string-compare %t -- -- -std=c++11
+
+namespace std {
+template <typename T>
+class allocator {};
+template <typename T>
+class char_traits {};
+template <typename C, typename T = std::char_traits<C>, typename A = std::allocator<C>>
+class basic_string {
+public:
+  basic_string();
+  basic_string(const C *, unsigned int size);
+  int compare(const basic_string<char> &str) const;
+  int compare(const C *) const;
+  int compare(int, int, const basic_string<char> &str) const;
+  bool empty();
+};
+bool operator==(const basic_string<char> &lhs, const basic_string<char> &rhs);
+bool operator!=(const basic_string<char> &lhs, const basic_string<char> &rhs);
+bool operator==(const basic_string<char> &lhs, const char *&rhs);
+typedef basic_string<char> string;
+}
+
+void func(bool b);
+
+std::string comp() {
+  std::string str("a", 1);
+  return str;
+}
+
+void Test() {
+  std::string str1("a", 1);
+  std::string str2("b", 1);
+
+  if (str1.compare(str2)) {
+  }
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: do not use 'compare' to test equality of strings; use the string equality operator instead [misc-string-compare]
+  if (!str1.compare(str2)) {
+  }
+  // CHECK-MESSAGES: [[@LINE-2]]:8: warning: do not use 'compare' to test equality of strings; use the string equality operator instead [misc-string-compare]
+  if (str1.compare(str2) == 0) {
+  }
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: do not use 'compare' to test equality of strings;
+  // CHECK-FIXES: if (str1 == str2) {
+  if (str1.compare(str2) != 0) {
+  }
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: do not use 'compare' to test equality of strings;
+  // CHECK-FIXES: if (str1 != str2) {
+  if (str1.compare("foo") == 0) {
+  }
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: do not use 'compare' to test equality of strings;
+  // CHECK-FIXES: if (str1 == "foo") {
+  if (0 == str1.compare(str2)) {
+  }
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: do not use 'compare' to test equality of strings;
+  // CHECK-FIXES: if (str2 == str1) {
+  if (0 != str1.compare(str2)) {
+  }
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: do not use 'compare' to test equality of strings;
+  // CHECK-FIXES: if (str2 != str1) {
+  func(str1.compare(str2));
+  // CHECK-MESSAGES: [[@LINE-1]]:8: warning: do not use 'compare' to test equality of strings;
+  if (str2.empty() || str1.compare(str2) != 0) {
+  }
+  // CHECK-MESSAGES: [[@LINE-2]]:23: warning: do not use 'compare' to test equality of strings;
+  // CHECK-FIXES: if (str2.empty() || str1 != str2) {
+  std::string *str3 = &str1;
+  if (str3->compare(str2)) {
+  }
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: do not use 'compare' to test equality of strings;
+  if (str3->compare(str2) == 0) {
+  }
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: do not use 'compare' to test equality of strings;
+  // CHECK-FIXES: if (*str3 == str2) {
+  if (str2.compare(*str3) == 0) {
+  }
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: do not use 'compare' to test equality of strings;
+  // CHECK-FIXES: if (str2 == *str3) {
+  if (comp().compare(str1) == 0) {
+  }
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: do not use 'compare' to test equality of strings;
+  // CHECK-FIXES: if (comp() == str1) {
+  if (str1.compare(comp()) == 0) {
+  }
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: do not use 'compare' to test equality of strings;
+  // CHECK-FIXES: if (str1 == comp()) {
+  if (str1.compare(comp())) {
+  }
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: do not use 'compare' to test equality of strings;
+}
+
+void Valid() {
+  std::string str1("a", 1);
+  std::string str2("b", 1);
+  if (str1 == str2) {
+  }
+  if (str1 != str2) {
+  }
+  if (str1.compare(str2) == str1.compare(str2)) {
+  }
+  if (0 == 0) {
+  }
+  if (str1.compare(str2) > 0) {
+  }
+  if (str1.compare(1, 3, str2)) {
+  }
+  if (str1.compare(str2) > 0) {
+  }
+  if (str1.compare(str2) < 0) {
+  }
+  if (str1.compare(str2) == 2) {
+  }
+  if (str1.compare(str2) == -3) {
+  }
+  if (str1.compare(str2) == 1) {
+  }
+  if (str1.compare(str2) == -1) {
+  }
+}
diff --git a/test/clang-tidy/misc-string-constructor.cpp b/test/clang-tidy/misc-string-constructor.cpp
new file mode 100644 (file)
index 0000000..85ae745
--- /dev/null
@@ -0,0 +1,56 @@
+// RUN: %check_clang_tidy %s misc-string-constructor %t
+
+namespace std {
+template <typename T>
+class allocator {};
+template <typename T>
+class char_traits {};
+template <typename C, typename T = std::char_traits<C>, typename A = std::allocator<C> >
+struct basic_string {
+  basic_string();
+  basic_string(const C*, unsigned int size);
+  basic_string(unsigned int size, C c);
+};
+typedef basic_string<char> string;
+typedef basic_string<wchar_t> wstring;
+}
+
+const char* kText = "";
+const char kText2[] = "";
+extern const char kText3[];
+
+void Test() {
+  std::string str('x', 4);
+  // CHECK-MESSAGES: [[@LINE-1]]:15: warning: string constructor parameters are probably swapped; expecting string(count, character) [misc-string-constructor]
+  // CHECK-FIXES: std::string str(4, 'x');
+  std::wstring wstr(L'x', 4);
+  // CHECK-MESSAGES: [[@LINE-1]]:16: warning: string constructor parameters are probably swapped
+  // CHECK-FIXES: std::wstring wstr(4, L'x');
+  std::string s0(0, 'x');
+  // CHECK-MESSAGES: [[@LINE-1]]:15: warning: constructor creating an empty string
+  std::string s1(-4, 'x');
+  // CHECK-MESSAGES: [[@LINE-1]]:15: warning: negative value used as length parameter
+  std::string s2(0x1000000, 'x');
+  // CHECK-MESSAGES: [[@LINE-1]]:15: warning: suspicious large length parameter
+
+  std::string q0("test", 0);
+  // CHECK-MESSAGES: [[@LINE-1]]:15: warning: constructor creating an empty string
+  std::string q1(kText, -4);
+  // CHECK-MESSAGES: [[@LINE-1]]:15: warning: negative value used as length parameter
+  std::string q2("test", 200);
+  // CHECK-MESSAGES: [[@LINE-1]]:15: warning: length is bigger then string literal size
+  std::string q3(kText, 200);
+  // CHECK-MESSAGES: [[@LINE-1]]:15: warning: length is bigger then string literal size
+  std::string q4(kText2, 200);
+  // CHECK-MESSAGES: [[@LINE-1]]:15: warning: length is bigger then string literal size
+  std::string q5(kText3,  0x1000000);
+  // CHECK-MESSAGES: [[@LINE-1]]:15: warning: suspicious large length parameter
+}
+
+void Valid() {
+  std::string empty();
+  std::string str(4, 'x');
+  std::wstring wstr(4, L'x');
+  std::string s1("test", 4);
+  std::string s2("test", 3);
+}
diff --git a/test/clang-tidy/misc-string-integer-assignment.cpp b/test/clang-tidy/misc-string-integer-assignment.cpp
new file mode 100644 (file)
index 0000000..cb82341
--- /dev/null
@@ -0,0 +1,53 @@
+// RUN: %check_clang_tidy %s misc-string-integer-assignment %t
+
+namespace std {
+template<typename T>
+struct basic_string {
+  basic_string& operator=(T);
+  basic_string& operator=(basic_string);
+  basic_string& operator+=(T);
+  basic_string& operator+=(basic_string);
+};
+
+typedef basic_string<char> string;
+typedef basic_string<wchar_t> wstring;
+}
+
+typedef int MyArcaneChar;
+
+int main() {
+  std::string s;
+  std::wstring ws;
+  int x = 5;
+
+  s = 6;
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: an integer is interpreted as a character code when assigning {{.*}} [misc-string-integer-assignment]
+// CHECK-FIXES: {{^}}  s = '6';{{$}}
+  s = 66;
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: an integer is interpreted as a chara
+// CHECK-FIXES: {{^}}  s = "66";{{$}}
+  s = x;
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: an integer is interpreted as a chara
+// CHECK-FIXES: {{^}}  s = std::to_string(x);{{$}}
+  s = 'c';
+  s = static_cast<char>(6);
+
+// +=
+  ws += 6;
+// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: an integer is interpreted as a chara
+// CHECK-FIXES: {{^}}  ws += L'6';{{$}}
+  ws += 66;
+// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: an integer is interpreted as a chara
+// CHECK-FIXES: {{^}}  ws += L"66";{{$}}
+  ws += x;
+// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: an integer is interpreted as a chara
+// CHECK-FIXES: {{^}}  ws += std::to_wstring(x);{{$}}
+  ws += L'c';
+  ws += (wchar_t)6;
+
+  std::basic_string<MyArcaneChar> as;
+  as = 6;
+// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: an integer is interpreted as a chara
+// CHECK-FIXES: {{^}}  as = 6;{{$}}
+
+}
diff --git a/test/clang-tidy/misc-string-literal-with-embedded-nul.cpp b/test/clang-tidy/misc-string-literal-with-embedded-nul.cpp
new file mode 100644 (file)
index 0000000..2605dd4
--- /dev/null
@@ -0,0 +1,85 @@
+// RUN: %check_clang_tidy %s misc-string-literal-with-embedded-nul %t
+
+namespace std {
+template <typename T>
+class allocator {};
+template <typename T>
+class char_traits {};
+template <typename C, typename T, typename A>
+struct basic_string {
+  typedef basic_string<C, T, A> _Type;
+  basic_string();
+  basic_string(const C *p, const A &a = A());
+
+  _Type& operator+=(const C* s);
+  _Type& operator=(const C* s);
+};
+
+typedef basic_string<char, std::char_traits<char>, std::allocator<char>> string;
+typedef basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t>> wstring;
+}
+
+bool operator==(const std::string&, const char*);
+bool operator==(const char*, const std::string&);
+
+
+const char Valid[] = "This is valid \x12.";
+const char Strange[] = "This is strange \0x12 and must be fixed";
+// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: suspicious embedded NUL character [misc-string-literal-with-embedded-nul]
+
+const char textA[] = "\0x01\0x02\0x03\0x04";
+// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: suspicious embedded NUL character
+const wchar_t textW[] = L"\0x01\0x02\0x03\0x04";
+// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: suspicious embedded NUL character
+
+const char A[] = "\0";
+const char B[] = "\0x";
+const char C[] = "\0x1";
+const char D[] = "\0x11";
+// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: suspicious embedded NUL character
+
+const wchar_t E[] = L"\0";
+const wchar_t F[] = L"\0x";
+const wchar_t G[] = L"\0x1";
+const wchar_t H[] = L"\0x11";
+// CHECK-MESSAGES: :[[@LINE-1]]:21: warning: suspicious embedded NUL character
+
+const char I[] = "\000\000\000\000";
+const char J[] = "\0\0\0\0\0\0";
+const char K[] = "";
+
+const char L[] = "\0x12" "\0x12" "\0x12" "\0x12";
+// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: suspicious embedded NUL character
+
+void TestA() {
+  std::string str1 = "abc\0def";
+  // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: truncated string literal
+  std::string str2 = "\0";
+  // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: truncated string literal
+  std::string str3("\0");
+  // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: truncated string literal
+  std::string str4{"\x00\x01\x02\x03"};
+  // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: truncated string literal
+
+  std::string str;
+  str += "abc\0def";
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: truncated string literal
+  str = "abc\0def";
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: truncated string literal
+
+  if (str == "abc\0def") return;
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: truncated string literal
+  if ("abc\0def" == str) return;
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: truncated string literal
+}
+
+void TestW() {
+  std::wstring str1 = L"abc\0def";
+  // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: truncated string literal
+  std::wstring str2 = L"\0";
+  // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: truncated string literal
+  std::wstring str3(L"\0");
+  // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: truncated string literal
+  std::wstring str4{L"\x00\x01\x02\x03"};
+  // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: truncated string literal
+}
diff --git a/test/clang-tidy/misc-suspicious-enum-usage-strict.cpp b/test/clang-tidy/misc-suspicious-enum-usage-strict.cpp
new file mode 100644 (file)
index 0000000..404c893
--- /dev/null
@@ -0,0 +1,98 @@
+// RUN: %check_clang_tidy %s misc-suspicious-enum-usage %t -- -config="{CheckOptions: [{key: misc-suspicious-enum-usage.StrictMode, value: 1}]}" --
+
+enum A {
+  A = 1,
+  B = 2,
+  C = 4,
+  D = 8,
+  E = 16,
+  F = 32,
+  G = 63
+};
+
+// CHECK-MESSAGES: :[[@LINE+2]]:1: warning: enum type seems like a bitmask (contains mostly power-of-2 literals) but a literal is not power-of-2
+// CHECK-MESSAGES: :76:7: note: used here as a bitmask
+enum X {
+  X = 8,
+  Y = 16,
+  Z = 4,
+  ZZ = 3
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: enum type seems like a bitmask (contains mostly power-of-2 literals), but this literal is not a power-of-2 [misc-suspicious-enum-usage]
+// CHECK-MESSAGES: :70:13: note: used here as a bitmask
+};
+// CHECK-MESSAGES: :[[@LINE+2]]:1: warning: enum type seems like a bitmask (contains mostly power-of-2 literals) but some literals are not power-of-2
+// CHECK-MESSAGES: :73:8: note: used here as a bitmask
+enum PP {
+  P = 2,
+  Q = 3,
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: enum type seems like a bitmask (contains mostly power-of-2 literals), but this literal is not a power-of-2
+  // CHECK-MESSAGES: :65:11: note: used here as a bitmask
+  R = 4,
+  S = 8,
+  T = 16,
+  U = 31
+};
+
+enum {
+  H,
+  I,
+  J,
+  K,
+  L
+};
+
+enum Days {
+  Monday,
+  Tuesday,
+  Wednesday,
+  Thursday,
+  Friday,
+  Saturday,
+  Sunday
+};
+
+Days bestDay() {
+  return Friday;
+}
+
+int trigger() {
+  if (bestDay() | A)
+    return 1;
+  // CHECK-MESSAGES: :[[@LINE-2]]:17: warning: enum values are from different enum types
+  if (I | Y)
+    return 1;
+  // CHECK-MESSAGES: :[[@LINE-2]]:9: warning: enum values are from different enum types
+  if (P + Q == R)
+    return 1;
+  else if ((S | R) == T)
+    return 1;
+  else
+    int k = ZZ | Z;
+  unsigned p = R;
+  PP pp = Q;
+  p |= pp;
+  
+  enum X x = Z;
+  p = x | Z;
+  return 0;
+}
+
+int dont_trigger() {
+  int a = 1, b = 5;
+  int c = a + b;
+  int d = c | H, e = b * a;
+  a = B | C;
+  b = X | Z;
+
+  unsigned bitflag;
+  enum A aa = B;
+  bitflag = aa | C;
+
+  if (Tuesday != Monday + 1 ||
+      Friday - Thursday != 1 ||
+      Sunday + Wednesday == (Sunday | Wednesday))
+    return 1;
+  if (H + I + L == 42)
+    return 1;
+  return 42;
+}
diff --git a/test/clang-tidy/misc-suspicious-enum-usage.cpp b/test/clang-tidy/misc-suspicious-enum-usage.cpp
new file mode 100644 (file)
index 0000000..27e78f5
--- /dev/null
@@ -0,0 +1,90 @@
+// RUN: %check_clang_tidy %s misc-suspicious-enum-usage %t -- -config="{CheckOptions: [{key: misc-suspicious-enum-usage.StrictMode, value: 0}]}" --
+
+enum Empty {
+};
+
+enum A {
+  A = 1,
+  B = 2,
+  C = 4,
+  D = 8,
+  E = 16,
+  F = 32,
+  G = 63
+};
+
+enum X {
+  X = 8,
+  Y = 16,
+  Z = 4
+};
+
+enum {
+  P = 2,
+  Q = 3,
+  R = 4,
+  S = 8,
+  T = 16
+};
+
+enum {
+  H,
+  I,
+  J,
+  K,
+  L
+};
+
+enum Days {
+  Monday,
+  Tuesday,
+  Wednesday,
+  Thursday,
+  Friday,
+  Saturday,
+  Sunday
+};
+
+Days bestDay() {
+  return Friday;
+}
+
+int trigger() {
+  Empty EmptyVal;
+  int emptytest = EmptyVal | B;
+  if (bestDay() | A)
+    return 1;
+  // CHECK-MESSAGES: :[[@LINE-2]]:17: warning: enum values are from different enum types 
+  if (I | Y)
+    return 1;
+  // CHECK-MESSAGES: :[[@LINE-2]]:9: warning: enum values are from different enum types
+}
+
+int dont_trigger() {
+  unsigned p;
+  p = Q | P;
+
+  if (A + G == E)
+    return 1;
+  else if ((Q | R) == T)
+    return 1;
+  else
+    int k = T | Q;
+
+  Empty EmptyVal;
+  int emptytest = EmptyVal | B;
+
+  int a = 1, b = 5;
+  int c = a + b;
+  int d = c | H, e = b * a;
+  a = B | C;
+  b = X | Z;
+  
+  if (Tuesday != Monday + 1 ||
+      Friday - Thursday != 1 ||
+      Sunday + Wednesday == (Sunday | Wednesday))
+    return 1;
+  if (H + I + L == 42)
+    return 1;
+  return 42;
+}
diff --git a/test/clang-tidy/misc-suspicious-missing-comma.cpp b/test/clang-tidy/misc-suspicious-missing-comma.cpp
new file mode 100644 (file)
index 0000000..0744d8f
--- /dev/null
@@ -0,0 +1,82 @@
+// RUN: %check_clang_tidy %s misc-suspicious-missing-comma %t
+
+const char* Cartoons[] = {
+  "Bugs Bunny",
+  "Homer Simpson",
+  "Mickey Mouse",
+  "Bart Simpson",
+  "Charlie Brown"  // There is a missing comma here.
+  "Fred Flintstone",
+  "Popeye",
+};
+// CHECK-MESSAGES: :[[@LINE-4]]:3: warning: suspicious string literal, probably missing a comma [misc-suspicious-missing-comma]
+
+const wchar_t* Colors[] = {
+  L"Red", L"Yellow", L"Blue", L"Green", L"Purple", L"Rose", L"White", L"Black"
+};
+
+// The following array should not trigger any warnings. There is more than 5
+// elements, but they are all concatenated string literals.
+const char* HttpCommands[] = {
+  "GET / HTTP/1.0\r\n"
+  "\r\n",
+
+  "GET /index.html HTTP/1.0\r\n"
+  "\r\n",
+
+  "GET /favicon.ico HTTP/1.0\r\n"
+  "header: dummy"
+  "\r\n",
+
+  "GET /index.html-en HTTP/1.0\r\n"
+  "\r\n",
+
+  "GET /index.html-fr HTTP/1.0\r\n"
+  "\r\n",
+
+  "GET /index.html-es HTTP/1.0\r\n"
+  "\r\n",
+};
+
+// This array is too small to trigger a warning.
+const char* SmallArray[] = {
+  "a" "b", "c"
+};
+
+// Parentheses should be enough to avoid warnings.
+const char* ParentheseArray[] = {
+  ("a" "b"), "c",
+  ("d"
+   "e"
+   "f"),
+  "g", "h", "i", "j", "k", "l"
+};
+
+// Indentation should be enough to avoid warnings.
+const char* CorrectlyIndentedArray[] = {
+  "This is a long message "
+      "which is spanning over multiple lines."
+      "And this should be fine.",
+  "a", "b", "c", "d", "e", "f",
+  "g", "h", "i", "j", "k", "l"
+};
+
+const char* IncorrectlyIndentedArray[] = {
+  "This is a long message "
+  "which is spanning over multiple lines."
+      "And this should be fine.",
+  "a", "b", "c", "d", "e", "f",
+  "g", "h", "i", "j", "k", "l"
+};
+// CHECK-MESSAGES: :[[@LINE-6]]:3: warning: suspicious string literal, probably missing a comma [misc-suspicious-missing-comma]
+
+const char* TooManyConcatenatedTokensArray[] = {
+  "Dummy line",
+  "Dummy line",
+  "a" "b" "c" "d" "e" "f",
+  "g" "h" "i" "j" "k" "l",
+  "Dummy line",
+  "Dummy line",
+  "Dummy line",
+  "Dummy line",
+};
diff --git a/test/clang-tidy/misc-suspicious-semicolon-fail.cpp b/test/clang-tidy/misc-suspicious-semicolon-fail.cpp
new file mode 100644 (file)
index 0000000..e4e7ae5
--- /dev/null
@@ -0,0 +1,26 @@
+// RUN: clang-tidy %s -checks="-*,misc-suspicious-semicolon" -- -DERROR 2>&1 \
+// RUN:   | FileCheck %s -check-prefix=CHECK-ERROR \
+// RUN:       -implicit-check-not="{{warning|error}}:"
+// RUN: clang-tidy %s -checks="-*,misc-suspicious-semicolon,clang-diagnostic*" \
+// RUN:    -- -DWERROR -Wno-everything -Werror=unused-variable 2>&1 \
+// RUN:   | FileCheck %s -check-prefix=CHECK-WERROR \
+// RUN:       -implicit-check-not="{{warning|error}}:"
+
+// Note: This test verifies that, the checker does not emit any warning for
+//       files that do not compile.
+
+bool g();
+
+void f() {
+  if (g());
+  // CHECK-WERROR: :[[@LINE-1]]:11: warning: potentially unintended semicolon [misc-suspicious-semicolon]
+#if ERROR
+  int a
+  // CHECK-ERROR: :[[@LINE-1]]:8: error: expected ';' at end of declaration [clang-diagnostic-error]
+#elif WERROR
+  int a;
+  // CHECK-WERROR: :[[@LINE-1]]:7: error: unused variable 'a' [clang-diagnostic-unused-variable]
+#else
+#error "One of ERROR or WERROR should be defined.
+#endif
+}
diff --git a/test/clang-tidy/misc-suspicious-semicolon.cpp b/test/clang-tidy/misc-suspicious-semicolon.cpp
new file mode 100644 (file)
index 0000000..77da2c9
--- /dev/null
@@ -0,0 +1,117 @@
+// RUN: %check_clang_tidy %s misc-suspicious-semicolon %t
+
+int x = 5;
+
+void nop();
+
+void correct1()
+{
+       if(x < 5) nop();
+}
+
+void correct2()
+{
+       if(x == 5)
+               nop();
+}
+
+void correct3()
+{
+       if(x > 5)
+       {
+               nop();
+       }
+}
+
+void fail1()
+{
+  if(x > 5); nop();
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: potentially unintended semicolon [misc-suspicious-semicolon]
+  // CHECK-FIXES: if(x > 5) nop();
+}
+
+void fail2()
+{
+       if(x == 5);
+               nop();
+  // CHECK-MESSAGES: :[[@LINE-2]]:12: warning: potentially unintended semicolon [misc-suspicious-semicolon]
+  // CHECK-FIXES: if(x == 5){{$}}
+}
+
+void fail3()
+{
+       if(x < 5);
+       {
+               nop();
+       }
+  // CHECK-MESSAGES: :[[@LINE-4]]:11: warning: potentially unintended semicolon
+  // CHECK-FIXES: if(x < 5){{$}}
+}
+
+void correct4()
+{
+  while(x % 5 == 1);
+  nop();
+}
+
+void correct5()
+{
+       for(int i = 0; i < x; ++i)
+               ;
+}
+
+void fail4()
+{
+       for(int i = 0; i < x; ++i);
+               nop();
+  // CHECK-MESSAGES: :[[@LINE-2]]:28: warning: potentially unintended semicolon
+  // CHECK-FIXES: for(int i = 0; i < x; ++i){{$}}
+}
+
+void fail5()
+{
+       if(x % 5 == 1);
+         nop();
+  // CHECK-MESSAGES: :[[@LINE-2]]:16: warning: potentially unintended semicolon
+  // CHECK-FIXES: if(x % 5 == 1){{$}}
+}
+
+void fail6() {
+  int a = 0;
+  if (a != 0) {
+  } else if (a != 1);
+    a = 2;
+  // CHECK-MESSAGES: :[[@LINE-2]]:21: warning: potentially unintended semicolon
+  // CHECK-FIXES: } else if (a != 1){{$}}
+}
+
+void fail7() {
+  if (true)
+    ;
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: potentially unintended semicolon
+}
+
+void correct6()
+{
+       do; while(false);
+}
+
+int correct7()
+{
+  int t_num = 0;
+  char c = 'b';
+  char *s = "a";
+  if (s == "(" || s != "'" || c == '"') {
+    t_num += 3;
+    return (c == ')' && c == '\'');
+  }
+
+  return 0;
+}
+
+void correct8() {
+  if (true)
+    ;
+  else {
+  }
+}
diff --git a/test/clang-tidy/misc-suspicious-string-compare.c b/test/clang-tidy/misc-suspicious-string-compare.c
new file mode 100644 (file)
index 0000000..d1dbf9e
--- /dev/null
@@ -0,0 +1,79 @@
+// RUN: %check_clang_tidy %s misc-suspicious-string-compare %t -- \
+// RUN: -config='{CheckOptions: \
+// RUN:  [{key: misc-suspicious-string-compare.WarnOnImplicitComparison, value: 1}, \
+// RUN:   {key: misc-suspicious-string-compare.WarnOnLogicalNotComparison, value: 1}]}' \
+// RUN: -- -std=c99
+
+static const char A[] = "abc";
+
+int strcmp(const char *, const char *);
+
+int test_warning_patterns() {
+  if (strcmp(A, "a"))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcmp' is called without explicitly comparing result [misc-suspicious-string-compare]
+  // CHECK-FIXES: if (strcmp(A, "a") != 0)
+
+  if (strcmp(A, "a") != 0 ||
+      strcmp(A, "b"))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcmp' is called without explicitly comparing result
+  // CHECK-FIXES: strcmp(A, "b") != 0)
+
+  if (strcmp(A, "a") == 1)
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcmp' is compared to a suspicious constant
+
+  if (strcmp(A, "a") == -1)
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcmp' is compared to a suspicious constant
+
+  if (strcmp(A, "a") < '0')
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcmp' is compared to a suspicious constant
+
+  if (strcmp(A, "a") < 0.)
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcmp' has suspicious implicit cast
+
+  if (!strcmp(A, "a"))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:8: warning: function 'strcmp' is compared using logical not operator
+  // CHECK-FIXES: if (strcmp(A, "a") == 0)
+}
+
+void test_structure_patterns() {
+  if (strcmp(A, "a")) {}
+  // CHECK-MESSAGES: [[@LINE-1]]:7: warning: function 'strcmp' is called without explicitly comparing result
+  // CHECK-FIXES: if (strcmp(A, "a") != 0) {}
+
+  while (strcmp(A, "a")) {}
+  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: function 'strcmp' is called without explicitly comparing result
+  // CHECK-FIXES: while (strcmp(A, "a") != 0) {}
+
+  for (;strcmp(A, "a");) {}
+  // CHECK-MESSAGES: [[@LINE-1]]:9: warning: function 'strcmp' is called without explicitly comparing result
+  // CHECK-FIXES: for (;strcmp(A, "a") != 0;) {}
+}
+
+int test_valid_patterns() {
+  // The following cases are valid.
+  if (strcmp(A, "a") < 0) return 0;
+  if (strcmp(A, "a") == 0) return 0;
+  if (strcmp(A, "a") <= 0) return 0;
+  if (strcmp(A, "a") == strcmp(A, "b")) return 0;
+  return 1;
+}
+
+int wrapper(const char* a, const char* b) {
+  return strcmp(a, b);
+}
+
+int assignment_wrapper(const char* a, const char* b) {
+  int cmp = strcmp(a, b);
+  return cmp;
+}
+
+int condexpr_wrapper(const char* a, const char* b) {
+  return (a < b) ? strcmp(a, b) : strcmp(b, a);
+}
diff --git a/test/clang-tidy/misc-suspicious-string-compare.cpp b/test/clang-tidy/misc-suspicious-string-compare.cpp
new file mode 100644 (file)
index 0000000..4c51676
--- /dev/null
@@ -0,0 +1,337 @@
+// RUN: %check_clang_tidy %s misc-suspicious-string-compare %t -- \
+// RUN: -config='{CheckOptions: \
+// RUN:  [{key: misc-suspicious-string-compare.WarnOnImplicitComparison, value: 1}, \
+// RUN:   {key: misc-suspicious-string-compare.WarnOnLogicalNotComparison, value: 1}]}' \
+// RUN: --
+
+typedef __SIZE_TYPE__ size;
+
+struct locale_t {
+  void* dummy;
+} locale;
+
+static const char A[] = "abc";
+static const unsigned char U[] = "abc";
+static const unsigned char V[] = "xyz";
+static const wchar_t W[] = L"abc";
+
+int strlen(const char *);
+
+int memcmp(const void *, const void *, size);
+int wmemcmp(const wchar_t *, const wchar_t *, size);
+int memicmp(const void *, const void *, size);
+int _memicmp(const void *, const void *, size);
+int _memicmp_l(const void *, const void *, size, locale_t);
+
+int strcmp(const char *, const char *);
+int strncmp(const char *, const char *, size);
+int strcasecmp(const char *, const char *);
+int strncasecmp(const char *, const char *, size);
+int stricmp(const char *, const char *);
+int strcmpi(const char *, const char *);
+int strnicmp(const char *, const char *, size);
+int _stricmp(const char *, const char * );
+int _strnicmp(const char *, const char *, size);
+int _stricmp_l(const char *, const char *, locale_t);
+int _strnicmp_l(const char *, const char *, size, locale_t);
+
+int wcscmp(const wchar_t *, const wchar_t *);
+int wcsncmp(const wchar_t *, const wchar_t *, size);
+int wcscasecmp(const wchar_t *, const wchar_t *);
+int wcsicmp(const wchar_t *, const wchar_t *);
+int wcsnicmp(const wchar_t *, const wchar_t *, size);
+int _wcsicmp(const wchar_t *, const wchar_t *);
+int _wcsnicmp(const wchar_t *, const wchar_t *, size);
+int _wcsicmp_l(const wchar_t *, const wchar_t *, locale_t);
+int _wcsnicmp_l(const wchar_t *, const wchar_t *, size, locale_t);
+
+int _mbscmp(const unsigned char *, const unsigned char *);
+int _mbsncmp(const unsigned char *, const unsigned char *, size);
+int _mbsnbcmp(const unsigned char *, const unsigned char *, size);
+int _mbsnbicmp(const unsigned char *, const unsigned char *, size);
+int _mbsicmp(const unsigned char *, const unsigned char *);
+int _mbsnicmp(const unsigned char *, const unsigned char *, size);
+int _mbscmp_l(const unsigned char *, const unsigned char *, locale_t);
+int _mbsncmp_l(const unsigned char *, const unsigned char *, size, locale_t);
+int _mbsicmp_l(const unsigned char *, const unsigned char *, locale_t);
+int _mbsnicmp_l(const unsigned char *, const unsigned char *, size, locale_t);
+int _mbsnbcmp_l(const unsigned char *, const unsigned char *, size, locale_t);
+int _mbsnbicmp_l(const unsigned char *, const unsigned char *, size, locale_t);
+
+int test_warning_patterns() {
+  if (strcmp(A, "a"))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcmp' is called without explicitly comparing result [misc-suspicious-string-compare]
+  // CHECK-FIXES: if (strcmp(A, "a") != 0)
+
+  if (strcmp(A, "a") == 0 ||
+      strcmp(A, "b"))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcmp' is called without explicitly comparing result
+  // CHECK-FIXES: strcmp(A, "b") != 0)
+
+  if (strcmp(A, "a") == 1)
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcmp' is compared to a suspicious constant
+
+  if (strcmp(A, "a") == -1)
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcmp' is compared to a suspicious constant
+
+  if (strcmp(A, "a") == true)
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcmp' is compared to a suspicious constant
+
+  if (strcmp(A, "a") < '0')
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcmp' is compared to a suspicious constant
+
+  if (strcmp(A, "a") < 0.)
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcmp' has suspicious implicit cast
+}
+
+int test_valid_patterns() {
+  // The following cases are valid.
+  if (strcmp(A, "a") < 0)
+    return 0;
+  if (strcmp(A, "a") == 0)
+    return 0;
+  if (strcmp(A, "a") <= 0)
+    return 0;
+
+  if (wcscmp(W, L"a") < 0)
+    return 0;
+  if (wcscmp(W, L"a") == 0)
+    return 0;
+  if (wcscmp(W, L"a") <= 0)
+    return 0;
+
+  return 1;
+}
+
+int test_implicit_compare_with_functions() {
+
+  if (memcmp(A, "a", 1))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'memcmp' is called without explicitly comparing result
+  // CHECK-FIXES: memcmp(A, "a", 1) != 0)
+
+  if (wmemcmp(W, L"a", 1))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'wmemcmp' is called without explicitly comparing result
+  // CHECK-FIXES: wmemcmp(W, L"a", 1) != 0)
+
+  if (memicmp(A, "a", 1))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'memicmp' is called without explicitly comparing result
+  // CHECK-FIXES: memicmp(A, "a", 1) != 0)
+
+  if (_memicmp(A, "a", 1))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_memicmp' is called without explicitly comparing result
+  // CHECK-FIXES: _memicmp(A, "a", 1) != 0)
+
+  if (_memicmp_l(A, "a", 1, locale))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_memicmp_l' is called without explicitly comparing result
+  // CHECK-FIXES: _memicmp_l(A, "a", 1, locale) != 0)
+
+  if (strcmp(A, "a"))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcmp' is called without explicitly comparing result
+  // CHECK-FIXES: strcmp(A, "a") != 0)
+
+  if (strncmp(A, "a", 1))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strncmp' is called without explicitly comparing result
+  // CHECK-FIXES: strncmp(A, "a", 1) != 0)
+
+  if (strcasecmp(A, "a"))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcasecmp' is called without explicitly comparing result
+  // CHECK-FIXES: strcasecmp(A, "a") != 0)
+
+  if (strncasecmp(A, "a", 1))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strncasecmp' is called without explicitly comparing result
+  // CHECK-FIXES: strncasecmp(A, "a", 1) != 0)
+
+  if (stricmp(A, "a"))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'stricmp' is called without explicitly comparing result
+  // CHECK-FIXES: stricmp(A, "a") != 0)
+
+  if (strcmpi(A, "a"))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcmpi' is called without explicitly comparing result
+  // CHECK-FIXES: strcmpi(A, "a") != 0)
+
+  if (_stricmp(A, "a"))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_stricmp' is called without explicitly comparing result
+  // CHECK-FIXES: _stricmp(A, "a") != 0)
+
+  if (strnicmp(A, "a", 1))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strnicmp' is called without explicitly comparing result
+  // CHECK-FIXES: strnicmp(A, "a", 1) != 0)
+
+  if (_strnicmp(A, "a", 1))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_strnicmp' is called without explicitly comparing result
+  // CHECK-FIXES: _strnicmp(A, "a", 1) != 0)
+
+  if (_stricmp_l(A, "a", locale))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_stricmp_l' is called without explicitly comparing result
+  // CHECK-FIXES: _stricmp_l(A, "a", locale) != 0)
+
+  if (_strnicmp_l(A, "a", 1, locale))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_strnicmp_l' is called without explicitly comparing result
+  // CHECK-FIXES: _strnicmp_l(A, "a", 1, locale) != 0)
+
+  if (wcscmp(W, L"a"))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'wcscmp' is called without explicitly comparing result
+  // CHECK-FIXES: wcscmp(W, L"a") != 0)
+
+  if (wcsncmp(W, L"a", 1))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'wcsncmp' is called without explicitly comparing result
+  // CHECK-FIXES: wcsncmp(W, L"a", 1) != 0)
+
+  if (wcscasecmp(W, L"a"))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'wcscasecmp' is called without explicitly comparing result
+  // CHECK-FIXES: wcscasecmp(W, L"a") != 0)
+
+  if (wcsicmp(W, L"a"))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'wcsicmp' is called without explicitly comparing result
+  // CHECK-FIXES: wcsicmp(W, L"a") != 0)
+
+  if (_wcsicmp(W, L"a"))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_wcsicmp' is called without explicitly comparing result
+  // CHECK-FIXES: _wcsicmp(W, L"a") != 0)
+
+  if (_wcsicmp_l(W, L"a", locale))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_wcsicmp_l' is called without explicitly comparing result
+  // CHECK-FIXES: _wcsicmp_l(W, L"a", locale) != 0)
+
+  if (wcsnicmp(W, L"a", 1))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'wcsnicmp' is called without explicitly comparing result
+  // CHECK-FIXES: wcsnicmp(W, L"a", 1) != 0)
+
+  if (_wcsnicmp(W, L"a", 1))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_wcsnicmp' is called without explicitly comparing result
+  // CHECK-FIXES: _wcsnicmp(W, L"a", 1) != 0)
+
+  if (_wcsnicmp_l(W, L"a", 1, locale))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_wcsnicmp_l' is called without explicitly comparing result
+  // CHECK-FIXES: _wcsnicmp_l(W, L"a", 1, locale) != 0)
+
+  if (_mbscmp(U, V))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_mbscmp' is called without explicitly comparing result
+  // CHECK-FIXES: _mbscmp(U, V) != 0)
+
+  if (_mbsncmp(U, V, 1))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_mbsncmp' is called without explicitly comparing result
+  // CHECK-FIXES: _mbsncmp(U, V, 1) != 0)
+
+  if (_mbsnbcmp(U, V, 1))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_mbsnbcmp' is called without explicitly comparing result
+  // CHECK-FIXES: _mbsnbcmp(U, V, 1) != 0)
+
+  if (_mbsnbicmp(U, V, 1))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_mbsnbicmp' is called without explicitly comparing result
+  // CHECK-FIXES: _mbsnbicmp(U, V, 1) != 0)
+
+  if (_mbsicmp(U, V))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_mbsicmp' is called without explicitly comparing result
+  // CHECK-FIXES: _mbsicmp(U, V) != 0)
+
+  if (_mbsnicmp(U, V, 1))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_mbsnicmp' is called without explicitly comparing result
+  // CHECK-FIXES: _mbsnicmp(U, V, 1) != 0)
+
+  if (_mbscmp_l(U, V, locale))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_mbscmp_l' is called without explicitly comparing result
+  // CHECK-FIXES: _mbscmp_l(U, V, locale) != 0)
+
+  if (_mbsncmp_l(U, V, 1, locale))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_mbsncmp_l' is called without explicitly comparing result
+  // CHECK-FIXES: _mbsncmp_l(U, V, 1, locale) != 0)
+
+  if (_mbsicmp_l(U, V, locale))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_mbsicmp_l' is called without explicitly comparing result
+  // CHECK-FIXES: _mbsicmp_l(U, V, locale) != 0)
+
+  if (_mbsnicmp_l(U, V, 1, locale))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_mbsnicmp_l' is called without explicitly comparing result
+  // CHECK-FIXES: _mbsnicmp_l(U, V, 1, locale) != 0)
+
+  if (_mbsnbcmp_l(U, V, 1, locale))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_mbsnbcmp_l' is called without explicitly comparing result
+  // CHECK-FIXES: _mbsnbcmp_l(U, V, 1, locale) != 0)
+
+  if (_mbsnbicmp_l(U, V, 1, locale))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_mbsnbicmp_l' is called without explicitly comparing result
+  // CHECK-FIXES: _mbsnbicmp_l(U, V, 1, locale) != 0)
+
+  return 1;
+}
+
+int strcmp_wrapper1(const char* a, const char* b) {
+  return strcmp(a, b);
+}
+
+int strcmp_wrapper2(const char* a, const char* b) {
+  return (a && b) ? strcmp(a, b) : 0;
+}
+
+#define macro_strncmp(s1, s2, n)                                              \
+  (__extension__ (__builtin_constant_p (n)                                    \
+                  && ((__builtin_constant_p (s1)                              \
+                       && strlen (s1) < ((size) (n)))                         \
+                      || (__builtin_constant_p (s2)                           \
+                          && strlen (s2) < ((size) (n))))                     \
+                  ? strcmp (s1, s2) : strncmp (s1, s2, n)))
+
+int strncmp_macro(const char* a, const char* b) {
+  if (macro_strncmp(a, b, 4))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcmp' is called without explicitly comparing result
+
+  if (macro_strncmp(a, b, 4) == 2)
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcmp' is compared to a suspicious constant
+
+  if (macro_strncmp(a, b, 4) <= .0)
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcmp' has suspicious implicit cast
+
+  if (macro_strncmp(a, b, 4) + 0)
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: results of function 'strcmp' used by operator '+'
+
+  return 1;
+}
diff --git a/test/clang-tidy/misc-swapped-arguments.cpp b/test/clang-tidy/misc-swapped-arguments.cpp
new file mode 100644 (file)
index 0000000..4f1f0ea
--- /dev/null
@@ -0,0 +1,53 @@
+// RUN: %check_clang_tidy %s misc-swapped-arguments %t
+
+void F(int, double);
+
+int SomeFunction();
+
+template <typename T, typename U>
+void G(T a, U b) {
+  F(a, b); // no-warning
+  F(2.0, 4);
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: argument with implicit conversion from 'int' to 'double' followed by argument converted from 'double' to 'int', potentially swapped arguments.
+// CHECK-FIXES: F(4, 2.0)
+}
+
+void foo() {
+  F(1.0, 3);
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: argument with implicit conversion from 'int' to 'double' followed by argument converted from 'double' to 'int', potentially swapped arguments.
+// CHECK-FIXES: F(3, 1.0)
+
+#define M(x, y) x##y()
+
+  double b = 1.0;
+  F(b, M(Some, Function));
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: argument with implicit conversion from 'int' to 'double' followed by argument converted from 'double' to 'int', potentially swapped arguments.
+// CHECK-FIXES: F(M(Some, Function), b);
+
+#define N F(b, SomeFunction())
+
+  N;
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: argument with implicit conversion from 'int' to 'double' followed by argument converted from 'double' to 'int', potentially swapped arguments.
+// In macro, don't emit fixits.
+// CHECK-FIXES: #define N F(b, SomeFunction())
+
+  G(b, 3);
+  G(3, 1.0);
+  G(0, 0);
+
+  F(1.0, 1.0);    // no-warning
+  F(3, 1.0);      // no-warning
+  F(true, false); // no-warning
+  F(0, 'c');      // no-warning
+
+#define APPLY(f, x, y) f(x, y)
+  APPLY(F, 1.0, 3);
+// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: argument with implicit conversion from 'int' to 'double' followed by argument converted from 'double' to 'int', potentially swapped arguments.
+// CHECK-FIXES: APPLY(F, 3, 1.0);
+
+#define PARAMS 1.0, 3
+#define CALL(P) F(P)
+  CALL(PARAMS);
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: argument with implicit conversion from 'int' to 'double' followed by argument converted from 'double' to 'int', potentially swapped arguments.
+// In macro, don't emit fixits.
+}
diff --git a/test/clang-tidy/misc-throw-by-value-catch-by-reference.cpp b/test/clang-tidy/misc-throw-by-value-catch-by-reference.cpp
new file mode 100644 (file)
index 0000000..120b07e
--- /dev/null
@@ -0,0 +1,156 @@
+// RUN: %check_clang_tidy %s misc-throw-by-value-catch-by-reference %t -- -- -std=c++11 -fcxx-exceptions
+
+
+class logic_error {
+public:
+  logic_error(const char *message) {}
+};
+
+typedef logic_error *logic_ptr;
+typedef logic_ptr logic_double_typedef;
+
+int lastException;
+
+template <class T> struct remove_reference { typedef T type; };
+template <class T> struct remove_reference<T &> { typedef T type; };
+template <class T> struct remove_reference<T &&> { typedef T type; };
+
+template <typename T> typename remove_reference<T>::type &&move(T &&arg) {
+  return static_cast<typename remove_reference<T>::type &&>(arg);
+}
+
+logic_error CreateException() { return logic_error("created"); }
+
+void testThrowFunc() {
+  throw new logic_error("by pointer");
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: throw expression throws a pointer; it should throw a non-pointer value instead [misc-throw-by-value-catch-by-reference]
+  logic_ptr tmp = new logic_error("by pointer");
+  throw tmp;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: throw expression should throw anonymous temporary values instead [misc-throw-by-value-catch-by-reference]
+  // CHECK-MESSAGES: :[[@LINE-2]]:9: warning: throw expression throws a pointer; it should throw a non-pointer value instead [misc-throw-by-value-catch-by-reference]
+  throw logic_error("by value");
+  auto *literal = "test";
+  throw logic_error(literal);
+  throw "test string literal";
+  throw L"throw wide string literal";
+  const char *characters = 0;
+  throw characters;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: throw expression should throw anonymous temporary values instead [misc-throw-by-value-catch-by-reference]
+  // CHECK-MESSAGES: :[[@LINE-2]]:9: warning: throw expression throws a pointer; it should throw a non-pointer value instead [misc-throw-by-value-catch-by-reference]
+  logic_error lvalue("lvalue");
+  throw lvalue;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: throw expression should throw anonymous temporary values instead [misc-throw-by-value-catch-by-reference]
+
+  throw move(lvalue);
+  int &ex = lastException;
+  throw ex;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: throw expression should throw anonymous temporary values instead [misc-throw-by-value-catch-by-reference]
+  throw CreateException();
+}
+
+void throwReferenceFunc(logic_error &ref) { throw ref; }
+
+void catchByPointer() {
+  try {
+    testThrowFunc();
+  } catch (logic_error *e) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: catch handler catches a pointer value; should throw a non-pointer value and catch by reference instead [misc-throw-by-value-catch-by-reference]
+  }
+}
+
+void catchByValue() {
+  try {
+    testThrowFunc();
+  } catch (logic_error e) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: catch handler catches by value; should catch by reference instead [misc-throw-by-value-catch-by-reference]
+  }
+}
+
+void catchByReference() {
+  try {
+    testThrowFunc();
+  } catch (logic_error &e) {
+  }
+}
+
+void catchByConstReference() {
+  try {
+    testThrowFunc();
+  } catch (const logic_error &e) {
+  }
+}
+
+void catchTypedef() {
+  try {
+    testThrowFunc();
+  } catch (logic_ptr) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: catch handler catches a pointer value; should throw a non-pointer value and catch by reference instead [misc-throw-by-value-catch-by-reference]
+  }
+}
+
+void catchAll() {
+  try {
+    testThrowFunc();
+  } catch (...) {
+  }
+}
+
+void catchLiteral() {
+  try {
+    testThrowFunc();
+  } catch (const char *) {
+  } catch (const wchar_t *) {
+    // disabled for now until it is clear
+    // how to enable them in the test
+    //} catch (const char16_t*) {
+    //} catch (const char32_t*) {
+  }
+}
+
+// catching fundamentals should not warn
+void catchFundamental() {
+  try {
+    testThrowFunc();
+  } catch (int) {
+  } catch (double) {
+  } catch (unsigned long) {
+  }
+}
+
+struct TrivialType {
+  double x;
+  double y;
+};
+
+void catchTrivial() {
+  try {
+    testThrowFunc();
+  } catch (TrivialType) {
+  }
+}
+
+typedef logic_error &fine;
+void additionalTests() {
+  try {
+  } catch (int i) {  // ok
+    throw i;         // ok
+  } catch (fine e) { // ok
+    throw e;         // ok
+  } catch (logic_error *e) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: catch handler catches a pointer value; should throw a non-pointer value and catch by reference instead [misc-throw-by-value-catch-by-reference]
+    throw e;      // ok, despite throwing a pointer
+  } catch (...) { // ok
+    throw;        // ok
+  }
+}
+
+struct S {};
+
+S &returnByReference();
+S returnByValue();
+
+void f() {
+  throw returnByReference(); // Should diagnose
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: throw expression should throw anonymous temporary values instead [misc-throw-by-value-catch-by-reference]
+  throw returnByValue(); // Should not diagnose
+}
diff --git a/test/clang-tidy/misc-unconventional-assign-operator.cpp b/test/clang-tidy/misc-unconventional-assign-operator.cpp
new file mode 100644 (file)
index 0000000..4bd9cb2
--- /dev/null
@@ -0,0 +1,111 @@
+// RUN: %check_clang_tidy %s misc-unconventional-assign-operator %t -- -- -std=c++11 -isystem %S/Inputs/Headers -fno-delayed-template-parsing
+
+namespace std {
+template <typename T>
+struct remove_reference { typedef T type; };
+template <typename T>
+struct remove_reference<T &> { typedef T type; };
+template <typename T>
+struct remove_reference<T &&> { typedef T type; };
+template <typename T>
+typename remove_reference<T>::type &&move(T &&t);
+}
+
+
+struct Good {
+  Good& operator=(const Good&);
+  Good& operator=(Good&&);
+
+  // Assign from other types is fine too.
+  Good& operator=(int);
+};
+
+struct AlsoGood {
+  // By value is also fine.
+  AlsoGood& operator=(AlsoGood);
+};
+
+struct BadReturnType {
+  void operator=(const BadReturnType&);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: operator=() should return 'BadReturnType&' [misc-unconventional-assign-operator]
+  const BadReturnType& operator=(BadReturnType&&);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: operator=() should return 'Bad
+  void operator=(int);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: operator=() should return 'Bad
+};
+
+struct BadReturnType2 {
+  BadReturnType2&& operator=(const BadReturnType2&);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: operator=() should return 'Bad
+  int operator=(BadReturnType2&&);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: operator=() should return 'Bad
+};
+
+struct BadArgument {
+  BadArgument& operator=(BadArgument&);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: operator=() should take 'BadArgument const&', 'BadArgument&&' or 'BadArgument'
+  BadArgument& operator=(const BadArgument&&);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: operator=() should take 'BadAr
+};
+
+struct BadModifier {
+  BadModifier& operator=(const BadModifier&) const;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: operator=() should not be marked 'const'
+};
+
+struct Deleted {
+  // We don't check the return value of deleted operators.
+  void operator=(const Deleted&) = delete;
+  void operator=(Deleted&&) = delete;
+};
+
+class Private {
+  // We don't check the return value of private operators.
+  // Pre-C++11 way of disabling assignment.
+  void operator=(const Private &);
+};
+
+struct Virtual {
+  virtual Virtual& operator=(const Virtual &);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: operator=() should not be marked 'virtual'
+};
+
+class BadReturnStatement {
+  int n;
+
+public:
+  BadReturnStatement& operator=(BadReturnStatement&& rhs) {
+    n = std::move(rhs.n);
+    return rhs;
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: operator=() should always return '*this'
+  }
+
+  // Do not check if return type is different from '&BadReturnStatement'
+  int operator=(int i) {
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: operator=() should return 'Bad
+    n = i;
+    return n;
+  }
+};
+
+namespace pr31531 {
+enum E { e };
+// This declaration makes the 'return *this' below have an unresolved operator
+// in the class template, but not in an instantiation.
+E operator*(E, E);
+
+template <typename>
+struct UnresolvedOperator {
+  UnresolvedOperator &operator=(const UnresolvedOperator &) { return *this; }
+};
+
+UnresolvedOperator<int> UnresolvedOperatorInt;
+
+template <typename>
+struct Template {
+  Template &operator=(const Template &) { return this; }
+  // CHECK-MESSAGES: :[[@LINE-1]]:43: warning: operator=() should always return '*this'
+};
+
+Template<int> TemplateInt;
+}
diff --git a/test/clang-tidy/misc-undelegated-constructor-cxx98.cpp b/test/clang-tidy/misc-undelegated-constructor-cxx98.cpp
new file mode 100644 (file)
index 0000000..78b53bb
--- /dev/null
@@ -0,0 +1,23 @@
+// RUN: clang-tidy %s -checks=-*,misc-undelegated-constructor -- -std=c++98 | count 0
+
+// Note: this test expects no diagnostics, but FileCheck cannot handle that,
+// hence the use of | count 0.
+
+struct Ctor;
+Ctor foo();
+
+struct Ctor {
+  Ctor();
+  Ctor(int);
+  Ctor(int, int);
+  Ctor(Ctor *i) {
+    Ctor();
+    Ctor(0);
+    Ctor(1, 2);
+    foo();
+  }
+};
+
+Ctor::Ctor() {
+  Ctor(1);
+}
diff --git a/test/clang-tidy/misc-undelegated-constructor.cpp b/test/clang-tidy/misc-undelegated-constructor.cpp
new file mode 100644 (file)
index 0000000..1b5e3c4
--- /dev/null
@@ -0,0 +1,54 @@
+// RUN: %check_clang_tidy %s misc-undelegated-constructor %t
+
+struct Ctor;
+Ctor foo();
+
+struct Ctor {
+  Ctor();
+  Ctor(int);
+  Ctor(int, int);
+  Ctor(Ctor *i) {
+    Ctor();
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: did you intend to call a delegated constructor? A temporary object is created here instead [misc-undelegated-constructor]
+    Ctor(0);
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: did you intend to call a delegated constructor?
+    Ctor(1, 2);
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: did you intend to call a delegated constructor?
+    foo();
+  }
+};
+
+Ctor::Ctor() {
+  Ctor(1);
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: did you intend to call a delegated constructor?
+}
+
+Ctor::Ctor(int i) : Ctor(i, 1) {} // properly delegated.
+
+struct Dtor {
+  Dtor();
+  Dtor(int);
+  Dtor(int, int);
+  Dtor(Ctor *i) {
+    Dtor();
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: did you intend to call a delegated constructor?
+    Dtor(0);
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: did you intend to call a delegated constructor?
+    Dtor(1, 2);
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: did you intend to call a delegated constructor?
+  }
+  ~Dtor();
+};
+
+struct Base {};
+struct Derived : public Base {
+  Derived() { Base(); }
+// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: did you intend to call a delegated constructor?
+};
+
+template <typename T>
+struct TDerived : public Base {
+  TDerived() { Base(); }
+};
+
+TDerived<int> t;
diff --git a/test/clang-tidy/misc-uniqueptr-reset-release.cpp b/test/clang-tidy/misc-uniqueptr-reset-release.cpp
new file mode 100644 (file)
index 0000000..1bd6e6f
--- /dev/null
@@ -0,0 +1,69 @@
+// RUN: %check_clang_tidy %s misc-uniqueptr-reset-release %t
+
+namespace std {
+
+template <typename T>
+struct default_delete {};
+
+template <typename T, class Deleter = std::default_delete<T>>
+struct unique_ptr {
+  unique_ptr();
+  explicit unique_ptr(T *);
+  template <typename U, typename E>
+  unique_ptr(unique_ptr<U, E> &&);
+  void reset(T *);
+  T *release();
+};
+} // namespace std
+
+struct Foo {};
+struct Bar : Foo {};
+
+std::unique_ptr<Foo> Create();
+std::unique_ptr<Foo> &Look();
+std::unique_ptr<Foo> *Get();
+
+using FooFunc = void (*)(Foo *);
+using BarFunc = void (*)(Bar *);
+
+void f() {
+  std::unique_ptr<Foo> a, b;
+  std::unique_ptr<Bar> c;
+  std::unique_ptr<Foo> *x = &a;
+  std::unique_ptr<Foo> *y = &b;
+
+  a.reset(b.release());
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: prefer ptr1 = std::move(ptr2) over ptr1.reset(ptr2.release()) [misc-uniqueptr-reset-release]
+  // CHECK-FIXES: a = std::move(b);
+  a.reset(c.release());
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: prefer ptr1 = std::move(ptr2)
+  // CHECK-FIXES: a = std::move(c);
+  a.reset(Create().release());
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: prefer ptr = ReturnUnique() over ptr.reset(ReturnUnique().release()) [misc-uniqueptr-reset-release]
+  // CHECK-FIXES: a = Create();
+  x->reset(y->release());
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: prefer ptr1 = std::move(ptr2)
+  // CHECK-FIXES: *x = std::move(*y);
+  Look().reset(Look().release());
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: prefer ptr1 = std::move(ptr2)
+  // CHECK-FIXES: Look() = std::move(Look());
+  Get()->reset(Get()->release());
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: prefer ptr1 = std::move(ptr2)
+  // CHECK-FIXES: *Get() = std::move(*Get());
+
+  std::unique_ptr<Bar, FooFunc> func_a, func_b;
+  func_a.reset(func_b.release());
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: prefer ptr1 = std::move(ptr2)
+  // CHECK-FIXES: func_a = std::move(func_b);
+}
+
+void negatives() {
+  std::unique_ptr<Foo> src;
+  struct OtherDeleter {};
+  std::unique_ptr<Foo, OtherDeleter> dest;
+  dest.reset(src.release());
+
+  std::unique_ptr<Bar, FooFunc> func_a;
+  std::unique_ptr<Bar, BarFunc> func_b;
+  func_a.reset(func_b.release());
+}
diff --git a/test/clang-tidy/misc-unused-alias-decls.cpp b/test/clang-tidy/misc-unused-alias-decls.cpp
new file mode 100644 (file)
index 0000000..6d63df4
--- /dev/null
@@ -0,0 +1,12 @@
+// RUN: %check_clang_tidy %s misc-unused-alias-decls %t
+
+namespace my_namespace {
+class C {};
+}
+
+namespace unused_alias = ::my_namespace; // eol-comments aren't removed (yet)
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: namespace alias decl 'unused_alias' is unused
+// CHECK-FIXES: {{^}}// eol-comments aren't removed (yet)
+
+namespace used_alias = ::my_namespace;
+void f() { used_alias::C c; }
diff --git a/test/clang-tidy/misc-unused-parameters.c b/test/clang-tidy/misc-unused-parameters.c
new file mode 100644 (file)
index 0000000..635aace
--- /dev/null
@@ -0,0 +1,17 @@
+// RUN: %check_clang_tidy %s misc-unused-parameters %t -- -- -xc
+
+// Basic removal
+// =============
+void a(int i) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: parameter 'i' is unused [misc-unused-parameters]
+// CHECK-FIXES: {{^}}void a(int  /*i*/) {}{{$}}
+
+static void b(); // In C, forward declarations can leave out parameters.
+static void b(int i) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: parameter 'i' is unused [misc-unused-parameters]
+// CHECK-FIXES: {{^}}static void b() {}{{$}}
+
+// Unchanged cases
+// ===============
+void h(i, c, d) int i; char *c, *d; {} // Don't mess with K&R style
+
diff --git a/test/clang-tidy/misc-unused-parameters.cpp b/test/clang-tidy/misc-unused-parameters.cpp
new file mode 100644 (file)
index 0000000..fc20b2b
--- /dev/null
@@ -0,0 +1,158 @@
+// RUN: echo "static void staticFunctionHeader(int i) {}" > %T/header.h
+// RUN: echo "static void staticFunctionHeader(int  /*i*/) {}" > %T/header-fixed.h
+// RUN: %check_clang_tidy %s misc-unused-parameters %t -- -header-filter='.*' -- -std=c++11 -fno-delayed-template-parsing
+// RUN: diff %T/header.h %T/header-fixed.h
+
+#include "header.h"
+// CHECK-MESSAGES: header.h:1:38: warning
+
+// Basic removal
+// =============
+void a(int i) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: parameter 'i' is unused [misc-unused-parameters]
+// CHECK-FIXES: {{^}}void a(int  /*i*/) {}{{$}}
+
+void b(int i = 1) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: parameter 'i' is unused [misc-unused-parameters]
+// CHECK-FIXES: {{^}}void b(int  /*i*/) {}{{$}}
+
+void c(int *i) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: parameter 'i' is unused [misc-unused-parameters]
+// CHECK-FIXES: {{^}}void c(int * /*i*/) {}{{$}}
+
+// Unchanged cases
+// ===============
+void g(int i);             // Don't remove stuff in declarations
+void h(int i) { (void)i; } // Don't remove used parameters
+
+bool useLambda(int (*fn)(int));
+static bool static_var = useLambda([] (int a) { return a; });
+
+// Remove parameters of local functions
+// ====================================
+static void staticFunctionA(int i);
+// CHECK-FIXES: {{^}}static void staticFunctionA();
+static void staticFunctionA(int i) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:33: warning
+// CHECK-FIXES: {{^}}static void staticFunctionA()
+
+static void staticFunctionB(int i, int j) { (void)i; }
+// CHECK-MESSAGES: :[[@LINE-1]]:40: warning
+// CHECK-FIXES: {{^}}static void staticFunctionB(int i)
+
+static void staticFunctionC(int i, int j) { (void)j; }
+// CHECK-MESSAGES: :[[@LINE-1]]:33: warning
+// CHECK-FIXES: {{^}}static void staticFunctionC(int j)
+
+static void staticFunctionD(int i, int j, int k) { (void)i; (void)k; }
+// CHECK-MESSAGES: :[[@LINE-1]]:40: warning
+// CHECK-FIXES: {{^}}static void staticFunctionD(int i, int k)
+
+static void staticFunctionE(int i = 4) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:33: warning
+// CHECK-FIXES: {{^}}static void staticFunctionE()
+
+
+static void someCallSites() {
+  staticFunctionA(1);
+// CHECK-FIXES: staticFunctionA();
+  staticFunctionB(1, 2);
+// CHECK-FIXES: staticFunctionB(1);
+  staticFunctionC(1, 2);
+// CHECK-FIXES: staticFunctionC(2);
+  staticFunctionD(1, 2, 3);
+// CHECK-FIXES: staticFunctionD(1, 3);
+  staticFunctionE();
+}
+
+/*
+ * FIXME: This fails because the removals overlap and ClangTidy doesn't apply
+ *        them.
+ * static void bothVarsUnused(int a, int b) {}
+ */
+
+// Regression test for long variable names and expressions
+// =======================================================
+static int variableWithLongName1(int LongName1, int LongName2) {
+// CHECK-MESSAGES: :[[@LINE-1]]:53: warning: parameter 'LongName2' is unused
+// CHECK-FIXES: {{^}}static int variableWithLongName1(int LongName1) {
+  return LongName1;
+}
+static int variableWithLongName2(int LongName1, int LongName2) {
+// CHECK-MESSAGES: :[[@LINE-1]]:38: warning: parameter 'LongName1' is unused
+// CHECK-FIXES: {{^}}static int variableWithLongName2(int LongName2) {
+  return LongName2;
+}
+static void someLongNameCallSites() {
+  int LongName1 = 7, LongName2 = 17;
+  variableWithLongName1(LongName1, LongName2);
+// CHECK-FIXES: variableWithLongName1(LongName1);
+  variableWithLongName2(LongName1, LongName2);
+// CHECK-FIXES: variableWithLongName2(LongName2);
+}
+
+class SomeClass {
+  static void f(int i) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:21: warning
+// CHECK-FIXES: static void f(int  /*i*/) {}
+};
+
+namespace {
+class C {
+public:
+  void f(int i);
+// CHECK-FIXES: void f();
+  void g(int i) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:14: warning
+// CHECK-FIXES: void g() {}
+  void h(int i) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:14: warning
+// CHECK-FIXES: void h(int  /*i*/) {}
+};
+
+void C::f(int i) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:15: warning
+// CHECK-FIXES: void C::f() {}
+
+template <typename T>
+void useFunction(T t);
+
+void someMoreCallSites() {
+  C c;
+  c.f(1);
+// CHECK-FIXES: c.f();
+  c.g(1);
+// CHECK-FIXES: c.g();
+
+  useFunction(&C::h);
+}
+
+class Base {
+  virtual void f(int i);
+};
+
+class Derived : public Base {
+  void f(int i) override {}
+// CHECK-MESSAGES: :[[@LINE-1]]:14: warning
+// CHECK-FIXES: void f(int  /*i*/) override {}
+};
+
+} // end namespace
+
+template <typename T> void someFunctionTemplate(T b, T e) { (void)b; (void)e; }
+
+template <typename T> void someFunctionTemplateOneUnusedParam(T b, T e) { (void)e; }
+// CHECK-MESSAGES: :[[@LINE-1]]:65: warning
+// CHECK-FIXES: {{^}}template <typename T> void someFunctionTemplateOneUnusedParam(T  /*b*/, T e) { (void)e; }
+
+template <typename T> void someFunctionTemplateAllUnusedParams(T b, T e) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:66: warning
+// CHECK-MESSAGES: :[[@LINE-2]]:71: warning
+// CHECK-FIXES: {{^}}template <typename T> void someFunctionTemplateAllUnusedParams(T  /*b*/, T  /*e*/) {}
+
+static void dontGetConfusedByParametersInFunctionTypes() { void (*F)(int i); }
+
+template <typename T> class Function {};
+static Function<void(int, int i)> dontGetConfusedByFunctionReturnTypes() {
+  return Function<void(int, int)>();
+}
diff --git a/test/clang-tidy/misc-unused-raii.cpp b/test/clang-tidy/misc-unused-raii.cpp
new file mode 100644 (file)
index 0000000..f22746c
--- /dev/null
@@ -0,0 +1,68 @@
+// RUN: %check_clang_tidy %s misc-unused-raii %t
+
+struct Foo {
+  Foo();
+  Foo(int);
+  Foo(int, int);
+  ~Foo();
+};
+
+struct Bar {
+  Bar();
+};
+
+struct FooBar {
+  FooBar();
+  Foo f;
+};
+
+template <typename T>
+void qux() {
+  T(42);
+}
+
+template <typename T>
+struct TFoo {
+  TFoo(T);
+  ~TFoo();
+};
+
+Foo f();
+
+struct Ctor {
+  Ctor(int);
+  Ctor() {
+    Ctor(0); // TODO: warn here.
+  }
+};
+
+void test() {
+  Foo(42);
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: object destroyed immediately after creation; did you mean to name the object?
+// CHECK-FIXES: Foo give_me_a_name(42);
+  Foo(23, 42);
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: object destroyed immediately after creation; did you mean to name the object?
+// CHECK-FIXES: Foo give_me_a_name(23, 42);
+  Foo();
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: object destroyed immediately after creation; did you mean to name the object?
+// CHECK-FIXES: Foo give_me_a_name;
+  TFoo<int>(23);
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: object destroyed immediately after creation; did you mean to name the object?
+// CHECK-FIXES: TFoo<int> give_me_a_name(23);
+
+  FooBar();
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: object destroyed immediately after creation; did you mean to name the object?
+// CHECK-FIXES: FooBar give_me_a_name;
+
+  Bar();
+  f();
+  qux<Foo>();
+
+#define M Foo();
+  M
+
+  {
+    Foo();
+  }
+  Foo();
+}
diff --git a/test/clang-tidy/misc-unused-using-decls-errors.cpp b/test/clang-tidy/misc-unused-using-decls-errors.cpp
new file mode 100644 (file)
index 0000000..5eb380f
--- /dev/null
@@ -0,0 +1,12 @@
+// RUN: %check_clang_tidy %s misc-unused-using-decls %t
+
+namespace n {
+class C;
+}
+
+using n::C;
+
+void f() {
+  for (C *p : unknown()) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:15: error: use of undeclared identifier 'unknown' [clang-diagnostic-error]
+}
diff --git a/test/clang-tidy/misc-unused-using-decls.cpp b/test/clang-tidy/misc-unused-using-decls.cpp
new file mode 100644 (file)
index 0000000..65ef0da
--- /dev/null
@@ -0,0 +1,203 @@
+// RUN: %check_clang_tidy %s misc-unused-using-decls %t -- -- -fno-delayed-template-parsing -isystem %S/Inputs/
+
+
+// ----- Definitions -----
+template <typename T> class vector {};
+namespace n {
+class A;
+class B;
+class C;
+class D;
+class D { public: static int i; };
+template <typename T> class E {};
+template <typename T> class F {};
+class G { public: static void func() {} };
+class H { public: static int i; };
+class I {
+ public:
+  static int ii;
+};
+template <typename T> class J {};
+class G;
+class H;
+
+template <typename T> class K {};
+template <template <typename> class S>
+class L {};
+
+template <typename T> class M {};
+class N {};
+
+template <int T> class P {};
+const int Constant = 0;
+
+class Base {
+ public:
+  void f();
+};
+
+D UsedInstance;
+D UnusedInstance;
+
+int UsedFunc() { return 1; }
+int UnusedFunc() { return 1; }
+template <typename T> int UsedTemplateFunc() { return 1; }
+template <typename T> int UnusedTemplateFunc() { return 1; }
+template <typename T> int UsedInTemplateFunc() { return 1; }
+void OverloadFunc(int);
+void OverloadFunc(double);
+int FuncUsedByUsingDeclInMacro() { return 1; }
+
+class ostream {
+public:
+  ostream &operator<<(ostream &(*PF)(ostream &));
+};
+extern ostream cout;
+ostream &endl(ostream &os);
+
+enum Color1 { Green };
+
+enum Color2 { Red };
+
+enum Color3 { Yellow };
+
+enum Color4 { Blue };
+
+}  // namespace n
+
+#include "unused-using-decls.h"
+namespace ns {
+template <typename T>
+class AA {
+  T t;
+};
+template <typename T>
+T ff() { T t; return t; }
+} // namespace ns
+
+// ----- Using declarations -----
+// eol-comments aren't removed (yet)
+using n::A; // A
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: using decl 'A' is unused
+// CHECK-FIXES: {{^}}// A
+using n::B;
+using n::C;
+using n::D;
+using n::E; // E
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: using decl 'E' is unused
+// CHECK-FIXES: {{^}}// E
+using n::F;
+using n::G;
+using n::H;
+using n::I;
+int I::ii = 1;
+class Derived : public n::Base {
+ public:
+  using Base::f;
+};
+using n::UsedInstance;
+using n::UsedFunc;
+using n::UsedTemplateFunc;
+using n::UnusedInstance; // UnusedInstance
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: using decl 'UnusedInstance' is unused
+// CHECK-FIXES: {{^}}// UnusedInstance
+using n::UnusedFunc; // UnusedFunc
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: using decl 'UnusedFunc' is unused
+// CHECK-FIXES: {{^}}// UnusedFunc
+using n::cout;
+using n::endl;
+
+using n::UsedInTemplateFunc;
+using n::J;
+template <typename T> void Callee() {
+  J<T> j;
+  UsedInTemplateFunc<T>();
+}
+
+using n::OverloadFunc; // OverloadFunc
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: using decl 'OverloadFunc' is unused
+// CHECK-FIXES: {{^}}// OverloadFunc
+
+#define DEFINE_INT(name)        \
+  namespace INT {               \
+  static const int _##name = 1; \
+  }                             \
+  using INT::_##name
+DEFINE_INT(test);
+#undef DEFIND_INT
+
+#define USING_FUNC \
+  using n::FuncUsedByUsingDeclInMacro;
+USING_FUNC
+#undef USING_FUNC
+
+namespace N1 {
+// n::G is used in namespace N2.
+// Currently, the check doesn't support multiple scopes. All the relevant
+// using-decls will be marked as used once we see an usage even the usage is in
+// other scope.
+using n::G;
+}
+
+namespace N2 {
+using n::G;
+void f(G g);
+}
+
+void IgnoreFunctionScope() {
+// Using-decls defined in function scope will be ignored.
+using n::H;
+}
+
+using n::Color1;
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: using decl 'Color1' is unused
+using n::Green;
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: using decl 'Green' is unused
+using n::Color2;
+using n::Color3;
+using n::Blue;
+
+using ns::AA;
+using ns::ff;
+
+using n::K;
+
+using n::N;
+
+// FIXME: Currently non-type template arguments are not supported.
+using n::Constant;
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: using decl 'Constant' is unused
+
+// ----- Usages -----
+void f(B b);
+void g() {
+  vector<C> data;
+  D::i = 1;
+  F<int> f;
+  void (*func)() = &G::func;
+  int *i = &H::i;
+  UsedInstance.i;
+  UsedFunc();
+  UsedTemplateFunc<int>();
+  cout << endl;
+  Color2 color2;
+  int t1 = Color3::Yellow;
+  int t2 = Blue;
+
+  MyClass a;
+  int t3 = 0;
+  a.func1<AA>(&t3);
+  a.func2<int, ff>(t3);
+
+  n::L<K> l;
+}
+
+template<class T>
+void h(n::M<T>* t) {}
+// n::N is used the explicit template instantiation.
+template void h(n::M<N>* t);
+
+// Test on Non-type template arguments.
+template <int T>
+void i(n::P<T>* t) {}
+template void i(n::P<Constant>* t);
diff --git a/test/clang-tidy/misc-use-after-move.cpp b/test/clang-tidy/misc-use-after-move.cpp
new file mode 100644 (file)
index 0000000..970364b
--- /dev/null
@@ -0,0 +1,1159 @@
+// RUN: %check_clang_tidy %s misc-use-after-move %t -- -- -std=c++11 -fno-delayed-template-parsing
+
+typedef decltype(nullptr) nullptr_t;
+
+namespace std {
+typedef unsigned size_t;
+
+template <typename T>
+struct unique_ptr {
+  unique_ptr();
+  T *get() const;
+  explicit operator bool() const;
+  void reset(T *ptr);
+  T &operator*() const;
+  T *operator->() const;
+  T& operator[](size_t i) const;
+};
+
+template <typename T>
+struct shared_ptr {
+  shared_ptr();
+  T *get() const;
+  explicit operator bool() const;
+  void reset(T *ptr);
+  T &operator*() const;
+  T *operator->() const;
+};
+
+template <typename T>
+struct weak_ptr {
+  weak_ptr();
+  bool expired() const;
+};
+
+#define DECLARE_STANDARD_CONTAINER(name) \
+  template <typename T>                  \
+  struct name {                          \
+    name();                              \
+    void clear();                        \
+    bool empty();                        \
+  }
+
+#define DECLARE_STANDARD_CONTAINER_WITH_ASSIGN(name) \
+  template <typename T>                              \
+  struct name {                                      \
+    name();                                          \
+    void clear();                                    \
+    bool empty();                                    \
+    void assign(size_t, const T &);                  \
+  }
+
+DECLARE_STANDARD_CONTAINER_WITH_ASSIGN(basic_string);
+DECLARE_STANDARD_CONTAINER_WITH_ASSIGN(vector);
+DECLARE_STANDARD_CONTAINER_WITH_ASSIGN(deque);
+DECLARE_STANDARD_CONTAINER_WITH_ASSIGN(forward_list);
+DECLARE_STANDARD_CONTAINER_WITH_ASSIGN(list);
+DECLARE_STANDARD_CONTAINER(set);
+DECLARE_STANDARD_CONTAINER(map);
+DECLARE_STANDARD_CONTAINER(multiset);
+DECLARE_STANDARD_CONTAINER(multimap);
+DECLARE_STANDARD_CONTAINER(unordered_set);
+DECLARE_STANDARD_CONTAINER(unordered_map);
+DECLARE_STANDARD_CONTAINER(unordered_multiset);
+DECLARE_STANDARD_CONTAINER(unordered_multimap);
+
+typedef basic_string<char> string;
+
+template <typename>
+struct remove_reference;
+
+template <typename _Tp>
+struct remove_reference {
+  typedef _Tp type;
+};
+
+template <typename _Tp>
+struct remove_reference<_Tp &> {
+  typedef _Tp type;
+};
+
+template <typename _Tp>
+struct remove_reference<_Tp &&> {
+  typedef _Tp type;
+};
+
+template <typename _Tp>
+constexpr typename std::remove_reference<_Tp>::type &&move(_Tp &&__t) noexcept {
+  return static_cast<typename remove_reference<_Tp>::type &&>(__t);
+}
+
+} // namespace std
+
+class A {
+public:
+  A();
+  A(const A &);
+  A(A &&);
+
+  A &operator=(const A &);
+  A &operator=(A &&);
+
+  void foo() const;
+  int getInt() const;
+
+  operator bool() const;
+
+  int i;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// General tests.
+
+// Simple case.
+void simple() {
+  A a;
+  a.foo();
+  A other_a = std::move(a);
+  a.foo();
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: 'a' used after it was moved
+  // CHECK-MESSAGES: [[@LINE-3]]:15: note: move occurred here
+}
+
+// A warning should only be emitted for one use-after-move.
+void onlyFlagOneUseAfterMove() {
+  A a;
+  a.foo();
+  A other_a = std::move(a);
+  a.foo();
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: 'a' used after it was moved
+  // CHECK-MESSAGES: [[@LINE-3]]:15: note: move occurred here
+  a.foo();
+}
+
+void moveAfterMove() {
+  // Move-after-move also counts as a use.
+  {
+    A a;
+    std::move(a);
+    std::move(a);
+    // CHECK-MESSAGES: [[@LINE-1]]:15: warning: 'a' used after it was moved
+    // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here
+  }
+  // This is also true if the move itself turns into the use on the second loop
+  // iteration.
+  {
+    A a;
+    for (int i = 0; i < 10; ++i) {
+      std::move(a);
+      // CHECK-MESSAGES: [[@LINE-1]]:17: warning: 'a' used after it was moved
+      // CHECK-MESSAGES: [[@LINE-2]]:7: note: move occurred here
+      // CHECK-MESSAGES: [[@LINE-3]]:17: note: the use happens in a later loop
+    }
+  }
+}
+
+// Checks also works on function parameters that have a use-after move.
+void parameters(A a) {
+  std::move(a);
+  a.foo();
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: 'a' used after it was moved
+  // CHECK-MESSAGES: [[@LINE-3]]:3: note: move occurred here
+}
+
+void standardSmartPtr() {
+  // std::unique_ptr<>, std::shared_ptr<> and std::weak_ptr<> are guaranteed to
+  // be null after a std::move. So the check only flags accesses that would
+  // dereference the pointer.
+  {
+    std::unique_ptr<A> ptr;
+    std::move(ptr);
+    ptr.get();
+    static_cast<bool>(ptr);
+    *ptr;
+    // CHECK-MESSAGES: [[@LINE-1]]:6: warning: 'ptr' used after it was moved
+    // CHECK-MESSAGES: [[@LINE-5]]:5: note: move occurred here
+  }
+  {
+    std::unique_ptr<A> ptr;
+    std::move(ptr);
+    ptr->foo();
+    // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'ptr' used after it was moved
+    // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here
+  }
+  {
+    std::unique_ptr<A> ptr;
+    std::move(ptr);
+    ptr[0];
+    // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'ptr' used after it was moved
+    // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here
+  }
+  {
+    std::shared_ptr<A> ptr;
+    std::move(ptr);
+    ptr.get();
+    static_cast<bool>(ptr);
+    *ptr;
+    // CHECK-MESSAGES: [[@LINE-1]]:6: warning: 'ptr' used after it was moved
+    // CHECK-MESSAGES: [[@LINE-5]]:5: note: move occurred here
+  }
+  {
+    std::shared_ptr<A> ptr;
+    std::move(ptr);
+    ptr->foo();
+    // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'ptr' used after it was moved
+    // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here
+  }
+  {
+    // std::weak_ptr<> cannot be dereferenced directly, so we only check that
+    // member functions may be called on it after a move.
+    std::weak_ptr<A> ptr;
+    std::move(ptr);
+    ptr.expired();
+  }
+  // Make sure we recognize std::unique_ptr<> or std::shared_ptr<> if they're
+  // wrapped in a typedef.
+  {
+    typedef std::unique_ptr<A> PtrToA;
+    PtrToA ptr;
+    std::move(ptr);
+    ptr.get();
+  }
+  {
+    typedef std::shared_ptr<A> PtrToA;
+    PtrToA ptr;
+    std::move(ptr);
+    ptr.get();
+  }
+  // And we don't get confused if the template argument is a little more
+  // involved.
+  {
+    struct B {
+      typedef A AnotherNameForA;
+    };
+    std::unique_ptr<B::AnotherNameForA> ptr;
+    std::move(ptr);
+    ptr.get();
+  }
+  // We don't give any special treatment to types that are called "unique_ptr"
+  // or "shared_ptr" but are not in the "::std" namespace.
+  {
+    struct unique_ptr {
+      void get();
+    } ptr;
+    std::move(ptr);
+    ptr.get();
+    // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'ptr' used after it was moved
+    // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here
+  }
+}
+
+// The check also works in member functions.
+class Container {
+  void useAfterMoveInMemberFunction() {
+    A a;
+    std::move(a);
+    a.foo();
+    // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'a' used after it was moved
+    // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here
+  }
+};
+
+// We see the std::move() if it's inside a declaration.
+void moveInDeclaration() {
+  A a;
+  A another_a(std::move(a));
+  a.foo();
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: 'a' used after it was moved
+  // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here
+}
+
+// We see the std::move if it's inside an initializer list. Initializer lists
+// are a special case because they cause ASTContext::getParents() to return
+// multiple parents for certain nodes in their subtree. This is because
+// RecursiveASTVisitor visits both the syntactic and semantic forms of
+// InitListExpr, and the parent-child relationships are different between the
+// two forms.
+void moveInInitList() {
+  struct S {
+    A a;
+  };
+  A a;
+  S s{std::move(a)};
+  a.foo();
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: 'a' used after it was moved
+  // CHECK-MESSAGES: [[@LINE-3]]:7: note: move occurred here
+}
+
+void lambdas() {
+  // Use-after-moves inside a lambda should be detected.
+  {
+    A a;
+    auto lambda = [a] {
+      std::move(a);
+      a.foo();
+      // CHECK-MESSAGES: [[@LINE-1]]:7: warning: 'a' used after it was moved
+      // CHECK-MESSAGES: [[@LINE-3]]:7: note: move occurred here
+    };
+  }
+  // This is just as true if the variable was declared inside the lambda.
+  {
+    auto lambda = [] {
+      A a;
+      std::move(a);
+      a.foo();
+      // CHECK-MESSAGES: [[@LINE-1]]:7: warning: 'a' used after it was moved
+      // CHECK-MESSAGES: [[@LINE-3]]:7: note: move occurred here
+    };
+  }
+  // But don't warn if the move happened inside the lambda but the use happened
+  // outside -- because
+  // - the 'a' inside the lambda is a copy, and
+  // - we don't know when the lambda will get called anyway
+  {
+    A a;
+    auto lambda = [a] {
+      std::move(a);
+    };
+    a.foo();
+  }
+  // Warn if the use consists of a capture that happens after a move.
+  {
+    A a;
+    std::move(a);
+    auto lambda = [a]() { a.foo(); };
+    // CHECK-MESSAGES: [[@LINE-1]]:20: warning: 'a' used after it was moved
+    // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here
+  }
+  // ...even if the capture was implicit.
+  {
+    A a;
+    std::move(a);
+    auto lambda = [=]() { a.foo(); };
+    // CHECK-MESSAGES: [[@LINE-1]]:27: warning: 'a' used after it was moved
+    // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here
+  }
+  // Same tests but for capture by reference.
+  {
+    A a;
+    std::move(a);
+    auto lambda = [&a]() { a.foo(); };
+    // CHECK-MESSAGES: [[@LINE-1]]:21: warning: 'a' used after it was moved
+    // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here
+  }
+  {
+    A a;
+    std::move(a);
+    auto lambda = [&]() { a.foo(); };
+    // CHECK-MESSAGES: [[@LINE-1]]:27: warning: 'a' used after it was moved
+    // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here
+  }
+  // But don't warn if the move happened after the capture.
+  {
+    A a;
+    auto lambda = [a]() { a.foo(); };
+    std::move(a);
+  }
+  // ...and again, same thing with an implicit move.
+  {
+    A a;
+    auto lambda = [=]() { a.foo(); };
+    std::move(a);
+  }
+  // Same tests but for capture by reference.
+  {
+    A a;
+    auto lambda = [&a]() { a.foo(); };
+    std::move(a);
+  }
+  {
+    A a;
+    auto lambda = [&]() { a.foo(); };
+    std::move(a);
+  }
+}
+
+// Use-after-moves are detected in uninstantiated templates if the moved type
+// is not a dependent type.
+template <class T>
+void movedTypeIsNotDependentType() {
+  T t;
+  A a;
+  std::move(a);
+  a.foo();
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: 'a' used after it was moved
+  // CHECK-MESSAGES: [[@LINE-3]]:3: note: move occurred here
+}
+
+// And if the moved type is a dependent type, the use-after-move is detected if
+// the template is instantiated.
+template <class T>
+void movedTypeIsDependentType() {
+  T t;
+  std::move(t);
+  t.foo();
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: 't' used after it was moved
+  // CHECK-MESSAGES: [[@LINE-3]]:3: note: move occurred here
+}
+template void movedTypeIsDependentType<A>();
+
+// We handle the case correctly where the move consists of an implicit call
+// to a conversion operator.
+void implicitConversionOperator() {
+  struct Convertible {
+    operator A() && { return A(); }
+  };
+  void takeA(A a);
+
+  Convertible convertible;
+  takeA(std::move(convertible));
+  convertible;
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: 'convertible' used after it was moved
+  // CHECK-MESSAGES: [[@LINE-3]]:9: note: move occurred here
+}
+
+// Using decltype on an expression is not a use.
+void decltypeIsNotUse() {
+  A a;
+  std::move(a);
+  decltype(a) other_a;
+}
+
+// Ignore moves or uses that occur as part of template arguments.
+template <int>
+class ClassTemplate {
+public:
+  void foo(A a);
+};
+template <int>
+void functionTemplate(A a);
+void templateArgIsNotUse() {
+  {
+    // A pattern like this occurs in the EXPECT_EQ and ASSERT_EQ macros in
+    // Google Test.
+    A a;
+    ClassTemplate<sizeof(A(std::move(a)))>().foo(std::move(a));
+  }
+  {
+    A a;
+    functionTemplate<sizeof(A(std::move(a)))>(std::move(a));
+  }
+}
+
+// Ignore moves of global variables.
+A global_a;
+void ignoreGlobalVariables() {
+  std::move(global_a);
+  global_a.foo();
+}
+
+// Ignore moves of member variables.
+class IgnoreMemberVariables {
+  A a;
+  static A static_a;
+
+  void f() {
+    std::move(a);
+    a.foo();
+
+    std::move(static_a);
+    static_a.foo();
+  }
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// Tests involving control flow.
+
+void useAndMoveInLoop() {
+  // Warn about use-after-moves if they happen in a later loop iteration than
+  // the std::move().
+  {
+    A a;
+    for (int i = 0; i < 10; ++i) {
+      a.foo();
+      // CHECK-MESSAGES: [[@LINE-1]]:7: warning: 'a' used after it was moved
+      // CHECK-MESSAGES: [[@LINE+2]]:7: note: move occurred here
+      // CHECK-MESSAGES: [[@LINE-3]]:7: note: the use happens in a later loop
+      std::move(a);
+    }
+  }
+  // However, this case shouldn't be flagged -- the scope of the declaration of
+  // 'a' is important.
+  {
+    for (int i = 0; i < 10; ++i) {
+      A a;
+      a.foo();
+      std::move(a);
+    }
+  }
+  // Same as above, except that we have an unrelated variable being declared in
+  // the same declaration as 'a'. This case is interesting because it tests that
+  // the synthetic DeclStmts generated by the CFG are sequenced correctly
+  // relative to the other statements.
+  {
+    for (int i = 0; i < 10; ++i) {
+      A a, other;
+      a.foo();
+      std::move(a);
+    }
+  }
+  // Don't warn if we return after the move.
+  {
+    A a;
+    for (int i = 0; i < 10; ++i) {
+      a.foo();
+      if (a.getInt() > 0) {
+        std::move(a);
+        return;
+      }
+    }
+  }
+}
+
+void differentBranches(int i) {
+  // Don't warn if the use is in a different branch from the move.
+  {
+    A a;
+    if (i > 0) {
+      std::move(a);
+    } else {
+      a.foo();
+    }
+  }
+  // Same thing, but with a ternary operator.
+  {
+    A a;
+    i > 0 ? (void)std::move(a) : a.foo();
+  }
+  // A variation on the theme above.
+  {
+    A a;
+    a.getInt() > 0 ? a.getInt() : A(std::move(a)).getInt();
+  }
+  // Same thing, but with a switch statement.
+  {
+    A a;
+    switch (i) {
+    case 1:
+      std::move(a);
+      break;
+    case 2:
+      a.foo();
+      break;
+    }
+  }
+  // However, if there's a fallthrough, we do warn.
+  {
+    A a;
+    switch (i) {
+    case 1:
+      std::move(a);
+    case 2:
+      a.foo();
+      // CHECK-MESSAGES: [[@LINE-1]]:7: warning: 'a' used after it was moved
+      // CHECK-MESSAGES: [[@LINE-4]]:7: note: move occurred here
+      break;
+    }
+  }
+}
+
+// False positive: A use-after-move is flagged even though the "if (b)" and
+// "if (!b)" are mutually exclusive.
+void mutuallyExclusiveBranchesFalsePositive(bool b) {
+  A a;
+  if (b) {
+    std::move(a);
+  }
+  if (!b) {
+    a.foo();
+    // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'a' used after it was moved
+    // CHECK-MESSAGES: [[@LINE-5]]:5: note: move occurred here
+  }
+}
+
+// Destructors marked [[noreturn]] are handled correctly in the control flow
+// analysis. (These are used in some styles of assertion macros.)
+class FailureLogger {
+public:
+  FailureLogger();
+  [[noreturn]] ~FailureLogger();
+  void log(const char *);
+};
+#define ASSERT(x) \
+  while (x)       \
+  FailureLogger().log(#x)
+bool operationOnA(A);
+void noreturnDestructor() {
+  A a;
+  // The while loop in the ASSERT() would ordinarily have the potential to cause
+  // a use-after-move because the second iteration of the loop would be using a
+  // variable that had been moved from in the first iteration. Check that the
+  // CFG knows that the second iteration of the loop is never reached because
+  // the FailureLogger destructor is marked [[noreturn]].
+  ASSERT(operationOnA(std::move(a)));
+}
+#undef ASSERT
+
+////////////////////////////////////////////////////////////////////////////////
+// Tests for reinitializations
+
+template <class T>
+void swap(T &a, T &b) {
+  T tmp = std::move(a);
+  a = std::move(b);
+  b = std::move(tmp);
+}
+void assignments(int i) {
+  // Don't report a use-after-move if the variable was assigned to in the
+  // meantime.
+  {
+    A a;
+    std::move(a);
+    a = A();
+    a.foo();
+  }
+  // The assignment should also be recognized if move, assignment and use don't
+  // all happen in the same block (but the assignment is still guaranteed to
+  // prevent a use-after-move).
+  {
+    A a;
+    if (i == 1) {
+      std::move(a);
+      a = A();
+    }
+    if (i == 2) {
+      a.foo();
+    }
+  }
+  {
+    A a;
+    if (i == 1) {
+      std::move(a);
+    }
+    if (i == 2) {
+      a = A();
+      a.foo();
+    }
+  }
+  // The built-in assignment operator should also be recognized as a
+  // reinitialization. (std::move() may be called on built-in types in template
+  // code.)
+  {
+    int a1 = 1, a2 = 2;
+    swap(a1, a2);
+  }
+  // A std::move() after the assignment makes the variable invalid again.
+  {
+    A a;
+    std::move(a);
+    a = A();
+    std::move(a);
+    a.foo();
+    // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'a' used after it was moved
+    // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here
+  }
+  // Report a use-after-move if we can't be sure that the variable was assigned
+  // to.
+  {
+    A a;
+    std::move(a);
+    if (i < 10) {
+      a = A();
+    }
+    if (i > 5) {
+      a.foo();
+      // CHECK-MESSAGES: [[@LINE-1]]:7: warning: 'a' used after it was moved
+      // CHECK-MESSAGES: [[@LINE-7]]:5: note: move occurred here
+    }
+  }
+}
+
+// Passing the object to a function through a non-const pointer or reference
+// counts as a re-initialization.
+void passByNonConstPointer(A *);
+void passByNonConstReference(A &);
+void passByNonConstPointerIsReinit() {
+  {
+    A a;
+    std::move(a);
+    passByNonConstPointer(&a);
+    a.foo();
+  }
+  {
+    A a;
+    std::move(a);
+    passByNonConstReference(a);
+    a.foo();
+  }
+}
+
+// Passing the object through a const pointer or reference counts as a use --
+// since the called function cannot reinitialize the object.
+void passByConstPointer(const A *);
+void passByConstReference(const A &);
+void passByConstPointerIsUse() {
+  {
+    // Declaring 'a' as const so that no ImplicitCastExpr is inserted into the
+    // AST -- we wouldn't want the check to rely solely on that to detect a
+    // const pointer argument.
+    const A a;
+    std::move(a);
+    passByConstPointer(&a);
+    // CHECK-MESSAGES: [[@LINE-1]]:25: warning: 'a' used after it was moved
+    // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here
+  }
+  const A a;
+  std::move(a);
+  passByConstReference(a);
+  // CHECK-MESSAGES: [[@LINE-1]]:24: warning: 'a' used after it was moved
+  // CHECK-MESSAGES: [[@LINE-3]]:3: note: move occurred here
+}
+
+// Clearing a standard container using clear() is treated as a
+// re-initialization.
+void standardContainerClearIsReinit() {
+  {
+    std::string container;
+    std::move(container);
+    container.clear();
+    container.empty();
+  }
+  {
+    std::vector<int> container;
+    std::move(container);
+    container.clear();
+    container.empty();
+  }
+  {
+    std::deque<int> container;
+    std::move(container);
+    container.clear();
+    container.empty();
+  }
+  {
+    std::forward_list<int> container;
+    std::move(container);
+    container.clear();
+    container.empty();
+  }
+  {
+    std::list<int> container;
+    std::move(container);
+    container.clear();
+    container.empty();
+  }
+  {
+    std::set<int> container;
+    std::move(container);
+    container.clear();
+    container.empty();
+  }
+  {
+    std::map<int> container;
+    std::move(container);
+    container.clear();
+    container.empty();
+  }
+  {
+    std::multiset<int> container;
+    std::move(container);
+    container.clear();
+    container.empty();
+  }
+  {
+    std::multimap<int> container;
+    std::move(container);
+    container.clear();
+    container.empty();
+  }
+  {
+    std::unordered_set<int> container;
+    std::move(container);
+    container.clear();
+    container.empty();
+  }
+  {
+    std::unordered_map<int> container;
+    std::move(container);
+    container.clear();
+    container.empty();
+  }
+  {
+    std::unordered_multiset<int> container;
+    std::move(container);
+    container.clear();
+    container.empty();
+  }
+  {
+    std::unordered_multimap<int> container;
+    std::move(container);
+    container.clear();
+    container.empty();
+  }
+  // This should also work for typedefs of standard containers.
+  {
+    typedef std::vector<int> IntVector;
+    IntVector container;
+    std::move(container);
+    container.clear();
+    container.empty();
+  }
+  // But it shouldn't work for non-standard containers.
+  {
+    // This might be called "vector", but it's not in namespace "std".
+    struct vector {
+      void clear() {}
+    } container;
+    std::move(container);
+    container.clear();
+    // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'container' used after it was
+    // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here
+  }
+  // An intervening clear() on a different container does not reinitialize.
+  {
+    std::vector<int> container1, container2;
+    std::move(container1);
+    container2.clear();
+    container1.empty();
+    // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'container1' used after it was
+    // CHECK-MESSAGES: [[@LINE-4]]:5: note: move occurred here
+  }
+}
+
+// Clearing a standard container using assign() is treated as a
+// re-initialization.
+void standardContainerAssignIsReinit() {
+  {
+    std::string container;
+    std::move(container);
+    container.assign(0, ' ');
+    container.empty();
+  }
+  {
+    std::vector<int> container;
+    std::move(container);
+    container.assign(0, 0);
+    container.empty();
+  }
+  {
+    std::deque<int> container;
+    std::move(container);
+    container.assign(0, 0);
+    container.empty();
+  }
+  {
+    std::forward_list<int> container;
+    std::move(container);
+    container.assign(0, 0);
+    container.empty();
+  }
+  {
+    std::list<int> container;
+    std::move(container);
+    container.clear();
+    container.empty();
+  }
+  // But it doesn't work for non-standard containers.
+  {
+    // This might be called "vector", but it's not in namespace "std".
+    struct vector {
+      void assign(std::size_t, int) {}
+    } container;
+    std::move(container);
+    container.assign(0, 0);
+    // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'container' used after it was
+    // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here
+  }
+  // An intervening assign() on a different container does not reinitialize.
+  {
+    std::vector<int> container1, container2;
+    std::move(container1);
+    container2.assign(0, 0);
+    container1.empty();
+    // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'container1' used after it was
+    // CHECK-MESSAGES: [[@LINE-4]]:5: note: move occurred here
+  }
+}
+
+// Resetting the standard smart pointer types using reset() is treated as a
+// re-initialization. (We don't test std::weak_ptr<> because it can't be
+// dereferenced directly.)
+void standardSmartPointerResetIsReinit() {
+  {
+    std::unique_ptr<A> ptr;
+    std::move(ptr);
+    ptr.reset(new A);
+    *ptr;
+  }
+  {
+    std::shared_ptr<A> ptr;
+    std::move(ptr);
+    ptr.reset(new A);
+    *ptr;
+  }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Tests related to order of evaluation within expressions
+
+// Relative sequencing of move and use.
+void passByRvalueReference(int i, A &&a);
+void passByValue(int i, A a);
+void passByValue(A a, int i);
+A g(A, A &&);
+int intFromA(A &&);
+int intFromInt(int);
+void sequencingOfMoveAndUse() {
+  // This case is fine because the move only happens inside
+  // passByRvalueReference(). At this point, a.getInt() is guaranteed to have
+  // been evaluated.
+  {
+    A a;
+    passByRvalueReference(a.getInt(), std::move(a));
+  }
+  // However, if we pass by value, the move happens when the move constructor is
+  // called to create a temporary, and this happens before the call to
+  // passByValue(). Because the order in which arguments are evaluated isn't
+  // defined, the move may happen before the call to a.getInt().
+  //
+  // Check that we warn about a potential use-after move for both orderings of
+  // a.getInt() and std::move(a), independent of the order in which the
+  // arguments happen to get evaluated by the compiler.
+  {
+    A a;
+    passByValue(a.getInt(), std::move(a));
+    // CHECK-MESSAGES: [[@LINE-1]]:17: warning: 'a' used after it was moved
+    // CHECK-MESSAGES: [[@LINE-2]]:29: note: move occurred here
+    // CHECK-MESSAGES: [[@LINE-3]]:17: note: the use and move are unsequenced
+  }
+  {
+    A a;
+    passByValue(std::move(a), a.getInt());
+    // CHECK-MESSAGES: [[@LINE-1]]:31: warning: 'a' used after it was moved
+    // CHECK-MESSAGES: [[@LINE-2]]:17: note: move occurred here
+    // CHECK-MESSAGES: [[@LINE-3]]:31: note: the use and move are unsequenced
+  }
+  // An even more convoluted example.
+  {
+    A a;
+    g(g(a, std::move(a)), g(a, std::move(a)));
+    // CHECK-MESSAGES: [[@LINE-1]]:9: warning: 'a' used after it was moved
+    // CHECK-MESSAGES: [[@LINE-2]]:27: note: move occurred here
+    // CHECK-MESSAGES: [[@LINE-3]]:9: note: the use and move are unsequenced
+    // CHECK-MESSAGES: [[@LINE-4]]:29: warning: 'a' used after it was moved
+    // CHECK-MESSAGES: [[@LINE-5]]:7: note: move occurred here
+    // CHECK-MESSAGES: [[@LINE-6]]:29: note: the use and move are unsequenced
+  }
+  // This case is fine because the actual move only happens inside the call to
+  // operator=(). a.getInt(), by necessity, is evaluated before that call.
+  {
+    A a;
+    A vec[1];
+    vec[a.getInt()] = std::move(a);
+  }
+  // However, in the following case, the move happens before the assignment, and
+  // so the order of evaluation is not guaranteed.
+  {
+    A a;
+    int v[3];
+    v[a.getInt()] = intFromA(std::move(a));
+    // CHECK-MESSAGES: [[@LINE-1]]:7: warning: 'a' used after it was moved
+    // CHECK-MESSAGES: [[@LINE-2]]:21: note: move occurred here
+    // CHECK-MESSAGES: [[@LINE-3]]:7: note: the use and move are unsequenced
+  }
+  {
+    A a;
+    int v[3];
+    v[intFromA(std::move(a))] = intFromInt(a.i);
+    // CHECK-MESSAGES: [[@LINE-1]]:44: warning: 'a' used after it was moved
+    // CHECK-MESSAGES: [[@LINE-2]]:7: note: move occurred here
+    // CHECK-MESSAGES: [[@LINE-3]]:44: note: the use and move are unsequenced
+  }
+}
+
+// Relative sequencing of move and reinitialization. If the two are unsequenced,
+// we conservatively assume that the move happens after the reinitialization,
+// i.e. the that object does not get reinitialized after the move.
+A MutateA(A a);
+void passByValue(A a1, A a2);
+void sequencingOfMoveAndReinit() {
+  // Move and reinitialization as function arguments (which are indeterminately
+  // sequenced). Again, check that we warn for both orderings.
+  {
+    A a;
+    passByValue(std::move(a), (a = A()));
+    a.foo();
+    // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'a' used after it was moved
+    // CHECK-MESSAGES: [[@LINE-3]]:17: note: move occurred here
+  }
+  {
+    A a;
+    passByValue((a = A()), std::move(a));
+    a.foo();
+    // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'a' used after it was moved
+    // CHECK-MESSAGES: [[@LINE-3]]:28: note: move occurred here
+  }
+  // Common usage pattern: Move the object to a function that mutates it in some
+  // way, then reassign the result to the object. This pattern is fine.
+  {
+    A a;
+    a = MutateA(std::move(a));
+    a.foo();
+  }
+}
+
+// Relative sequencing of reinitialization and use. If the two are unsequenced,
+// we conservatively assume that the reinitialization happens after the use,
+// i.e. that the object is not reinitialized at the point in time when it is
+// used.
+void sequencingOfReinitAndUse() {
+  // Reinitialization and use in function arguments. Again, check both possible
+  // orderings.
+  {
+    A a;
+    std::move(a);
+    passByValue(a.getInt(), (a = A()));
+    // CHECK-MESSAGES: [[@LINE-1]]:17: warning: 'a' used after it was moved
+    // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here
+  }
+  {
+    A a;
+    std::move(a);
+    passByValue((a = A()), a.getInt());
+    // CHECK-MESSAGES: [[@LINE-1]]:28: warning: 'a' used after it was moved
+    // CHECK-MESSAGES: [[@LINE-3]]:5: note: move occurred here
+  }
+}
+
+// The comma operator sequences its operands.
+void commaOperatorSequences() {
+  {
+    A a;
+    A(std::move(a))
+    , (a = A());
+    a.foo();
+  }
+  {
+    A a;
+    (a = A()), A(std::move(a));
+    a.foo();
+    // CHECK-MESSAGES: [[@LINE-1]]:5: warning: 'a' used after it was moved
+    // CHECK-MESSAGES: [[@LINE-3]]:16: note: move occurred here
+  }
+}
+
+// An initializer list sequences its initialization clauses.
+void initializerListSequences() {
+  {
+    struct S1 {
+      int i;
+      A a;
+    };
+    A a;
+    S1 s1{a.getInt(), std::move(a)};
+  }
+  {
+    struct S2 {
+      A a;
+      int i;
+    };
+    A a;
+    S2 s2{std::move(a), a.getInt()};
+    // CHECK-MESSAGES: [[@LINE-1]]:25: warning: 'a' used after it was moved
+    // CHECK-MESSAGES: [[@LINE-2]]:11: note: move occurred here
+  }
+}
+
+// A declaration statement containing multiple declarations sequences the
+// initializer expressions.
+void declarationSequences() {
+  {
+    A a;
+    A a1 = a, a2 = std::move(a);
+  }
+  {
+    A a;
+    A a1 = std::move(a), a2 = a;
+    // CHECK-MESSAGES: [[@LINE-1]]:31: warning: 'a' used after it was moved
+    // CHECK-MESSAGES: [[@LINE-2]]:12: note: move occurred here
+  }
+}
+
+// The logical operators && and || sequence their operands.
+void logicalOperatorsSequence() {
+  {
+    A a;
+    if (a.getInt() > 0 && A(std::move(a)).getInt() > 0) {
+      A().foo();
+    }
+  }
+  // A variation: Negate the result of the && (which pushes the && further down
+  // into the AST).
+  {
+    A a;
+    if (!(a.getInt() > 0 && A(std::move(a)).getInt() > 0)) {
+      A().foo();
+    }
+  }
+  {
+    A a;
+    if (A(std::move(a)).getInt() > 0 && a.getInt() > 0) {
+      // CHECK-MESSAGES: [[@LINE-1]]:41: warning: 'a' used after it was moved
+      // CHECK-MESSAGES: [[@LINE-2]]:9: note: move occurred here
+      A().foo();
+    }
+  }
+  {
+    A a;
+    if (a.getInt() > 0 || A(std::move(a)).getInt() > 0) {
+      A().foo();
+    }
+  }
+  {
+    A a;
+    if (A(std::move(a)).getInt() > 0 || a.getInt() > 0) {
+      // CHECK-MESSAGES: [[@LINE-1]]:41: warning: 'a' used after it was moved
+      // CHECK-MESSAGES: [[@LINE-2]]:9: note: move occurred here
+      A().foo();
+    }
+  }
+}
+
+// A range-based for sequences the loop variable declaration before the body.
+void forRangeSequences() {
+  A v[2] = {A(), A()};
+  for (A &a : v) {
+    std::move(a);
+  }
+}
+
+// If a variable is declared in an if statement, the declaration of the variable
+// (which is treated like a reinitialization by the check) is sequenced before
+// the evaluation of the condition (which constitutes a use).
+void ifStmtSequencesDeclAndCondition() {
+  for (int i = 0; i < 10; ++i) {
+    if (A a = A()) {
+      std::move(a);
+    }
+  }
+}
+
+namespace PR33020 {
+class D {
+  ~D();
+};
+struct A {
+  D d;
+};
+class B {
+  A a;
+};
+template <typename T>
+class C : T, B {
+  void m_fn1() {
+    int a;
+    std::move(a);
+    C c;
+  }
+};
+}
diff --git a/test/clang-tidy/misc-virtual-near-miss.cpp b/test/clang-tidy/misc-virtual-near-miss.cpp
new file mode 100644 (file)
index 0000000..a6bf222
--- /dev/null
@@ -0,0 +1,133 @@
+// RUN: %check_clang_tidy %s misc-virtual-near-miss %t
+
+class NoDefinedClass1;
+class NoDefinedClass2;
+
+struct Base {
+  virtual void func();
+  virtual void gunk();
+  virtual ~Base();
+  virtual Base &operator=(const Base &);
+  virtual NoDefinedClass1 *f();
+};
+
+struct Derived : Base {
+  // Should not warn "do you want to override 'gunk'?", because gunk is already
+  // overriden by this class.
+  virtual void funk();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'Derived::funk' has a similar name and the same signature as virtual method 'Base::func'; did you mean to override it? [misc-virtual-near-miss]
+  // CHECK-FIXES: virtual void func();
+
+  void func2();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'Derived::func2' has {{.*}} 'Base::func'
+  // CHECK-FIXES: void func();
+
+  void func22(); // Should not warn.
+
+  void gunk(); // Should not warn: gunk is override.
+
+  void fun();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'Derived::fun' has {{.*}} 'Base::func'
+  // CHECK-FIXES: void func();
+
+  Derived &operator==(const Base &); // Should not warn: operators are ignored.
+
+  virtual NoDefinedClass2 *f1(); // Should not crash: non-defined class return type is ignored.
+};
+
+template <typename T>
+struct TBase {
+  virtual void tfunc(T t);
+};
+
+template <typename T>
+struct TDerived : TBase<T> {
+  virtual void tfunk(T t);
+  // Should not apply fix for template.
+  // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: method 'TDerived<double>::tfunk' has {{.*}} 'TBase<double>::tfunc'
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: method 'TDerived<int>::tfunk' has {{.*}} 'TBase<int>::tfunc'
+  // CHECK-FIXES: virtual void tfunk(T t);
+};
+
+TDerived<int> T1;
+TDerived<double> T2;
+
+// Should not fix macro definition
+#define MACRO1 void funcM()
+// CHECK-FIXES: #define MACRO1 void funcM()
+#define MACRO2(m) void m()
+// CHECK-FIXES: #define MACRO2(m) void m()
+
+struct DerivedMacro : Base {
+  MACRO1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'DerivedMacro::funcM' has {{.*}} 'Base::func'
+  // CHECK-FIXES: MACRO1;
+
+  MACRO2(func3);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'DerivedMacro::func3' has {{.*}} 'Base::func'
+  // CHECK-FIXES: MACRO2(func);
+};
+
+typedef Derived derived_type;
+
+class Father {
+public:
+  Father();
+  virtual void func();
+  virtual Father *create(int i);
+  virtual Base &&generate();
+  virtual Base *canonical(Derived D);
+};
+
+class Mother {
+public:
+  Mother();
+  static void method();
+  virtual int method(int argc, const char **argv);
+  virtual int method(int argc) const;
+  virtual int decay(const char *str);
+};
+
+class Child : private Father, private Mother {
+public:
+  Child();
+
+  virtual void func2();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'Child::func2' has {{.*}} 'Father::func'
+  // CHECK-FIXES: virtual void func();
+
+  int methoe(int x, char **strs); // Should not warn: parameter types don't match.
+
+  int methoe(int x);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'Child::methoe' has {{.*}} 'Mother::method'
+  // CHECK-FIXES: int method(int x);
+
+  void methof(int x, const char **strs); // Should not warn: return types don't match.
+
+  int methoh(int x, const char **strs);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'Child::methoh' has {{.*}} 'Mother::method'
+  // CHECK-FIXES: int method(int x, const char **strs);
+
+  virtual Child *creat(int i);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'Child::creat' has {{.*}} 'Father::create'
+  // CHECK-FIXES: virtual Child *create(int i);
+
+  virtual Derived &&generat();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'Child::generat' has {{.*}} 'Father::generate'
+  // CHECK-FIXES: virtual Derived &&generate();
+
+  int decaz(const char str[]);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'Child::decaz' has {{.*}} 'Mother::decay'
+  // CHECK-FIXES: int decay(const char str[]);
+
+  operator bool();
+
+  derived_type *canonica(derived_type D);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'Child::canonica' has {{.*}} 'Father::canonical'
+  // CHECK-FIXES: derived_type *canonical(derived_type D);
+
+private:
+  void funk();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: method 'Child::funk' has {{.*}} 'Father::func'
+  // CHECK-FIXES: void func();
+};
diff --git a/test/clang-tidy/modernize-avoid-bind.cpp b/test/clang-tidy/modernize-avoid-bind.cpp
new file mode 100644 (file)
index 0000000..1c78b9e
--- /dev/null
@@ -0,0 +1,79 @@
+// RUN: %check_clang_tidy %s modernize-avoid-bind %t -- -- -std=c++14
+
+namespace std {
+inline namespace impl {
+template <class Fp, class... Arguments>
+class bind_rt {};
+
+template <class Fp, class... Arguments>
+bind_rt<Fp, Arguments...> bind(Fp &&, Arguments &&...);
+}
+}
+
+int add(int x, int y) { return x + y; }
+
+void f() {
+  auto clj = std::bind(add, 2, 2);
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: prefer a lambda to std::bind [modernize-avoid-bind]
+  // CHECK-FIXES: auto clj = [] { return add(2, 2); };
+}
+
+void g() {
+  int x = 2;
+  int y = 2;
+  auto clj = std::bind(add, x, y);
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: prefer a lambda to std::bind
+  // CHECK-FIXES: auto clj = [=] { return add(x, y); };
+}
+
+struct placeholder {};
+placeholder _1;
+placeholder _2;
+
+void h() {
+  int x = 2;
+  auto clj = std::bind(add, x, _1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: prefer a lambda to std::bind
+  // CHECK-FIXES: auto clj = [=](auto && arg1) { return add(x, arg1); };
+}
+
+struct A;
+struct B;
+bool ABTest(const A &, const B &);
+
+void i() {
+  auto BATest = std::bind(ABTest, _2, _1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: prefer a lambda to std::bind
+  // CHECK-FIXES: auto BATest = [](auto && arg1, auto && arg2) { return ABTest(arg2, arg1); };
+}
+
+void j() {
+  auto clj = std::bind(add, 2, 2, 2);
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: prefer a lambda to std::bind
+  // No fix is applied for argument mismatches.
+  // CHECK-FIXES: auto clj = std::bind(add, 2, 2, 2);
+}
+
+void k() {
+  auto clj = std::bind(add, _1, _1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: prefer a lambda to std::bind
+  // No fix is applied for reused placeholders.
+  // CHECK-FIXES: auto clj = std::bind(add, _1, _1);
+}
+
+void m() {
+  auto clj = std::bind(add, 1, add(2, 5));
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: prefer a lambda to std::bind
+  // No fix is applied for nested calls.
+  // CHECK-FIXES: auto clj = std::bind(add, 1, add(2, 5));
+}
+
+namespace C {
+  int add(int x, int y){ return x + y; }
+}
+
+void n() {
+  auto clj = std::bind(C::add, 1, 1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: prefer a lambda to std::bind
+  // CHECK-FIXES: auto clj = [] { return C::add(1, 1); };
+}
diff --git a/test/clang-tidy/modernize-deprecated-headers-cxx03.cpp b/test/clang-tidy/modernize-deprecated-headers-cxx03.cpp
new file mode 100644 (file)
index 0000000..c604ba4
--- /dev/null
@@ -0,0 +1,148 @@
+// RUN: %check_clang_tidy %s modernize-deprecated-headers %t -- -extra-arg-before=-isystem%S/Inputs/modernize-deprecated-headers -- -std=c++03 -v
+
+#include <assert.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'assert.h'; consider using 'cassert' instead [modernize-deprecated-headers]
+// CHECK-FIXES: {{^}}#include <cassert>{{$}}
+#include <complex.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'complex.h'; consider using 'complex' instead
+// CHECK-FIXES: {{^}}#include <complex>{{$}}
+#include <ctype.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'ctype.h'; consider using 'cctype' instead
+// CHECK-FIXES: {{^}}#include <cctype>{{$}}
+#include <errno.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'errno.h'; consider using 'cerrno' instead
+// CHECK-FIXES: {{^}}#include <cerrno>{{$}}
+#include <float.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'float.h'; consider using 'cfloat' instead
+// CHECK-FIXES: {{^}}#include <cfloat>{{$}}
+#include <limits.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'limits.h'; consider using 'climits' instead
+// CHECK-FIXES: {{^}}#include <climits>{{$}}
+#include <locale.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'locale.h'; consider using 'clocale' instead
+// CHECK-FIXES: {{^}}#include <clocale>{{$}}
+#include <math.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'math.h'; consider using 'cmath' instead
+// CHECK-FIXES: {{^}}#include <cmath>{{$}}
+#include <setjmp.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'setjmp.h'; consider using 'csetjmp' instead
+// CHECK-FIXES: {{^}}#include <csetjmp>{{$}}
+#include <signal.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'signal.h'; consider using 'csignal' instead
+// CHECK-FIXES: {{^}}#include <csignal>{{$}}
+#include <stdarg.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'stdarg.h'; consider using 'cstdarg' instead
+// CHECK-FIXES: {{^}}#include <cstdarg>{{$}}
+#include <stddef.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'stddef.h'; consider using 'cstddef' instead
+// CHECK-FIXES: {{^}}#include <cstddef>{{$}}
+#include <stdio.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'stdio.h'; consider using 'cstdio' instead
+// CHECK-FIXES: {{^}}#include <cstdio>{{$}}
+#include <stdlib.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'stdlib.h'; consider using 'cstdlib' instead
+// CHECK-FIXES: {{^}}#include <cstdlib>{{$}}
+#include <string.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'string.h'; consider using 'cstring' instead
+// CHECK-FIXES: {{^}}#include <cstring>{{$}}
+#include <time.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'time.h'; consider using 'ctime' instead
+// CHECK-FIXES: {{^}}#include <ctime>{{$}}
+#include <wchar.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'wchar.h'; consider using 'cwchar' instead
+// CHECK-FIXES: {{^}}#include <cwchar>{{$}}
+#include <wctype.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'wctype.h'; consider using 'cwctype' instead
+// CHECK-FIXES: {{^}}#include <cwctype>{{$}}
+
+// Headers that have no effect in C++; remove them
+#include <stdalign.h> // <stdalign.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: including 'stdalign.h' has no effect in C++; consider removing it
+// CHECK-FIXES: {{^}}// <stdalign.h>{{$}}
+#include <stdbool.h> // <stdbool.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: including 'stdbool.h' has no effect in C++; consider removing it
+// CHECK-FIXES: {{^}}// <stdbool.h>{{$}}
+#include <iso646.h> // <iso646.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: including 'iso646.h' has no effect in C++; consider removing it
+// CHECK-FIXES: {{^}}// <iso646.h>{{$}}
+
+// Headers deprecated since C++11: expect no diagnostics.
+#include <fenv.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <tgmath.h>
+#include <uchar.h>
+
+
+#include "assert.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'assert.h'; consider using 'cassert' instead
+// CHECK-FIXES: {{^}}#include <cassert>{{$}}
+#include "complex.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'complex.h'; consider using 'complex' instead
+// CHECK-FIXES: {{^}}#include <complex>{{$}}
+#include "ctype.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'ctype.h'; consider using 'cctype' instead
+// CHECK-FIXES: {{^}}#include <cctype>{{$}}
+#include "errno.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'errno.h'; consider using 'cerrno' instead
+// CHECK-FIXES: {{^}}#include <cerrno>{{$}}
+#include "float.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'float.h'; consider using 'cfloat' instead
+// CHECK-FIXES: {{^}}#include <cfloat>{{$}}
+#include "limits.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'limits.h'; consider using 'climits' instead
+// CHECK-FIXES: {{^}}#include <climits>{{$}}
+#include "locale.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'locale.h'; consider using 'clocale' instead
+// CHECK-FIXES: {{^}}#include <clocale>{{$}}
+#include "math.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'math.h'; consider using 'cmath' instead
+// CHECK-FIXES: {{^}}#include <cmath>{{$}}
+#include "setjmp.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'setjmp.h'; consider using 'csetjmp' instead
+// CHECK-FIXES: {{^}}#include <csetjmp>{{$}}
+#include "signal.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'signal.h'; consider using 'csignal' instead
+// CHECK-FIXES: {{^}}#include <csignal>{{$}}
+#include "stdarg.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'stdarg.h'; consider using 'cstdarg' instead
+// CHECK-FIXES: {{^}}#include <cstdarg>{{$}}
+#include "stddef.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'stddef.h'; consider using 'cstddef' instead
+// CHECK-FIXES: {{^}}#include <cstddef>{{$}}
+#include "stdio.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'stdio.h'; consider using 'cstdio' instead
+// CHECK-FIXES: {{^}}#include <cstdio>{{$}}
+#include "stdlib.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'stdlib.h'; consider using 'cstdlib' instead
+// CHECK-FIXES: {{^}}#include <cstdlib>{{$}}
+#include "string.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'string.h'; consider using 'cstring' instead
+// CHECK-FIXES: {{^}}#include <cstring>{{$}}
+#include "time.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'time.h'; consider using 'ctime' instead
+// CHECK-FIXES: {{^}}#include <ctime>{{$}}
+#include "wchar.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'wchar.h'; consider using 'cwchar' instead
+// CHECK-FIXES: {{^}}#include <cwchar>{{$}}
+#include "wctype.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'wctype.h'; consider using 'cwctype' instead
+// CHECK-FIXES: {{^}}#include <cwctype>
+
+// Headers that have no effect in C++; remove them
+#include "stdalign.h" // "stdalign.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: including 'stdalign.h' has no effect in C++; consider removing it
+// CHECK-FIXES: {{^}}// "stdalign.h"{{$}}
+#include "stdbool.h" // "stdbool.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: including 'stdbool.h' has no effect in C++; consider removing it
+// CHECK-FIXES: {{^}}// "stdbool.h"{{$}}
+#include "iso646.h" // "iso646.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: including 'iso646.h' has no effect in C++; consider removing it
+// CHECK-FIXES: {{^}}// "iso646.h"{{$}}
+
+// Headers deprecated since C++11; expect no diagnostics
+#include "fenv.h"
+#include "inttypes.h"
+#include "stdint.h"
+#include "tgmath.h"
+#include "uchar.h"
diff --git a/test/clang-tidy/modernize-deprecated-headers-cxx11.cpp b/test/clang-tidy/modernize-deprecated-headers-cxx11.cpp
new file mode 100644 (file)
index 0000000..366f045
--- /dev/null
@@ -0,0 +1,163 @@
+// RUN: %check_clang_tidy %s modernize-deprecated-headers %t -- -extra-arg-before=-isystem%S/Inputs/modernize-deprecated-headers -- -std=c++11 -v
+
+#include <assert.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'assert.h'; consider using 'cassert' instead [modernize-deprecated-headers]
+// CHECK-FIXES: {{^}}#include <cassert>{{$}}
+#include <complex.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'complex.h'; consider using 'complex' instead
+// CHECK-FIXES: {{^}}#include <complex>{{$}}
+#include <ctype.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'ctype.h'; consider using 'cctype' instead
+// CHECK-FIXES: {{^}}#include <cctype>{{$}}
+#include <errno.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'errno.h'; consider using 'cerrno' instead
+// CHECK-FIXES: {{^}}#include <cerrno>{{$}}
+#include <fenv.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'fenv.h'; consider using 'cfenv' instead
+// CHECK-FIXES: {{^}}#include <cfenv>{{$}}
+#include <float.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'float.h'; consider using 'cfloat' instead
+// CHECK-FIXES: {{^}}#include <cfloat>{{$}}
+#include <inttypes.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'inttypes.h'; consider using 'cinttypes' instead
+// CHECK-FIXES: {{^}}#include <cinttypes>{{$}}
+#include <limits.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'limits.h'; consider using 'climits' instead
+// CHECK-FIXES: {{^}}#include <climits>{{$}}
+#include <locale.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'locale.h'; consider using 'clocale' instead
+// CHECK-FIXES: {{^}}#include <clocale>{{$}}
+#include <math.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'math.h'; consider using 'cmath' instead
+// CHECK-FIXES: {{^}}#include <cmath>{{$}}
+#include <setjmp.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'setjmp.h'; consider using 'csetjmp' instead
+// CHECK-FIXES: {{^}}#include <csetjmp>{{$}}
+#include <signal.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'signal.h'; consider using 'csignal' instead
+// CHECK-FIXES: {{^}}#include <csignal>{{$}}
+#include <stdarg.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'stdarg.h'; consider using 'cstdarg' instead
+// CHECK-FIXES: {{^}}#include <cstdarg>{{$}}
+#include <stddef.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'stddef.h'; consider using 'cstddef' instead
+// CHECK-FIXES: {{^}}#include <cstddef>{{$}}
+#include <stdint.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'stdint.h'; consider using 'cstdint' instead
+// CHECK-FIXES: {{^}}#include <cstdint>{{$}}
+#include <stdio.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'stdio.h'; consider using 'cstdio' instead
+// CHECK-FIXES: {{^}}#include <cstdio>{{$}}
+#include <stdlib.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'stdlib.h'; consider using 'cstdlib' instead
+// CHECK-FIXES: {{^}}#include <cstdlib>{{$}}
+#include <string.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'string.h'; consider using 'cstring' instead
+// CHECK-FIXES: {{^}}#include <cstring>{{$}}
+#include <tgmath.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'tgmath.h'; consider using 'ctgmath' instead
+// CHECK-FIXES: {{^}}#include <ctgmath>{{$}}
+#include <time.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'time.h'; consider using 'ctime' instead
+// CHECK-FIXES: {{^}}#include <ctime>{{$}}
+#include <uchar.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'uchar.h'; consider using 'cuchar' instead
+// CHECK-FIXES: {{^}}#include <cuchar>{{$}}
+#include <wchar.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'wchar.h'; consider using 'cwchar' instead
+// CHECK-FIXES: {{^}}#include <cwchar>{{$}}
+#include <wctype.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'wctype.h'; consider using 'cwctype' instead
+// CHECK-FIXES: {{^}}#include <cwctype>
+
+// Headers that have no effect in C++; remove them
+#include <stdalign.h> // <stdalign.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: including 'stdalign.h' has no effect in C++; consider removing it
+// CHECK-FIXES: {{^}}// <stdalign.h>{{$}}
+#include <stdbool.h> // <stdbool.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: including 'stdbool.h' has no effect in C++; consider removing it
+// CHECK-FIXES: {{^}}// <stdbool.h>{{$}}
+#include <iso646.h> // <iso646.h>
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: including 'iso646.h' has no effect in C++; consider removing it
+// CHECK-FIXES: {{^}}// <iso646.h>{{$}}
+
+#include "assert.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'assert.h'; consider using 'cassert' instead
+// CHECK-FIXES: {{^}}#include <cassert>{{$}}
+#include "complex.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'complex.h'; consider using 'complex' instead
+// CHECK-FIXES: {{^}}#include <complex>{{$}}
+#include "ctype.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'ctype.h'; consider using 'cctype' instead
+// CHECK-FIXES: {{^}}#include <cctype>{{$}}
+#include "errno.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'errno.h'; consider using 'cerrno' instead
+// CHECK-FIXES: {{^}}#include <cerrno>{{$}}
+#include "fenv.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'fenv.h'; consider using 'cfenv' instead
+// CHECK-FIXES: {{^}}#include <cfenv>{{$}}
+#include "float.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'float.h'; consider using 'cfloat' instead
+// CHECK-FIXES: {{^}}#include <cfloat>{{$}}
+#include "inttypes.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'inttypes.h'; consider using 'cinttypes' instead
+// CHECK-FIXES: {{^}}#include <cinttypes>{{$}}
+#include "limits.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'limits.h'; consider using 'climits' instead
+// CHECK-FIXES: {{^}}#include <climits>{{$}}
+#include "locale.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'locale.h'; consider using 'clocale' instead
+// CHECK-FIXES: {{^}}#include <clocale>{{$}}
+#include "math.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'math.h'; consider using 'cmath' instead
+// CHECK-FIXES: {{^}}#include <cmath>{{$}}
+#include "setjmp.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'setjmp.h'; consider using 'csetjmp' instead
+// CHECK-FIXES: {{^}}#include <csetjmp>{{$}}
+#include "signal.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'signal.h'; consider using 'csignal' instead
+// CHECK-FIXES: {{^}}#include <csignal>{{$}}
+#include "stdarg.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'stdarg.h'; consider using 'cstdarg' instead
+// CHECK-FIXES: {{^}}#include <cstdarg>{{$}}
+#include "stddef.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'stddef.h'; consider using 'cstddef' instead
+// CHECK-FIXES: {{^}}#include <cstddef>{{$}}
+#include "stdint.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'stdint.h'; consider using 'cstdint' instead
+// CHECK-FIXES: {{^}}#include <cstdint>{{$}}
+#include "stdio.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'stdio.h'; consider using 'cstdio' instead
+// CHECK-FIXES: {{^}}#include <cstdio>{{$}}
+#include "stdlib.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'stdlib.h'; consider using 'cstdlib' instead
+// CHECK-FIXES: {{^}}#include <cstdlib>{{$}}
+#include "string.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'string.h'; consider using 'cstring' instead
+// CHECK-FIXES: {{^}}#include <cstring>{{$}}
+#include "tgmath.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'tgmath.h'; consider using 'ctgmath' instead
+// CHECK-FIXES: {{^}}#include <ctgmath>{{$}}
+#include "time.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'time.h'; consider using 'ctime' instead
+// CHECK-FIXES: {{^}}#include <ctime>{{$}}
+#include "uchar.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'uchar.h'; consider using 'cuchar' instead
+// CHECK-FIXES: {{^}}#include <cuchar>{{$}}
+#include "wchar.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'wchar.h'; consider using 'cwchar' instead
+// CHECK-FIXES: {{^}}#include <cwchar>{{$}}
+#include "wctype.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: inclusion of deprecated C++ header 'wctype.h'; consider using 'cwctype' instead
+// CHECK-FIXES: {{^}}#include <cwctype>
+
+// Headers that have no effect in C++; remove them
+#include "stdalign.h" // "stdalign.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: including 'stdalign.h' has no effect in C++; consider removing it
+// CHECK-FIXES: {{^}}// "stdalign.h"{{$}}
+#include "stdbool.h" // "stdbool.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: including 'stdbool.h' has no effect in C++; consider removing it
+// CHECK-FIXES: {{^}}// "stdbool.h"{{$}}
+#include "iso646.h" // "iso646.h"
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: including 'iso646.h' has no effect in C++; consider removing it
+// CHECK-FIXES: {{^}}// "iso646.h"{{$}}
diff --git a/test/clang-tidy/modernize-loop-convert-assert-failure.cpp b/test/clang-tidy/modernize-loop-convert-assert-failure.cpp
new file mode 100644 (file)
index 0000000..1e28901
--- /dev/null
@@ -0,0 +1,18 @@
+// RUN: clang-tidy %s -checks=-*,modernize-loop-convert --
+
+// Note: this test expects no assert failure happened in clang-tidy.
+
+class LinguisticItem {
+  LinguisticItem *x0;
+  class x1 {
+    bool operator!= ( const x1 &;
+    operator* ( ;
+    LinguisticItem * &operator-> ( ;
+    operator++ (
+  } begin() const;
+  x1 end() const {
+    LinguisticStream x2;
+    for (x1 x3 = x2.begin x3 != x2.end; ++x3)
+      x3->x0
+  }
+};
diff --git a/test/clang-tidy/modernize-loop-convert-basic.cpp b/test/clang-tidy/modernize-loop-convert-basic.cpp
new file mode 100644 (file)
index 0000000..def7c4b
--- /dev/null
@@ -0,0 +1,793 @@
+// RUN: %check_clang_tidy %s modernize-loop-convert %t -- -- -std=c++11 -I %S/Inputs/modernize-loop-convert
+
+#include "structures.h"
+
+namespace Array {
+
+const int N = 6;
+const int NMinusOne = N - 1;
+int Arr[N] = {1, 2, 3, 4, 5, 6};
+const int ConstArr[N] = {1, 2, 3, 4, 5, 6};
+int (*PArr)[N] = &Arr;
+
+void f() {
+  int Sum = 0;
+
+  for (int I = 0; I < N; ++I) {
+    Sum += Arr[I];
+    int K;
+  }
+  // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead [modernize-loop-convert]
+  // CHECK-FIXES: for (int I : Arr)
+  // CHECK-FIXES-NEXT: Sum += I;
+  // CHECK-FIXES-NEXT: int K;
+
+  for (int I = 0; I < N; ++I) {
+    printf("Fibonacci number is %d\n", Arr[I]);
+    Sum += Arr[I] + 2;
+  }
+  // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int I : Arr)
+  // CHECK-FIXES-NEXT: printf("Fibonacci number is %d\n", I);
+  // CHECK-FIXES-NEXT: Sum += I + 2;
+
+  for (int I = 0; I < N; ++I) {
+    int X = Arr[I];
+    int Y = Arr[I] + 2;
+  }
+  // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int I : Arr)
+  // CHECK-FIXES-NEXT: int X = I;
+  // CHECK-FIXES-NEXT: int Y = I + 2;
+
+  for (int I = 0; I < N; ++I) {
+    int X = N;
+    X = Arr[I];
+  }
+  // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int I : Arr)
+  // CHECK-FIXES-NEXT: int X = N;
+  // CHECK-FIXES-NEXT: X = I;
+
+  for (int I = 0; I < N; ++I) {
+    Arr[I] += 1;
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int & I : Arr)
+  // CHECK-FIXES-NEXT: I += 1;
+
+  for (int I = 0; I < N; ++I) {
+    int X = Arr[I] + 2;
+    Arr[I]++;
+  }
+  // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int & I : Arr)
+  // CHECK-FIXES-NEXT: int X = I + 2;
+  // CHECK-FIXES-NEXT: I++;
+
+  for (int I = 0; I < N; ++I) {
+    Arr[I] = 4 + Arr[I];
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int & I : Arr)
+  // CHECK-FIXES-NEXT: I = 4 + I;
+
+  for (int I = 0; I < NMinusOne + 1; ++I) {
+    Sum += Arr[I];
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int I : Arr)
+  // CHECK-FIXES-NEXT: Sum += I;
+
+  for (int I = 0; I < N; ++I) {
+    printf("Fibonacci number %d has address %p\n", Arr[I], &Arr[I]);
+    Sum += Arr[I] + 2;
+  }
+  // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int & I : Arr)
+  // CHECK-FIXES-NEXT: printf("Fibonacci number %d has address %p\n", I, &I);
+  // CHECK-FIXES-NEXT: Sum += I + 2;
+
+  Val Teas[N];
+  for (int I = 0; I < N; ++I) {
+    Teas[I].g();
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (auto & Tea : Teas)
+  // CHECK-FIXES-NEXT: Tea.g();
+}
+
+const int *constArray() {
+  for (int I = 0; I < N; ++I) {
+    printf("2 * %d = %d\n", ConstArr[I], ConstArr[I] + ConstArr[I]);
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int I : ConstArr)
+  // CHECK-FIXES-NEXT: printf("2 * %d = %d\n", I, I + I);
+
+  const NonTriviallyCopyable NonCopy[N]{};
+  for (int I = 0; I < N; ++I) {
+    printf("2 * %d = %d\n", NonCopy[I].X, NonCopy[I].X + NonCopy[I].X);
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (const auto & I : NonCopy)
+  // CHECK-FIXES-NEXT: printf("2 * %d = %d\n", I.X, I.X + I.X);
+
+  const TriviallyCopyableButBig Big[N]{};
+  for (int I = 0; I < N; ++I) {
+    printf("2 * %d = %d\n", Big[I].X, Big[I].X + Big[I].X);
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (const auto & I : Big)
+  // CHECK-FIXES-NEXT: printf("2 * %d = %d\n", I.X, I.X + I.X);
+
+  bool Something = false;
+  for (int I = 0; I < N; ++I) {
+    if (Something)
+      return &ConstArr[I];
+  }
+  // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (const int & I : ConstArr)
+  // CHECK-FIXES-NEXT: if (Something)
+  // CHECK-FIXES-NEXT: return &I;
+}
+
+struct HasArr {
+  int Arr[N];
+  Val ValArr[N];
+  void implicitThis() {
+    for (int I = 0; I < N; ++I) {
+      printf("%d", Arr[I]);
+    }
+    // CHECK-MESSAGES: :[[@LINE-3]]:5: warning: use range-based for loop instead
+    // CHECK-FIXES: for (int I : Arr)
+    // CHECK-FIXES-NEXT: printf("%d", I);
+
+    for (int I = 0; I < N; ++I) {
+      printf("%d", ValArr[I].X);
+    }
+    // CHECK-MESSAGES: :[[@LINE-3]]:5: warning: use range-based for loop instead
+    // CHECK-FIXES: for (auto & I : ValArr)
+    // CHECK-FIXES-NEXT: printf("%d", I.X);
+  }
+
+  void explicitThis() {
+    for (int I = 0; I < N; ++I) {
+      printf("%d", this->Arr[I]);
+    }
+    // CHECK-MESSAGES: :[[@LINE-3]]:5: warning: use range-based for loop instead
+    // CHECK-FIXES: for (int I : this->Arr)
+    // CHECK-FIXES-NEXT: printf("%d", I);
+
+    for (int I = 0; I < N; ++I) {
+      printf("%d", this->ValArr[I].X);
+    }
+    // CHECK-MESSAGES: :[[@LINE-3]]:5: warning: use range-based for loop instead
+    // CHECK-FIXES: for (auto & I : this->ValArr)
+    // CHECK-FIXES-NEXT: printf("%d", I.X);
+  }
+};
+
+struct HasIndirectArr {
+  HasArr HA;
+  void implicitThis() {
+    for (int I = 0; I < N; ++I) {
+      printf("%d", HA.Arr[I]);
+    }
+    // CHECK-MESSAGES: :[[@LINE-3]]:5: warning: use range-based for loop instead
+    // CHECK-FIXES: for (int I : HA.Arr)
+    // CHECK-FIXES-NEXT: printf("%d", I);
+
+    for (int I = 0; I < N; ++I) {
+      printf("%d", HA.ValArr[I].X);
+    }
+    // CHECK-MESSAGES: :[[@LINE-3]]:5: warning: use range-based for loop instead
+    // CHECK-FIXES: for (auto & I : HA.ValArr)
+    // CHECK-FIXES-NEXT: printf("%d", I.X);
+  }
+
+  void explicitThis() {
+    for (int I = 0; I < N; ++I) {
+      printf("%d", this->HA.Arr[I]);
+    }
+    // CHECK-MESSAGES: :[[@LINE-3]]:5: warning: use range-based for loop instead
+    // CHECK-FIXES: for (int I : this->HA.Arr)
+    // CHECK-FIXES-NEXT: printf("%d", I);
+
+    for (int I = 0; I < N; ++I) {
+      printf("%d", this->HA.ValArr[I].X);
+    }
+    // CHECK-MESSAGES: :[[@LINE-3]]:5: warning: use range-based for loop instead
+    // CHECK-FIXES: for (auto & I : this->HA.ValArr)
+    // CHECK-FIXES-NEXT: printf("%d", I.X);
+  }
+};
+
+// Loops whose bounds are value-dependent should not be converted.
+template <int N>
+void dependentExprBound() {
+  for (int I = 0; I < N; ++I)
+    Arr[I] = 0;
+}
+template void dependentExprBound<20>();
+
+void memberFunctionPointer() {
+  Val V;
+  void (Val::*mfpArr[N])(void) = {&Val::g};
+  for (int I = 0; I < N; ++I)
+    (V.*mfpArr[I])();
+  // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (auto & I : mfpArr)
+  // CHECK-FIXES-NEXT: (V.*I)();
+
+  struct Foo {
+    int (Val::*f)();
+  } Foo[N];
+
+  for (int I = 0; I < N; ++I)
+    int R = (V.*(Foo[I].f))();
+  // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (auto & I : Foo)
+  // CHECK-FIXES-NEXT: int R = (V.*(I.f))();
+
+}
+
+} // namespace Array
+
+namespace Iterator {
+
+void f() {
+  /// begin()/end() - based for loops here:
+  T Tt;
+  for (T::iterator It = Tt.begin(), E = Tt.end(); It != E; ++It) {
+    printf("I found %d\n", *It);
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int & It : Tt)
+  // CHECK-FIXES-NEXT: printf("I found %d\n", It);
+
+  T *Pt;
+  for (T::iterator It = Pt->begin(), E = Pt->end(); It != E; ++It) {
+    printf("I found %d\n", *It);
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int & It : *Pt)
+  // CHECK-FIXES-NEXT: printf("I found %d\n", It);
+
+  S Ss;
+  for (S::iterator It = Ss.begin(), E = Ss.end(); It != E; ++It) {
+    printf("s has value %d\n", (*It).X);
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (auto & It : Ss)
+  // CHECK-FIXES-NEXT: printf("s has value %d\n", It.X);
+
+  S *Ps;
+  for (S::iterator It = Ps->begin(), E = Ps->end(); It != E; ++It) {
+    printf("s has value %d\n", (*It).X);
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (auto & P : *Ps)
+  // CHECK-FIXES-NEXT: printf("s has value %d\n", P.X);
+
+  for (S::const_iterator It = Ss.cbegin(), E = Ss.cend(); It != E; ++It) {
+    printf("s has value %d\n", (*It).X);
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (auto It : Ss)
+  // CHECK-FIXES-NEXT: printf("s has value %d\n", It.X);
+
+  for (S::iterator It = Ss.begin(), E = Ss.end(); It != E; ++It) {
+    printf("s has value %d\n", It->X);
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (auto & It : Ss)
+  // CHECK-FIXES-NEXT: printf("s has value %d\n", It.X);
+
+  for (S::iterator It = Ss.begin(), E = Ss.end(); It != E; ++It) {
+    It->X = 3;
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (auto & It : Ss)
+  // CHECK-FIXES-NEXT: It.X = 3;
+
+  for (S::iterator It = Ss.begin(), E = Ss.end(); It != E; ++It) {
+    (*It).X = 3;
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (auto & It : Ss)
+  // CHECK-FIXES-NEXT: It.X = 3;
+
+  for (S::iterator It = Ss.begin(), E = Ss.end(); It != E; ++It) {
+    It->nonConstFun(4, 5);
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (auto & It : Ss)
+  // CHECK-FIXES-NEXT: It.nonConstFun(4, 5);
+
+  U Uu;
+  for (U::iterator It = Uu.begin(), E = Uu.end(); It != E; ++It) {
+    printf("s has value %d\n", It->X);
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (auto & It : Uu)
+  // CHECK-FIXES-NEXT: printf("s has value %d\n", It.X);
+
+  for (U::iterator It = Uu.begin(), E = Uu.end(); It != E; ++It) {
+    printf("s has value %d\n", (*It).X);
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (auto & It : Uu)
+  // CHECK-FIXES-NEXT: printf("s has value %d\n", It.X);
+
+  for (U::iterator It = Uu.begin(), E = Uu.end(); It != E; ++It) {
+    Val* a = It.operator->();
+  }
+
+  U::iterator A;
+  for (U::iterator I = Uu.begin(), E = Uu.end(); I != E; ++I)
+    int K = A->X + I->X;
+  // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (auto & I : Uu)
+  // CHECK-FIXES-NEXT: int K = A->X + I.X;
+
+  dependent<int> V;
+  for (dependent<int>::iterator It = V.begin(), E = V.end();
+       It != E; ++It) {
+    printf("Fibonacci number is %d\n", *It);
+  }
+  // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int & It : V)
+  // CHECK-FIXES-NEXT: printf("Fibonacci number is %d\n", It);
+
+  for (dependent<int>::iterator It(V.begin()), E = V.end();
+       It != E; ++It) {
+    printf("Fibonacci number is %d\n", *It);
+  }
+  // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int & It : V)
+  // CHECK-FIXES-NEXT: printf("Fibonacci number is %d\n", It);
+
+  doublyDependent<int, int> Intmap;
+  for (doublyDependent<int, int>::iterator It = Intmap.begin(), E = Intmap.end();
+       It != E; ++It) {
+    printf("Intmap[%d] = %d", It->first, It->second);
+  }
+  // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (auto & It : Intmap)
+  // CHECK-FIXES: printf("Intmap[%d] = %d", It.first, It.second);
+
+  // PtrSet's iterator dereferences by value so auto & can't be used.
+  {
+    PtrSet<int *> Val_int_ptrs;
+    for (PtrSet<int *>::iterator I = Val_int_ptrs.begin(),
+                                 E = Val_int_ptrs.end();
+         I != E; ++I) {
+      (void) *I;
+    }
+    // CHECK-MESSAGES: :[[@LINE-5]]:5: warning: use range-based for loop instead
+    // CHECK-FIXES: for (auto Val_int_ptr : Val_int_ptrs)
+  }
+
+  // This container uses an iterator where the derefence type is a typedef of
+  // a reference type. Make sure non-const auto & is still used. A failure here
+  // means canonical types aren't being tested.
+  {
+    TypedefDerefContainer<int> Int_ptrs;
+    for (TypedefDerefContainer<int>::iterator I = Int_ptrs.begin(),
+                                              E = Int_ptrs.end();
+         I != E; ++I) {
+      (void) *I;
+    }
+    // CHECK-MESSAGES: :[[@LINE-5]]:5: warning: use range-based for loop instead
+    // CHECK-FIXES: for (int & Int_ptr : Int_ptrs)
+  }
+
+  {
+    // Iterators returning an rvalue reference should disqualify the loop from
+    // transformation.
+    RValueDerefContainer<int> Container;
+    for (RValueDerefContainer<int>::iterator I = Container.begin(),
+                                             E = Container.end();
+         I != E; ++I) {
+      (void) *I;
+    }
+  }
+
+  dependent<Val *> Dpp;
+  for (dependent<Val *>::iterator I = Dpp.begin(), E = Dpp.end(); I != E; ++I) {
+    printf("%d\n", (**I).X);
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (auto & I : Dpp)
+  // CHECK-FIXES-NEXT: printf("%d\n", (*I).X);
+
+  for (dependent<Val *>::iterator I = Dpp.begin(), E = Dpp.end(); I != E; ++I) {
+    printf("%d\n", (*I)->X);
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (auto & I : Dpp)
+  // CHECK-FIXES-NEXT: printf("%d\n", I->X);
+}
+
+// Tests to verify the proper use of auto where the init variable type and the
+// initializer type differ or are mostly the same except for const qualifiers.
+void different_type() {
+  // Ss.begin() returns a type 'iterator' which is just a non-const pointer and
+  // differs from const_iterator only on the const qualification.
+  S Ss;
+  for (S::const_iterator It = Ss.begin(), E = Ss.end(); It != E; ++It) {
+    printf("s has value %d\n", (*It).X);
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (auto It : Ss)
+  // CHECK-FIXES-NEXT: printf("s has value %d\n", It.X);
+
+  S *Ps;
+  for (S::const_iterator It = Ps->begin(), E = Ps->end(); It != E; ++It) {
+    printf("s has value %d\n", (*It).X);
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (auto P : *Ps)
+  // CHECK-FIXES-NEXT: printf("s has value %d\n", P.X);
+
+  // V.begin() returns a user-defined type 'iterator' which, since it's
+  // different from const_iterator, disqualifies these loops from
+  // transformation.
+  dependent<int> V;
+  for (dependent<int>::const_iterator It = V.begin(), E = V.end();
+       It != E; ++It) {
+    printf("Fibonacci number is %d\n", *It);
+  }
+
+  for (dependent<int>::const_iterator It(V.begin()), E = V.end();
+       It != E; ++It) {
+    printf("Fibonacci number is %d\n", *It);
+  }
+}
+
+// Tests to ensure that an implicit 'this' is picked up as the container.
+// If member calls are made to 'this' within the loop, the transform becomes
+// risky as these calls may affect state that affects the loop.
+class C {
+public:
+  typedef MutableVal *iterator;
+  typedef const MutableVal *const_iterator;
+
+  iterator begin();
+  iterator end();
+  const_iterator begin() const;
+  const_iterator end() const;
+
+  void doSomething();
+  void doSomething() const;
+
+  void doLoop() {
+    for (iterator I = begin(), E = end(); I != E; ++I)
+      (void) *I;
+    // CHECK-MESSAGES: :[[@LINE-2]]:5: warning: use range-based for loop instead
+    // CHECK-FIXES: for (auto & I : *this)
+
+    for (iterator I = C::begin(), E = C::end(); I != E; ++I)
+      (void) *I;
+    // CHECK-MESSAGES: :[[@LINE-2]]:5: warning: use range-based for loop instead
+    // CHECK-FIXES: for (auto & I : *this)
+
+    for (iterator I = begin(), E = end(); I != E; ++I) {
+      (void) *I;
+      doSomething();
+    }
+
+    for (iterator I = begin(); I != end(); ++I)
+      (void) *I;
+    // CHECK-MESSAGES: :[[@LINE-2]]:5: warning: use range-based for loop instead
+    // CHECK-FIXES: for (auto & I : *this)
+
+    for (iterator I = begin(); I != end(); ++I) {
+      (void) *I;
+      doSomething();
+    }
+  }
+
+  void doLoop() const {
+    for (const_iterator I = begin(), E = end(); I != E; ++I)
+      (void) *I;
+    // CHECK-MESSAGES: :[[@LINE-2]]:5: warning: use range-based for loop instead
+    // CHECK-FIXES: for (auto I : *this)
+
+    for (const_iterator I = C::begin(), E = C::end(); I != E; ++I)
+      (void) *I;
+    // CHECK-MESSAGES: :[[@LINE-2]]:5: warning: use range-based for loop instead
+    // CHECK-FIXES: for (auto I : *this)
+
+    for (const_iterator I = begin(), E = end(); I != E; ++I) {
+      (void) *I;
+      doSomething();
+    }
+  }
+};
+
+class C2 {
+public:
+  typedef MutableVal *iterator;
+
+  iterator begin() const;
+  iterator end() const;
+
+  void doLoop() {
+    // The implicit 'this' will have an Implicit cast to const C2* wrapped
+    // around it. Make sure the replacement still happens.
+    for (iterator I = begin(), E = end(); I != E; ++I)
+      (void) *I;
+    // CHECK-MESSAGES: :[[@LINE-2]]:5: warning: use range-based for loop instead
+    // CHECK-FIXES: for (auto & I : *this)
+  }
+};
+
+} // namespace Iterator
+
+namespace PseudoArray {
+
+const int N = 6;
+dependent<int> V;
+dependent<int> *Pv;
+const dependent<NonTriviallyCopyable> Constv;
+const dependent<NonTriviallyCopyable> *Pconstv;
+
+transparent<dependent<int>> Cv;
+
+void f() {
+  int Sum = 0;
+  for (int I = 0, E = V.size(); I < E; ++I) {
+    printf("Fibonacci number is %d\n", V[I]);
+    Sum += V[I] + 2;
+  }
+  // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int I : V)
+  // CHECK-FIXES-NEXT: printf("Fibonacci number is %d\n", I);
+  // CHECK-FIXES-NEXT: Sum += I + 2;
+
+  for (int I = 0, E = V.size(); I < E; ++I) {
+    printf("Fibonacci number is %d\n", V.at(I));
+    Sum += V.at(I) + 2;
+  }
+  // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int I : V)
+  // CHECK-FIXES-NEXT: printf("Fibonacci number is %d\n", I);
+  // CHECK-FIXES-NEXT: Sum += I + 2;
+
+  for (int I = 0, E = Pv->size(); I < E; ++I) {
+    printf("Fibonacci number is %d\n", Pv->at(I));
+    Sum += Pv->at(I) + 2;
+  }
+  // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int I : *Pv)
+  // CHECK-FIXES-NEXT: printf("Fibonacci number is %d\n", I);
+  // CHECK-FIXES-NEXT: Sum += I + 2;
+
+  // This test will fail if size() isn't called repeatedly, since it
+  // returns unsigned int, and 0 is deduced to be signed int.
+  // FIXME: Insert the necessary explicit conversion, or write out the types
+  // explicitly.
+  for (int I = 0; I < Pv->size(); ++I) {
+    printf("Fibonacci number is %d\n", (*Pv).at(I));
+    Sum += (*Pv)[I] + 2;
+  }
+  // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int I : *Pv)
+  // CHECK-FIXES-NEXT: printf("Fibonacci number is %d\n", I);
+  // CHECK-FIXES-NEXT: Sum += I + 2;
+
+  for (int I = 0; I < Cv->size(); ++I) {
+    printf("Fibonacci number is %d\n", Cv->at(I));
+    Sum += Cv->at(I) + 2;
+  }
+  // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int I : *Cv)
+  // CHECK-FIXES-NEXT: printf("Fibonacci number is %d\n", I);
+  // CHECK-FIXES-NEXT: Sum += I + 2;
+}
+
+// Ensure that 'const auto &' is used with containers of non-trivial types.
+void constness() {
+  int Sum = 0;
+  for (int I = 0, E = Constv.size(); I < E; ++I) {
+    printf("Fibonacci number is %d\n", Constv[I].X);
+    Sum += Constv[I].X + 2;
+  }
+  // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (const auto & I : Constv)
+  // CHECK-FIXES-NEXT: printf("Fibonacci number is %d\n", I.X);
+  // CHECK-FIXES-NEXT: Sum += I.X + 2;
+
+  for (int I = 0, E = Constv.size(); I < E; ++I) {
+    printf("Fibonacci number is %d\n", Constv.at(I).X);
+    Sum += Constv.at(I).X + 2;
+  }
+  // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (const auto & I : Constv)
+  // CHECK-FIXES-NEXT: printf("Fibonacci number is %d\n", I.X);
+  // CHECK-FIXES-NEXT: Sum += I.X + 2;
+
+  for (int I = 0, E = Pconstv->size(); I < E; ++I) {
+    printf("Fibonacci number is %d\n", Pconstv->at(I).X);
+    Sum += Pconstv->at(I).X + 2;
+  }
+  // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (const auto & I : *Pconstv)
+  // CHECK-FIXES-NEXT: printf("Fibonacci number is %d\n", I.X);
+  // CHECK-FIXES-NEXT: Sum += I.X + 2;
+
+  // This test will fail if size() isn't called repeatedly, since it
+  // returns unsigned int, and 0 is deduced to be signed int.
+  // FIXME: Insert the necessary explicit conversion, or write out the types
+  // explicitly.
+  for (int I = 0; I < Pconstv->size(); ++I) {
+    printf("Fibonacci number is %d\n", (*Pconstv).at(I).X);
+    Sum += (*Pconstv)[I].X + 2;
+  }
+  // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (const auto & I : *Pconstv)
+  // CHECK-FIXES-NEXT: printf("Fibonacci number is %d\n", I.X);
+  // CHECK-FIXES-NEXT: Sum += I.X + 2;
+}
+
+void constRef(const dependent<int>& ConstVRef) {
+  int sum = 0;
+  // FIXME: This does not work with size_t (probably due to the implementation
+  // of dependent); make dependent work exactly like a std container type.
+  for (int I = 0; I < ConstVRef.size(); ++I) {
+    sum += ConstVRef[I];
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int I : ConstVRef)
+  // CHECK-FIXES-NEXT: sum += I;
+
+  for (auto I = ConstVRef.begin(), E = ConstVRef.end(); I != E; ++I) {
+    sum += *I;
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int I : ConstVRef)
+  // CHECK-FIXES-NEXT: sum += I;
+}
+
+// Check for loops that don't mention containers.
+void noContainer() {
+  for (auto I = 0; I < V.size(); ++I) {
+  }
+
+  for (auto I = 0; I < V.size(); ++I)
+    ;
+}
+
+struct NoBeginEnd {
+  unsigned size() const;
+  unsigned& operator[](int);
+  const unsigned& operator[](int) const;
+};
+
+struct NoConstBeginEnd {
+  NoConstBeginEnd();
+  unsigned size() const;
+  unsigned* begin();
+  unsigned* end();
+  unsigned& operator[](int);
+  const unsigned& operator[](int) const;
+};
+
+struct ConstBeginEnd {
+  ConstBeginEnd();
+  unsigned size() const;
+  unsigned* begin() const;
+  unsigned* end() const;
+  unsigned& operator[](int);
+  const unsigned& operator[](int) const;
+};
+
+// Shouldn't transform pseudo-array uses if the container doesn't provide
+// begin() and end() of the right const-ness.
+void NoBeginEndTest() {
+  NoBeginEnd NBE;
+  for (unsigned I = 0, E = NBE.size(); I < E; ++I)
+    printf("%d\n", NBE[I]);
+
+  const NoConstBeginEnd Const_NCBE;
+  for (unsigned I = 0, E = Const_NCBE.size(); I < E; ++I)
+    printf("%d\n", Const_NCBE[I]);
+
+  ConstBeginEnd CBE;
+  for (unsigned I = 0, E = CBE.size(); I < E; ++I)
+    printf("%d\n", CBE[I]);
+  // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (unsigned int I : CBE)
+  // CHECK-FIXES-NEXT: printf("%d\n", I);
+
+  const ConstBeginEnd Const_CBE;
+  for (unsigned I = 0, E = Const_CBE.size(); I < E; ++I)
+    printf("%d\n", Const_CBE[I]);
+  // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (unsigned int I : Const_CBE)
+  // CHECK-FIXES-NEXT: printf("%d\n", I);
+}
+
+struct DerefByValue {
+  DerefByValue();
+  struct iter { unsigned operator*(); };
+  unsigned size() const;
+  iter begin();
+  iter end();
+  unsigned operator[](int);
+};
+
+void derefByValueTest() {
+  DerefByValue DBV;
+  for (unsigned I = 0, E = DBV.size(); I < E; ++I) {
+    printf("%d\n", DBV[I]);
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (unsigned int I : DBV)
+  // CHECK-FIXES-NEXT: printf("%d\n", I);
+
+  for (unsigned I = 0, E = DBV.size(); I < E; ++I) {
+    auto f = [DBV, I]() {};
+    printf("%d\n", DBV[I]);
+  }
+  // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (unsigned int I : DBV)
+  // CHECK-FIXES-NEXT: auto f = [DBV, &I]() {};
+  // CHECK-FIXES-NEXT: printf("%d\n", I);
+}
+
+void fundamentalTypesTest() {
+  const int N = 10;
+  bool Bools[N];
+  for (int i = 0; i < N; ++i)
+    printf("%d", Bools[i]);
+  // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (bool Bool : Bools)
+
+  int Ints[N];
+  unsigned short int Shorts[N];
+  for (int i = 0; i < N; ++i)
+    printf("%d", Shorts[i]);
+  // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (unsigned short Short : Shorts)
+
+  signed long Longs[N];
+  for (int i = 0; i < N; ++i)
+    printf("%d", Longs[i]);
+  // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (long Long : Longs)
+
+  long long int LongLongs[N];
+  for (int i = 0; i < N; ++i)
+    printf("%d", LongLongs[i]);
+  // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (long long LongLong : LongLongs)
+
+  char Chars[N];
+  for (int i = 0; i < N; ++i)
+    printf("%d", Chars[i]);
+  // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (char Char : Chars)
+
+  wchar_t WChars[N];
+  for (int i = 0; i < N; ++i)
+    printf("%d", WChars[i]);
+  // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (wchar_t WChar : WChars)
+
+  float Floats[N];
+  for (int i = 0; i < N; ++i)
+    printf("%d", Floats[i]);
+  // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (float Float : Floats)
+
+  double Doubles[N];
+  for (int i = 0; i < N; ++i)
+    printf("%d", Doubles[i]);
+  // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (double Double : Doubles)
+}
+
+} // namespace PseudoArray
diff --git a/test/clang-tidy/modernize-loop-convert-camelback.cpp b/test/clang-tidy/modernize-loop-convert-camelback.cpp
new file mode 100644 (file)
index 0000000..50044de
--- /dev/null
@@ -0,0 +1,33 @@
+// RUN: %check_clang_tidy %s modernize-loop-convert %t -- \
+// RUN:   -config="{CheckOptions: [{key: modernize-loop-convert.NamingStyle, value: 'camelBack'}]}" \
+// RUN:   -- -std=c++11 -I %S/Inputs/modernize-loop-convert
+
+#include "structures.h"
+
+const int n = 10;
+int arr[n];
+int nums[n];
+
+void naming() {
+  for (int i = 0; i < n; ++i) {
+    printf("%d\n", arr[i]);
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead [modernize-loop-convert]
+  // CHECK-FIXES: for (int i : arr)
+  // CHECK-FIXES-NEXT: printf("%d\n", i);
+
+  for (int i = 0; i < n; ++i) {
+    printf("%d\n", nums[i]);
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int num : nums)
+  // CHECK-FIXES-NEXT: printf("%d\n", num);
+
+  int num = 0;
+  for (int i = 0; i < n; ++i) {
+    printf("%d\n", nums[i] + num);
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int i : nums)
+  // CHECK-FIXES-NEXT: printf("%d\n", i + num);
+}
diff --git a/test/clang-tidy/modernize-loop-convert-const.cpp b/test/clang-tidy/modernize-loop-convert-const.cpp
new file mode 100644 (file)
index 0000000..806b6c1
--- /dev/null
@@ -0,0 +1,360 @@
+// RUN: %check_clang_tidy %s modernize-loop-convert %t -- -- -std=c++11
+
+struct Str {
+  Str() = default;
+  Str(const Str &) = default;
+  void constMember(int) const;
+  void nonConstMember(int);
+  bool operator<(const Str &str) const;     // const operator.
+  Str &operator=(const Str &str) = default; // non const operator.
+};
+
+// This class is non-trivially copyable because the copy-constructor and copy
+// assignment take non-const references.
+struct ModifiesRightSide {
+  ModifiesRightSide() = default;
+  ModifiesRightSide(ModifiesRightSide &) = default;
+  bool operator<(ModifiesRightSide &) const;
+  ModifiesRightSide &operator=(ModifiesRightSide &) = default;
+};
+
+template <typename T>
+void copyArg(T);
+
+template <typename T>
+void nonConstRefArg(T &);
+
+// If we define this as a template, the type is deduced to "T&",
+// and "const (T&) &" is the same as "T& &", and this collapses to "T&".
+void constRefArg(const Str &);
+void constRefArg(const ModifiesRightSide &);
+void constRefArg(const int &);
+
+void foo();
+
+const int N = 10;
+Str Array[N], OtherStr;
+ModifiesRightSide Right[N], OtherRight;
+int Ints[N], OtherInt;
+
+void memberFunctionsAndOperators() {
+  // Calling const member functions or operator is a const usage.
+  for (int I = 0; I < N; ++I) {
+    Array[I].constMember(0);
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead [modernize-loop-convert]
+  // CHECK-FIXES: for (auto I : Array)
+  // CHECK-FIXES-NEXT: I.constMember(0);
+
+  for (int I = 0; I < N; ++I) {
+    if (Array[I] < OtherStr)
+      foo();
+  }
+  // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop
+  // CHECK-FIXES: for (auto I : Array)
+  // CHECK-FIXES-NEXT: if (I < OtherStr)
+  for (int I = 0; I < N; ++I) {
+    if (Right[I] < OtherRight)
+      foo();
+  }
+  // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop
+  // CHECK-FIXES: for (const auto & I : Right)
+  // CHECK-FIXES-NEXT: if (I < OtherRight)
+
+  // Calling non-const member functions is not.
+  for (int I = 0; I < N; ++I) {
+    Array[I].nonConstMember(0);
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop
+  // CHECK-FIXES: for (auto & I : Array)
+  // CHECK-FIXES-NEXT: I.nonConstMember(0);
+
+  for (int I = 0; I < N; ++I) {
+    Array[I] = OtherStr;
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop
+  // CHECK-FIXES: for (auto & I : Array)
+  // CHECK-FIXES-NEXT: I = OtherStr;
+
+  for (int I = 0; I < N; ++I) {
+    Right[I] = OtherRight;
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop
+  // CHECK-FIXES: for (auto & I : Right)
+  // CHECK-FIXES-NEXT: I = OtherRight;
+}
+
+void usedAsParameterToFunctionOrOperator() {
+  // Copying is OK, as long as the copy constructor takes a const-reference.
+  for (int I = 0; I < N; ++I) {
+    copyArg(Array[I]);
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop
+  // CHECK-FIXES: for (auto I : Array)
+  // CHECK-FIXES-NEXT: copyArg(I);
+
+  for (int I = 0; I < N; ++I) {
+    copyArg(Right[I]);
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop
+  // CHECK-FIXES: for (auto & I : Right)
+  // CHECK-FIXES-NEXT: copyArg(I);
+
+  // Using as a const reference argument is allowed.
+  for (int I = 0; I < N; ++I) {
+    constRefArg(Array[I]);
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop
+  // CHECK-FIXES: for (auto I : Array)
+  // CHECK-FIXES-NEXT: constRefArg(I);
+
+  for (int I = 0; I < N; ++I) {
+    if (OtherStr < Array[I])
+      foo();
+  }
+  // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop
+  // CHECK-FIXES: for (auto I : Array)
+  // CHECK-FIXES-NEXT: if (OtherStr < I)
+
+  for (int I = 0; I < N; ++I) {
+    constRefArg(Right[I]);
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop
+  // CHECK-FIXES: for (const auto & I : Right)
+  // CHECK-FIXES-NEXT: constRefArg(I);
+
+  // Using as a non-const reference is not.
+  for (int I = 0; I < N; ++I) {
+    nonConstRefArg(Array[I]);
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop
+  // CHECK-FIXES: for (auto & I : Array)
+  // CHECK-FIXES-NEXT: nonConstRefArg(I);
+  for (int I = 0; I < N; ++I) {
+    nonConstRefArg(Right[I]);
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop
+  // CHECK-FIXES: for (auto & I : Right)
+  // CHECK-FIXES-NEXT: nonConstRefArg(I);
+  for (int I = 0; I < N; ++I) {
+    if (OtherRight < Right[I])
+      foo();
+  }
+  // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop
+  // CHECK-FIXES: for (auto & I : Right)
+  // CHECK-FIXES-NEXT: if (OtherRight < I)
+}
+
+void primitiveTypes() {
+  // As argument to a function.
+  for (int I = 0; I < N; ++I) {
+    copyArg(Ints[I]);
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop
+  // CHECK-FIXES: for (int Int : Ints)
+  // CHECK-FIXES-NEXT: copyArg(Int);
+  for (int I = 0; I < N; ++I) {
+    constRefArg(Ints[I]);
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop
+  // CHECK-FIXES: for (int Int : Ints)
+  // CHECK-FIXES-NEXT: constRefArg(Int);
+  for (int I = 0; I < N; ++I) {
+    nonConstRefArg(Ints[I]);
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop
+  // CHECK-FIXES: for (int & Int : Ints)
+  // CHECK-FIXES-NEXT: nonConstRefArg(Int);
+
+  // Builtin operators.
+  // Comparisons.
+  for (int I = 0; I < N; ++I) {
+    if (Ints[I] < N)
+      foo();
+  }
+  // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop
+  // CHECK-FIXES: for (int Int : Ints)
+  // CHECK-FIXES-NEXT: if (Int < N)
+
+  for (int I = 0; I < N; ++I) {
+    if (N == Ints[I])
+      foo();
+  }
+  // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop
+  // CHECK-FIXES: for (int Int : Ints)
+  // CHECK-FIXES-NEXT: if (N == Int)
+
+  // Assignment.
+  for (int I = 0; I < N; ++I) {
+    Ints[I] = OtherInt;
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop
+  // CHECK-FIXES: for (int & Int : Ints)
+  // CHECK-FIXES-NEXT: Int = OtherInt;
+
+  for (int I = 0; I < N; ++I) {
+    OtherInt = Ints[I];
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop
+  // CHECK-FIXES: for (int Int : Ints)
+  // CHECK-FIXES-NEXT: OtherInt = Int;
+
+  for (int I = 0; I < N; ++I) {
+    OtherInt = Ints[I] = OtherInt;
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop
+  // CHECK-FIXES: for (int & Int : Ints)
+  // CHECK-FIXES-NEXT: OtherInt = Int = OtherInt;
+
+  // Arithmetic operations.
+  for (int I = 0; I < N; ++I) {
+    OtherInt += Ints[I];
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop
+  // CHECK-FIXES: for (int Int : Ints)
+  // CHECK-FIXES-NEXT: OtherInt += Int;
+
+  for (int I = 0; I < N; ++I) {
+    Ints[I] += Ints[I];
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop
+  // CHECK-FIXES: for (int & Int : Ints)
+  // CHECK-FIXES-NEXT: Int += Int;
+
+  for (int I = 0; I < N; ++I) {
+    int Res = 5 * (Ints[I] + 1) - Ints[I];
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop
+  // CHECK-FIXES: for (int Int : Ints)
+  // CHECK-FIXES-NEXT: int Res = 5 * (Int + 1) - Int;
+}
+
+void takingReferences() {
+  // We do it twice to prevent the check from thinking that they are aliases.
+
+  // Class type.
+  for (int I = 0; I < N; ++I) {
+    Str &J = Array[I];
+    Str &K = Array[I];
+  }
+  // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop
+  // CHECK-FIXES: for (auto & I : Array)
+  // CHECK-FIXES-NEXT: Str &J = I;
+  // CHECK-FIXES-NEXT: Str &K = I;
+  for (int I = 0; I < N; ++I) {
+    const Str &J = Array[I];
+    const Str &K = Array[I];
+  }
+  // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop
+  // CHECK-FIXES: for (auto I : Array)
+  // CHECK-FIXES-NEXT: const Str &J = I;
+  // CHECK-FIXES-NEXT: const Str &K = I;
+
+  // Primitive type.
+  for (int I = 0; I < N; ++I) {
+    int &J = Ints[I];
+    int &K = Ints[I];
+  }
+  // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop
+  // CHECK-FIXES: for (int & Int : Ints)
+  // CHECK-FIXES-NEXT: int &J = Int;
+  // CHECK-FIXES-NEXT: int &K = Int;
+  for (int I = 0; I < N; ++I) {
+    const int &J = Ints[I];
+    const int &K = Ints[I];
+  }
+  // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop
+  // CHECK-FIXES: for (int Int : Ints)
+  // CHECK-FIXES-NEXT: const int &J = Int;
+  // CHECK-FIXES-NEXT: const int &K = Int;
+
+  // Aliases.
+  for (int I = 0; I < N; ++I) {
+    const Str &J = Array[I];
+    (void)J;
+  }
+  // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop
+  // CHECK-FIXES: for (auto J : Array)
+  for (int I = 0; I < N; ++I) {
+    Str &J = Array[I];
+    (void)J;
+  }
+  // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop
+  // CHECK-FIXES: for (auto & J : Array)
+
+  for (int I = 0; I < N; ++I) {
+    const int &J = Ints[I];
+    (void)J;
+  }
+  // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop
+  // CHECK-FIXES: for (int J : Ints)
+
+  for (int I = 0; I < N; ++I) {
+    int &J = Ints[I];
+    (void)J;
+  }
+  // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop
+  // CHECK-FIXES: for (int & J : Ints)
+}
+
+template <class T>
+struct vector {
+  unsigned size() const;
+  const T &operator[](int) const;
+  T &operator[](int);
+  T *begin();
+  T *end();
+  const T *begin() const;
+  const T *end() const;
+};
+
+// If the elements are already constant, we won't do any ImplicitCast to const.
+void testContainerOfConstIents() {
+  const int Ints[N]{};
+  for (int I = 0; I < N; ++I) {
+    OtherInt -= Ints[I];
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop
+  // CHECK-FIXES: for (int Int : Ints)
+
+  vector<const Str> Strs;
+  for (int I = 0; I < Strs.size(); ++I) {
+    Strs[I].constMember(0);
+    constRefArg(Strs[I]);
+  }
+  // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop
+  // CHECK-FIXES: for (auto Str : Strs)
+}
+
+// When we are inside a const-qualified member functions, all the data members
+// are implicitly set as const. As before, there won't be any ImplicitCast to
+// const in their usages.
+class TestInsideConstFunction {
+  const static int N = 10;
+  int Ints[N];
+  Str Array[N];
+  vector<int> V;
+
+  void foo() const {
+    for (int I = 0; I < N; ++I) {
+      if (Ints[I])
+        copyArg(Ints[I]);
+    }
+    // CHECK-MESSAGES: :[[@LINE-4]]:5: warning: use range-based for loop
+    // CHECK-FIXES: for (int Int : Ints)
+
+    for (int I = 0; I < N; ++I) {
+      Array[I].constMember(0);
+      constRefArg(Array[I]);
+    }
+    // CHECK-MESSAGES: :[[@LINE-4]]:5: warning: use range-based for loop
+    // CHECK-FIXES: for (auto I : Array)
+
+    for (int I = 0; I < V.size(); ++I) {
+      if (V[I])
+        copyArg(V[I]);
+    }
+    // CHECK-MESSAGES: :[[@LINE-4]]:5: warning: use range-based for loop
+    // CHECK-FIXES: for (int I : V)
+  }
+};
diff --git a/test/clang-tidy/modernize-loop-convert-extra.cpp b/test/clang-tidy/modernize-loop-convert-extra.cpp
new file mode 100644 (file)
index 0000000..b46ff25
--- /dev/null
@@ -0,0 +1,1074 @@
+// RUN: %check_clang_tidy %s modernize-loop-convert %t -- -- -std=c++11 -I %S/Inputs/modernize-loop-convert
+
+#include "structures.h"
+
+namespace Dependency {
+
+void f() {
+  const int N = 6;
+  const int M = 8;
+  int Arr[N][M];
+
+  for (int I = 0; I < N; ++I) {
+    int A = 0;
+    int B = Arr[I][A];
+  }
+  // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (auto & I : Arr)
+  // CHECK-FIXES-NEXT: int A = 0;
+  // CHECK-FIXES-NEXT: int B = I[A];
+
+  for (int J = 0; J < M; ++J) {
+    int A = 0;
+    int B = Arr[A][J];
+  }
+}
+
+} // namespace Dependency
+
+namespace NamingAlias {
+
+const int N = 10;
+
+Val Arr[N];
+dependent<Val> V;
+dependent<Val> *Pv;
+Val &func(Val &);
+void sideEffect(int);
+
+void aliasing() {
+  // If the loop container is only used for a declaration of a temporary
+  // variable to hold each element, we can name the new variable for the
+  // converted range-based loop as the temporary variable's name.
+
+  // In the following case, "T" is used as a temporary variable to hold each
+  // element, and thus we consider the name "T" aliased to the loop.
+  // The extra blank braces are left as a placeholder for after the variable
+  // declaration is deleted.
+  for (int I = 0; I < N; ++I) {
+    Val &T = Arr[I];
+    {}
+    int Y = T.X;
+  }
+  // CHECK-MESSAGES: :[[@LINE-5]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (auto & T : Arr)
+  // CHECK-FIXES-NOT: Val &{{[a-z_]+}} =
+  // CHECK-FIXES-NEXT: {}
+  // CHECK-FIXES-NEXT: int Y = T.X;
+
+  // The container was not only used to initialize a temporary loop variable for
+  // the container's elements, so we do not alias the new loop variable.
+  for (int I = 0; I < N; ++I) {
+    Val &T = Arr[I];
+    int Y = T.X;
+    int Z = Arr[I].X + T.X;
+  }
+  // CHECK-MESSAGES: :[[@LINE-5]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (auto & I : Arr)
+  // CHECK-FIXES-NEXT: Val &T = I;
+  // CHECK-FIXES-NEXT: int Y = T.X;
+  // CHECK-FIXES-NEXT: int Z = I.X + T.X;
+
+  for (int I = 0; I < N; ++I) {
+    Val T = Arr[I];
+    int Y = T.X;
+    int Z = Arr[I].X + T.X;
+  }
+  // CHECK-MESSAGES: :[[@LINE-5]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (auto & I : Arr)
+  // CHECK-FIXES-NEXT: Val T = I;
+  // CHECK-FIXES-NEXT: int Y = T.X;
+  // CHECK-FIXES-NEXT: int Z = I.X + T.X;
+
+  // The same for pseudo-arrays like std::vector<T> (or here dependent<Val>)
+  // which provide a subscript operator[].
+  for (int I = 0; I < V.size(); ++I) {
+    Val &T = V[I];
+    {}
+    int Y = T.X;
+  }
+  // CHECK-MESSAGES: :[[@LINE-5]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (auto & T : V)
+  // CHECK-FIXES-NEXT: {}
+  // CHECK-FIXES-NEXT: int Y = T.X;
+
+  // The same with a call to at()
+  for (int I = 0; I < Pv->size(); ++I) {
+    Val &T = Pv->at(I);
+    {}
+    int Y = T.X;
+  }
+  // CHECK-MESSAGES: :[[@LINE-5]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (auto & T : *Pv)
+  // CHECK-FIXES-NEXT: {}
+  // CHECK-FIXES-NEXT: int Y = T.X;
+
+  for (int I = 0; I < N; ++I) {
+    Val &T = func(Arr[I]);
+    int Y = T.X;
+  }
+  // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (auto & I : Arr)
+  // CHECK-FIXES-NEXT: Val &T = func(I);
+  // CHECK-FIXES-NEXT: int Y = T.X;
+
+  int IntArr[N];
+  for (unsigned I = 0; I < N; ++I) {
+    if (int Alias = IntArr[I]) {
+      sideEffect(Alias);
+    }
+  }
+  // CHECK-MESSAGES: :[[@LINE-5]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int Alias : IntArr)
+  // CHECK-FIXES-NEXT: if (Alias)
+
+  for (unsigned I = 0; I < N; ++I) {
+    while (int Alias = IntArr[I]) {
+      sideEffect(Alias);
+    }
+  }
+  // CHECK-MESSAGES: :[[@LINE-5]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int Alias : IntArr)
+  // CHECK-FIXES-NEXT: while (Alias)
+
+  for (unsigned I = 0; I < N; ++I) {
+    switch (int Alias = IntArr[I]) {
+    default:
+      sideEffect(Alias);
+    }
+  }
+  // CHECK-MESSAGES: :[[@LINE-6]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int Alias : IntArr)
+  // CHECK-FIXES-NEXT: switch (Alias)
+
+  for (unsigned I = 0; I < N; ++I) {
+    for (int Alias = IntArr[I]; Alias < N; ++Alias) {
+      sideEffect(Alias);
+    }
+  }
+  // CHECK-MESSAGES: :[[@LINE-5]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int Alias : IntArr)
+  // CHECK-FIXES-NEXT: for (; Alias < N; ++Alias)
+
+  for (unsigned I = 0; I < N; ++I) {
+    for (unsigned J = 0; int Alias = IntArr[I]; ++J) {
+      sideEffect(Alias);
+    }
+  }
+  // CHECK-MESSAGES: :[[@LINE-5]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int Alias : IntArr)
+  // CHECK-FIXES-NEXT: for (unsigned J = 0; Alias; ++J)
+
+  struct IntRef { IntRef(); IntRef(const int& i); operator int*(); };
+  for (int I = 0; I < N; ++I) {
+    IntRef Int(IntArr[I]);
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int I : IntArr)
+  // CHECK-FIXES-NEXT: IntRef Int(I);
+
+  int *PtrArr[N];
+  for (unsigned I = 0; I < N; ++I) {
+    const int* const P = PtrArr[I];
+    printf("%d\n", *P);
+  }
+  // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (auto P : PtrArr)
+  // CHECK-FIXES-NEXT: printf("%d\n", *P);
+
+  IntRef Refs[N];
+  for (unsigned I = 0; I < N; ++I) {
+    int *P = Refs[I];
+    printf("%d\n", *P);
+  }
+  // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (auto & Ref : Refs)
+  // CHECK-FIXES-NEXT: int *P = Ref;
+  // CHECK-FIXES-NEXT: printf("%d\n", *P);
+
+  // Ensure that removing the alias doesn't leave empty lines behind.
+  for (int I = 0; I < N; ++I) {
+    auto &X = IntArr[I];
+    X = 0;
+  }
+  // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int & X : IntArr) {
+  // CHECK-FIXES-NEXT: {{^    X = 0;$}}
+  // CHECK-FIXES-NEXT: {{^  }$}}
+}
+
+void refs_and_vals() {
+  // The following tests check that the transform correctly preserves the
+  // reference or value qualifiers of the aliased variable. That is, if the
+  // variable was declared as a value, the loop variable will be declared as a
+  // value and vice versa for references.
+
+  S Ss;
+  const S S_const = Ss;
+
+  for (S::const_iterator It = S_const.begin(); It != S_const.end(); ++It) {
+    MutableVal Alias = *It;
+    {}
+    Alias.X = 0;
+  }
+  // CHECK-MESSAGES: :[[@LINE-5]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (auto Alias : S_const)
+  // CHECK-FIXES-NOT: MutableVal {{[a-z_]+}} =
+  // CHECK-FIXES-NEXT: {}
+  // CHECK-FIXES-NEXT: Alias.X = 0;
+
+  for (S::iterator It = Ss.begin(), E = Ss.end(); It != E; ++It) {
+    MutableVal Alias = *It;
+    {}
+    Alias.X = 0;
+  }
+  // CHECK-MESSAGES: :[[@LINE-5]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (auto Alias : Ss)
+  // CHECK-FIXES-NOT: MutableVal {{[a-z_]+}} =
+  // CHECK-FIXES-NEXT: {}
+  // CHECK-FIXES-NEXT: Alias.X = 0;
+
+  for (S::iterator It = Ss.begin(), E = Ss.end(); It != E; ++It) {
+    MutableVal &Alias = *It;
+    {}
+    Alias.X = 0;
+  }
+  // CHECK-MESSAGES: :[[@LINE-5]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (auto & Alias : Ss)
+  // CHECK-FIXES-NOT: MutableVal &{{[a-z_]+}} =
+  // CHECK-FIXES-NEXT: {}
+  // CHECK-FIXES-NEXT: Alias.X = 0;
+
+  dependent<int> Dep, Other;
+  for (dependent<int>::iterator It = Dep.begin(), E = Dep.end(); It != E; ++It) {
+    printf("%d\n", *It);
+    const int& Idx = Other[0];
+    unsigned Othersize = Other.size();
+  }
+  // CHECK-MESSAGES: :[[@LINE-5]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int & It : Dep)
+  // CHECK-FIXES-NEXT: printf("%d\n", It);
+  // CHECK-FIXES-NEXT: const int& Idx = Other[0];
+  // CHECK-FIXES-NEXT: unsigned Othersize = Other.size();
+
+  for (int i = 0; i <  Other.size(); ++i) {
+    Other.at(i);
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int & i : Other)
+  // CHECK-FIXES: i;
+
+  for (int I = 0, E = Dep.size(); I != E; ++I) {
+    int Idx = Other.at(I);
+    Other.at(I, I);  // Should not trigger assert failure.
+  }
+}
+
+struct MemberNaming {
+  const static int N = 10;
+  int Ints[N], Ints_[N];
+  dependent<int> DInts;
+  void loops() {
+    for (int I = 0; I < N; ++I) {
+      printf("%d\n", Ints[I]);
+    }
+    // CHECK-MESSAGES: :[[@LINE-3]]:5: warning: use range-based for loop instead
+    // CHECK-FIXES: for (int Int : Ints)
+    // CHECK-FIXES-NEXT: printf("%d\n", Int);
+
+    for (int I = 0; I < N; ++I) {
+      printf("%d\n", Ints_[I]);
+    }
+    // CHECK-MESSAGES: :[[@LINE-3]]:5: warning: use range-based for loop instead
+    // CHECK-FIXES: for (int Int : Ints_)
+    // CHECK-FIXES-NEXT: printf("%d\n", Int);
+
+    for (int I = 0; I < DInts.size(); ++I) {
+      printf("%d\n", DInts[I]);
+    }
+    // CHECK-MESSAGES: :[[@LINE-3]]:5: warning: use range-based for loop instead
+    // CHECK-FIXES: for (int DInt : DInts)
+    // CHECK-FIXES-NEXT: printf("%d\n", DInt);
+  }
+
+  void outOfLine();
+};
+void MemberNaming::outOfLine() {
+  for (int I = 0; I < N; ++I) {
+    printf("%d\n", Ints[I]);
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int Int : Ints)
+  // CHECK-FIXES-NEXT: printf("%d\n", Int);
+
+  for (int I = 0; I < N; ++I) {
+    printf("%d\n", Ints_[I]);
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int Int : Ints_)
+  // CHECK-FIXES-NEXT: printf("%d\n", Int);
+}
+
+} // namespace NamingAlias
+
+namespace NamingConlict {
+
+#define MAX(a, b) (a > b) ? a : b
+#define DEF 5
+
+const int N = 10;
+int Nums[N];
+int Sum = 0;
+
+namespace ns {
+struct St {
+  int X;
+};
+}
+
+void sameNames() {
+  int Num = 0;
+  for (int I = 0; I < N; ++I) {
+    printf("Fibonacci number is %d\n", Nums[I]);
+    Sum += Nums[I] + 2 + Num;
+    (void)Nums[I];
+  }
+  // CHECK-MESSAGES: :[[@LINE-5]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int & I : Nums)
+  // CHECK-FIXES-NEXT: printf("Fibonacci number is %d\n", I);
+  // CHECK-FIXES-NEXT: Sum += I + 2 + Num;
+  // CHECK-FIXES-NEXT: (void)I;
+
+  int Elem = 0;
+  for (int I = 0; I < N; ++I) {
+    printf("Fibonacci number is %d\n", Nums[I]);
+    Sum += Nums[I] + 2 + Num + Elem;
+    (void)Nums[I];
+  }
+  // CHECK-MESSAGES: :[[@LINE-5]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int & I : Nums)
+  // CHECK-FIXES-NEXT: printf("Fibonacci number is %d\n", I);
+  // CHECK-FIXES-NEXT: Sum += I + 2 + Num + Elem;
+  // CHECK-FIXES-NEXT: (void)I;
+}
+
+void oldIndexConflict() {
+  for (int Num = 0; Num < N; ++Num) {
+    printf("Num: %d\n", Nums[Num]);
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int Num : Nums)
+  // CHECK-FIXES-NEXT: printf("Num: %d\n", Num);
+
+  S Things;
+  for (S::iterator Thing = Things.begin(), End = Things.end(); Thing != End; ++Thing) {
+    printf("Thing: %d %d\n", Thing->X, (*Thing).X);
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (auto & Thing : Things)
+  // CHECK-FIXES-NEXT: printf("Thing: %d %d\n", Thing.X, Thing.X);
+}
+
+void macroConflict() {
+  S MAXs;
+  for (S::iterator It = MAXs.begin(), E = MAXs.end(); It != E; ++It) {
+    printf("s has value %d\n", (*It).X);
+    printf("Max of 3 and 5: %d\n", MAX(3, 5));
+  }
+  // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (auto & It : MAXs)
+  // CHECK-FIXES-NEXT: printf("s has value %d\n", It.X);
+  // CHECK-FIXES-NEXT: printf("Max of 3 and 5: %d\n", MAX(3, 5));
+
+  for (S::const_iterator It = MAXs.begin(), E = MAXs.end(); It != E; ++It) {
+    printf("s has value %d\n", (*It).X);
+    printf("Max of 3 and 5: %d\n", MAX(3, 5));
+  }
+  // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (auto It : MAXs)
+  // CHECK-FIXES-NEXT: printf("s has value %d\n", It.X);
+  // CHECK-FIXES-NEXT: printf("Max of 3 and 5: %d\n", MAX(3, 5));
+
+  T DEFs;
+  for (T::iterator It = DEFs.begin(), E = DEFs.end(); It != E; ++It) {
+    if (*It == DEF) {
+      printf("I found %d\n", *It);
+    }
+  }
+  // CHECK-MESSAGES: :[[@LINE-5]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int & It : DEFs)
+  // CHECK-FIXES-NEXT: if (It == DEF)
+  // CHECK-FIXES-NEXT: printf("I found %d\n", It);
+}
+
+void keywordConflict() {
+  T ints;
+  for (T::iterator It = ints.begin(), E = ints.end(); It != E; ++It) {
+    *It = 5;
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int & It : ints)
+  // CHECK-FIXES-NEXT: It = 5;
+
+  U __FUNCTION__s;
+  for (U::iterator It = __FUNCTION__s.begin(), E = __FUNCTION__s.end();
+       It != E; ++It) {
+    int __FUNCTION__s_It = (*It).X + 2;
+  }
+  // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (auto & It : __FUNCTION__s)
+  // CHECK-FIXES-NEXT: int __FUNCTION__s_It = It.X + 2;
+}
+
+void typeConflict() {
+  T Vals;
+  // Using the name "Val", although it is the name of an existing struct, is
+  // safe in this loop since it will only exist within this scope.
+  for (T::iterator It = Vals.begin(), E = Vals.end(); It != E; ++It)
+    (void) *It;
+  // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int & Val : Vals)
+
+  // We cannot use the name "Val" in this loop since there is a reference to
+  // it in the body of the loop.
+  for (T::iterator It = Vals.begin(), E = Vals.end(); It != E; ++It) {
+    *It = sizeof(Val);
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int & It : Vals)
+  // CHECK-FIXES-NEXT: It = sizeof(Val);
+
+  typedef struct Val TD;
+  U TDs;
+  // Naming the variable "TD" within this loop is safe because the typedef
+  // was never used within the loop.
+  for (U::iterator It = TDs.begin(), E = TDs.end(); It != E; ++It)
+    (void) *It;
+  // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (auto & TD : TDs)
+
+  // "TD" cannot be used in this loop since the typedef is being used.
+  for (U::iterator It = TDs.begin(), E = TDs.end(); It != E; ++It) {
+    TD V;
+    V.X = 5;
+    (void) *It;
+  }
+  // CHECK-MESSAGES: :[[@LINE-5]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (auto & It : TDs)
+  // CHECK-FIXES-NEXT: TD V;
+  // CHECK-FIXES-NEXT: V.X = 5;
+
+  using ns::St;
+  T Sts;
+  for (T::iterator It = Sts.begin(), E = Sts.end(); It != E; ++It) {
+    *It = sizeof(St);
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int & It : Sts)
+  // CHECK-FIXES-NEXT: It = sizeof(St);
+}
+
+} // namespace NamingConflict
+
+namespace FreeBeginEnd {
+
+// FIXME: Loop Convert should detect free begin()/end() functions.
+
+struct MyArray {
+  unsigned size();
+};
+
+template <typename T>
+struct MyContainer {
+};
+
+int *begin(const MyArray &Arr);
+int *end(const MyArray &Arr);
+
+template <typename T>
+T *begin(const MyContainer<T> &C);
+template <typename T>
+T *end(const MyContainer<T> &C);
+
+// The Loop Convert Transform doesn't detect free functions begin()/end() and
+// so fails to transform these cases which it should.
+void f() {
+  MyArray Arr;
+  for (unsigned I = 0, E = Arr.size(); I < E; ++I) {
+  }
+
+  MyContainer<int> C;
+  for (int *I = begin(C), *E = end(C); I != E; ++I) {
+  }
+}
+
+} // namespace FreeBeginEnd
+
+namespace Nesting {
+
+void g(S::iterator It);
+void const_g(S::const_iterator It);
+class Foo {
+ public:
+  void g(S::iterator It);
+  void const_g(S::const_iterator It);
+};
+
+void f() {
+  const int N = 10;
+  const int M = 15;
+  Val Arr[N];
+  for (int I = 0; I < N; ++I) {
+    for (int J = 0; J < N; ++J) {
+      int K = Arr[I].X + Arr[J].X;
+      // The repeat is there to allow FileCheck to make sure the two variable
+      // names aren't the same.
+      int L = Arr[I].X + Arr[J].X;
+    }
+  }
+  // CHECK-MESSAGES: :[[@LINE-8]]:3: warning: use range-based for loop instead
+  // CHECK-MESSAGES: :[[@LINE-8]]:5: warning: use range-based for loop instead
+  // CHECK-FIXES: for (auto & I : Arr)
+  // CHECK-FIXES-NEXT: for (auto & J : Arr)
+  // CHECK-FIXES-NEXT: int K = I.X + J.X;
+  // CHECK-FIXES-NOT: int L = I.X + I.X;
+
+  // The inner loop is also convertible, but doesn't need to be converted
+  // immediately. FIXME: update this test when that changes.
+  Val Nest[N][M];
+  for (int I = 0; I < N; ++I) {
+    for (int J = 0; J < M; ++J) {
+      printf("Got item %d", Nest[I][J].X);
+    }
+  }
+  // CHECK-MESSAGES: :[[@LINE-5]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (auto & I : Nest)
+  // CHECK-FIXES-NEXT: for (int J = 0; J < M; ++J)
+  // CHECK-FIXES-NEXT: printf("Got item %d", I[J].X);
+
+  // Note that the order of M and N are switched for this test.
+  for (int J = 0; J < M; ++J) {
+    for (int I = 0; I < N; ++I) {
+      printf("Got item %d", Nest[I][J].X);
+    }
+  }
+  // CHECK-MESSAGES: :[[@LINE-4]]:5: warning: use range-based for loop instead
+  // CHECK-FIXES-NOT: for (auto & {{[a-zA-Z_]+}} : Nest[I])
+  // CHECK-FIXES: for (int J = 0; J < M; ++J)
+  // CHECK-FIXES-NEXT: for (auto & I : Nest)
+  // CHECK-FIXES-NEXT: printf("Got item %d", I[J].X);
+
+  // The inner loop is also convertible.
+  Nested<T> NestT;
+  for (Nested<T>::iterator I = NestT.begin(), E = NestT.end(); I != E; ++I) {
+    for (T::iterator TI = (*I).begin(), TE = (*I).end(); TI != TE; ++TI) {
+      printf("%d", *TI);
+    }
+  }
+  // CHECK-MESSAGES: :[[@LINE-5]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (auto & I : NestT)
+  // CHECK-FIXES-NEXT: for (T::iterator TI = I.begin(), TE = I.end(); TI != TE; ++TI)
+  // CHECK-FIXES-NEXT: printf("%d", *TI);
+
+  // The inner loop is also convertible.
+  Nested<S> NestS;
+  for (Nested<S>::const_iterator I = NestS.begin(), E = NestS.end(); I != E; ++I) {
+    for (S::const_iterator SI = (*I).begin(), SE = (*I).end(); SI != SE; ++SI) {
+      printf("%d", *SI);
+    }
+  }
+  // CHECK-MESSAGES: :[[@LINE-5]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (auto I : NestS)
+  // CHECK-FIXES-NEXT: for (S::const_iterator SI = I.begin(), SE = I.end(); SI != SE; ++SI)
+  // CHECK-FIXES-NEXT: printf("%d", *SI);
+
+  for (Nested<S>::const_iterator I = NestS.begin(), E = NestS.end(); I != E; ++I) {
+    const S &Ss = *I;
+    for (S::const_iterator SI = Ss.begin(), SE = Ss.end(); SI != SE; ++SI) {
+      printf("%d", *SI);
+      const_g(SI);
+    }
+  }
+  // CHECK-MESSAGES: :[[@LINE-7]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (auto Ss : NestS)
+
+  for (Nested<S>::iterator I = NestS.begin(), E = NestS.end(); I != E; ++I) {
+    S &Ss = *I;
+    for (S::iterator SI = Ss.begin(), SE = Ss.end(); SI != SE; ++SI) {
+      printf("%d", *SI);
+      g(SI);
+    }
+  }
+  // CHECK-MESSAGES: :[[@LINE-7]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (auto & Ss : NestS)
+
+  Foo foo;
+  for (Nested<S>::const_iterator I = NestS.begin(), E = NestS.end(); I != E; ++I) {
+    const S &Ss = *I;
+    for (S::const_iterator SI = Ss.begin(), SE = Ss.end(); SI != SE; ++SI) {
+      printf("%d", *SI);
+      foo.const_g(SI);
+    }
+  }
+  // CHECK-MESSAGES: :[[@LINE-7]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (auto Ss : NestS)
+
+  for (Nested<S>::iterator I = NestS.begin(), E = NestS.end(); I != E; ++I) {
+    S &Ss = *I;
+    for (S::iterator SI = Ss.begin(), SE = Ss.end(); SI != SE; ++SI) {
+      printf("%d", *SI);
+      foo.g(SI);
+    }
+  }
+  // CHECK-MESSAGES: :[[@LINE-7]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (auto & Ss : NestS)
+
+}
+
+} // namespace Nesting
+
+namespace SingleIterator {
+
+void complexContainer() {
+  X Exes[5];
+  int Index = 0;
+
+  for (S::iterator I = Exes[Index].getS().begin(), E = Exes[Index].getS().end(); I != E; ++I) {
+    MutableVal K = *I;
+    MutableVal J = *I;
+  }
+  // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (auto & I : Exes[Index].getS())
+  // CHECK-FIXES-NEXT: MutableVal K = I;
+  // CHECK-FIXES-NEXT: MutableVal J = I;
+}
+
+void f() {
+  /// begin()/end() - based for loops here:
+  T Tt;
+  for (T::iterator It = Tt.begin(); It != Tt.end(); ++It) {
+    printf("I found %d\n", *It);
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int & It : Tt)
+  // CHECK-FIXES-NEXT: printf("I found %d\n", It);
+
+  T *Pt;
+  for (T::iterator It = Pt->begin(); It != Pt->end(); ++It) {
+    printf("I found %d\n", *It);
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int & It : *Pt)
+  // CHECK-FIXES-NEXT: printf("I found %d\n", It);
+
+  S Ss;
+  for (S::iterator It = Ss.begin(); It != Ss.end(); ++It) {
+    printf("s has value %d\n", (*It).X);
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (auto & It : Ss)
+  // CHECK-FIXES-NEXT: printf("s has value %d\n", It.X);
+
+  S *Ps;
+  for (S::iterator It = Ps->begin(); It != Ps->end(); ++It) {
+    printf("s has value %d\n", (*It).X);
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (auto & P : *Ps)
+  // CHECK-FIXES-NEXT: printf("s has value %d\n", P.X);
+
+  for (S::iterator It = Ss.begin(); It != Ss.end(); ++It) {
+    printf("s has value %d\n", It->X);
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (auto & It : Ss)
+  // CHECK-FIXES-NEXT: printf("s has value %d\n", It.X);
+
+  for (S::iterator It = Ss.begin(); It != Ss.end(); ++It) {
+    It->X = 3;
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (auto & It : Ss)
+  // CHECK-FIXES-NEXT: It.X = 3;
+
+  for (S::iterator It = Ss.begin(); It != Ss.end(); ++It) {
+    (*It).X = 3;
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (auto & It : Ss)
+  // CHECK-FIXES-NEXT: It.X = 3;
+
+  for (S::iterator It = Ss.begin(); It != Ss.end(); ++It) {
+    It->nonConstFun(4, 5);
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (auto & It : Ss)
+  // CHECK-FIXES-NEXT: It.nonConstFun(4, 5);
+
+  U Uu;
+  for (U::iterator It = Uu.begin(); It != Uu.end(); ++It) {
+    printf("s has value %d\n", It->X);
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (auto & It : Uu)
+  // CHECK-FIXES-NEXT: printf("s has value %d\n", It.X);
+
+  for (U::iterator It = Uu.begin(); It != Uu.end(); ++It) {
+    printf("s has value %d\n", (*It).X);
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (auto & It : Uu)
+  // CHECK-FIXES-NEXT: printf("s has value %d\n", It.X);
+
+  U::iterator A;
+  for (U::iterator I = Uu.begin(); I != Uu.end(); ++I)
+    int K = A->X + I->X;
+  // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (auto & I : Uu)
+  // CHECK-FIXES-NEXT: int K = A->X + I.X;
+
+  dependent<int> V;
+  for (dependent<int>::iterator It = V.begin();
+       It != V.end(); ++It) {
+    printf("Fibonacci number is %d\n", *It);
+  }
+  // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int & It : V)
+  // CHECK-FIXES-NEXT: printf("Fibonacci number is %d\n", It);
+
+  for (dependent<int>::iterator It(V.begin());
+       It != V.end(); ++It) {
+    printf("Fibonacci number is %d\n", *It);
+  }
+  // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int & It : V)
+  // CHECK-FIXES-NEXT: printf("Fibonacci number is %d\n", It);
+
+  doublyDependent<int, int> intmap;
+  for (doublyDependent<int, int>::iterator It = intmap.begin();
+       It != intmap.end(); ++It) {
+    printf("intmap[%d] = %d", It->first, It->second);
+  }
+  // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (auto & It : intmap)
+  // CHECK-FIXES-NEXT: printf("intmap[%d] = %d", It.first, It.second);
+}
+
+void different_type() {
+  // Tests to verify the proper use of auto where the init variable type and the
+  // initializer type differ or are mostly the same except for const qualifiers.
+
+  // Ss.begin() returns a type 'iterator' which is just a non-const pointer and
+  // differs from const_iterator only on the const qualification.
+  S Ss;
+  for (S::const_iterator It = Ss.begin(); It != Ss.end(); ++It) {
+    printf("s has value %d\n", (*It).X);
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (auto It : Ss)
+  // CHECK-FIXES-NEXT: printf("s has value %d\n", It.X);
+
+  S *Ps;
+  for (S::const_iterator It = Ps->begin(); It != Ps->end(); ++It) {
+    printf("s has value %d\n", (*It).X);
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (auto P : *Ps)
+  // CHECK-FIXES-NEXT: printf("s has value %d\n", P.X);
+
+  // V.begin() returns a user-defined type 'iterator' which, since it's
+  // different from const_iterator, disqualifies these loops from
+  // transformation.
+  dependent<int> V;
+  for (dependent<int>::const_iterator It = V.begin(); It != V.end(); ++It) {
+    printf("Fibonacci number is %d\n", *It);
+  }
+
+  for (dependent<int>::const_iterator It(V.begin()); It != V.end(); ++It) {
+    printf("Fibonacci number is %d\n", *It);
+  }
+}
+
+} // namespace SingleIterator
+
+
+namespace Macros {
+
+#define TWO_PARAM(x, y) if (x == y) {}
+#define THREE_PARAM(x, y, z) if (x == y) {z;}
+
+const int N = 10;
+int Arr[N];
+
+void messing_with_macros() {
+  for (int I = 0; I < N; ++I) {
+    printf("Value: %d\n", Arr[I]);
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int I : Arr)
+  // CHECK-FIXES-NEXT:  printf("Value: %d\n", I);
+
+  for (int I = 0; I < N; ++I) {
+    printf("Value: %d\n", CONT Arr[I]);
+  }
+
+  // Multiple macro arguments.
+  for (int I = 0; I < N; ++I) {
+    TWO_PARAM(Arr[I], Arr[I]);
+    THREE_PARAM(Arr[I], Arr[I], Arr[I]);
+  }
+  // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int & I : Arr)
+  // CHECK-FIXES-NEXT: TWO_PARAM(I, I);
+  // CHECK-FIXES-NEXT: THREE_PARAM(I, I, I);
+}
+
+} // namespace Macros
+
+namespace Templates {
+
+template <class Container>
+void set_union(Container &container) {
+  for (typename Container::const_iterator SI = container.begin(),
+       SE = container.end(); SI != SE; ++SI) {
+    (void) *SI;
+  }
+
+  S Ss;
+  for (S::iterator SI = Ss.begin(), SE = Ss.end(); SI != SE; ++SI)
+    (void) *SI;
+  // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (auto & SI : Ss)
+}
+
+void template_instantiation() {
+  S Ss;
+  set_union(Ss);
+}
+
+} // namespace Templates
+
+namespace Lambdas {
+
+void capturesIndex() {
+  const int N = 10;
+  int Arr[N];
+  // FIXME: the next four loops could be convertible, if the capture list is
+  // also changed.
+
+  for (int I = 0; I < N; ++I)
+    auto F1 = [Arr, I]() { int R1 = Arr[I] + 1; };
+  // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int I : Arr)
+  // CHECK-FIXES-NEXT: auto F1 = [Arr, &I]() { int R1 = I + 1; };
+
+  for (int I = 0; I < N; ++I)
+    auto F2 = [Arr, &I]() { int R2 = Arr[I] + 3; };
+  // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int I : Arr)
+  // CHECK-FIXES-NEXT: auto F2 = [Arr, &I]() { int R2 = I + 3; };
+
+  // FIXME: alias don't work if the index is captured.
+  // Alias declared inside lambda (by value).
+  for (int I = 0; I < N; ++I)
+    auto F3 = [&Arr, I]() { int R3 = Arr[I]; };
+  // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int I : Arr)
+  // CHECK-FIXES-NEXT: auto F3 = [&Arr, &I]() { int R3 = I; };
+
+
+  for (int I = 0; I < N; ++I)
+    auto F4 = [&Arr, &I]() { int R4 = Arr[I]; };
+  // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int I : Arr)
+  // CHECK-FIXES-NEXT: auto F4 = [&Arr, &I]() { int R4 = I; };
+
+  // Alias declared inside lambda (by reference).
+  for (int I = 0; I < N; ++I)
+    auto F5 = [&Arr, I]() { int &R5 = Arr[I]; };
+  // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int & I : Arr)
+  // CHECK-FIXES-NEXT: auto F5 = [&Arr, &I]() { int &R5 = I; };
+
+
+  for (int I = 0; I < N; ++I)
+    auto F6 = [&Arr, &I]() { int &R6 = Arr[I]; };
+  // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int & I : Arr)
+  // CHECK-FIXES-NEXT: auto F6 = [&Arr, &I]() { int &R6 = I; };
+
+  for (int I = 0; I < N; ++I) {
+    auto F = [Arr, I](int k) {
+      printf("%d\n", Arr[I] + k);
+    };
+    F(Arr[I]);
+  }
+  // CHECK-MESSAGES: :[[@LINE-6]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int I : Arr)
+  // CHECK-FIXES-NEXT: auto F = [Arr, &I](int k)
+  // CHECK-FIXES-NEXT: printf("%d\n", I + k);
+  // CHECK-FIXES: F(I);
+}
+
+void implicitCapture() {
+  const int N = 10;
+  int Arr[N];
+  // Index is used, not convertible.
+  for (int I = 0; I < N; ++I) {
+    auto G1 = [&]() {
+      int R = Arr[I];
+      int J = I;
+    };
+  }
+
+  for (int I = 0; I < N; ++I) {
+    auto G2 = [=]() {
+      int R = Arr[I];
+      int J = I;
+    };
+  }
+
+  // Convertible.
+  for (int I = 0; I < N; ++I) {
+    auto G3 = [&]() {
+      int R3 = Arr[I];
+      int J3 = Arr[I] + R3;
+    };
+  }
+  // CHECK-MESSAGES: :[[@LINE-6]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int I : Arr)
+  // CHECK-FIXES-NEXT: auto G3 = [&]()
+  // CHECK-FIXES-NEXT: int R3 = I;
+  // CHECK-FIXES-NEXT: int J3 = I + R3;
+
+  for (int I = 0; I < N; ++I) {
+    auto G4 = [=]() {
+      int R4 = Arr[I] + 5;
+    };
+  }
+  // CHECK-MESSAGES: :[[@LINE-5]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int I : Arr)
+  // CHECK-FIXES-NEXT: auto G4 = [=]()
+  // CHECK-FIXES-NEXT: int R4 = I + 5;
+
+  // Alias by value.
+  for (int I = 0; I < N; ++I) {
+    auto G5 = [&]() {
+      int R5 = Arr[I];
+      int J5 = 8 + R5;
+    };
+  }
+  // CHECK-MESSAGES: :[[@LINE-6]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int R5 : Arr)
+  // CHECK-FIXES-NEXT: auto G5 = [&]()
+  // CHECK-FIXES-NEXT: int J5 = 8 + R5;
+
+  // Alias by reference.
+  for (int I = 0; I < N; ++I) {
+    auto G6 = [&]() {
+      int &R6 = Arr[I];
+      int J6 = -1 + R6;
+    };
+  }
+  // CHECK-MESSAGES: :[[@LINE-6]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int & R6 : Arr)
+  // CHECK-FIXES-NEXT: auto G6 = [&]()
+  // CHECK-FIXES-NEXT: int J6 = -1 + R6;
+}
+
+void iterators() {
+  dependent<int> Dep;
+
+  for (dependent<int>::iterator I = Dep.begin(), E = Dep.end(); I != E; ++I)
+    auto H1 = [&I]() { int R = *I; };
+  // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int & I : Dep)
+  // CHECK-FIXES-NEXT: auto H1 = [&I]() { int R = I; };
+
+  for (dependent<int>::iterator I = Dep.begin(), E = Dep.end(); I != E; ++I)
+    auto H2 = [&]() { int R = *I + 2; };
+  // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int & I : Dep)
+  // CHECK-FIXES-NEXT: auto H2 = [&]() { int R = I + 2; };
+
+  // FIXME: It doesn't work with const iterators.
+  for (dependent<int>::const_iterator I = Dep.begin(), E = Dep.end();
+       I != E; ++I)
+    auto H3 = [I]() { int R = *I; };
+
+  for (dependent<int>::const_iterator I = Dep.begin(), E = Dep.end();
+       I != E; ++I)
+    auto H4 = [&]() { int R = *I + 1; };
+
+  for (dependent<int>::const_iterator I = Dep.begin(), E = Dep.end();
+       I != E; ++I)
+    auto H5 = [=]() { int R = *I; };
+}
+
+void captureByValue() {
+  // When the index is captured by value, we should replace this by a capture
+  // by reference. This avoids extra copies.
+  // FIXME: this could change semantics on array or pseudoarray loops if the
+  // container is captured by copy.
+  const int N = 10;
+  int Arr[N];
+  dependent<int> Dep;
+
+  for (int I = 0; I < N; ++I) {
+    auto C1 = [&Arr, I]() { if (Arr[I] == 1); };
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int I : Arr)
+  // CHECK-FIXES-NEXT: auto C1 = [&Arr, &I]() { if (I == 1); };
+
+  for (unsigned I = 0; I < Dep.size(); ++I) {
+    auto C2 = [&Dep, I]() { if (Dep[I] == 2); };
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int I : Dep)
+  // CHECK-FIXES-NEXT: auto C2 = [&Dep, &I]() { if (I == 2); };
+}
+
+} // namespace Lambdas
+
+namespace InitLists {
+
+struct D { int Ii; };
+struct E { D Dd; };
+int g(int B);
+
+void f() {
+  const unsigned N = 3;
+  int Array[N];
+
+  // Subtrees of InitListExpr are visited twice. Test that we do not do repeated
+  // replacements.
+  for (unsigned I = 0; I < N; ++I) {
+    int A{ Array[I] };
+    int B{ g(Array[I]) };
+    int C{ g( { Array[I] } ) };
+    D Dd{ { g( { Array[I] } ) } };
+    E Ee{ { { g( { Array[I] } ) } } };
+  }
+  // CHECK-MESSAGES: :[[@LINE-7]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int I : Array)
+  // CHECK-FIXES-NEXT: int A{ I };
+  // CHECK-FIXES-NEXT: int B{ g(I) };
+  // CHECK-FIXES-NEXT: int C{ g( { I } ) };
+  // CHECK-FIXES-NEXT: D Dd{ { g( { I } ) } };
+  // CHECK-FIXES-NEXT: E Ee{ { { g( { I } ) } } };
+}
+
+} // namespace InitLists
+
+void bug28341() {
+  char v[5];
+  for(int i = 0; i < 5; ++i) {
+      unsigned char value = v[i];
+      if (value > 127)
+        ;
+  // CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for(unsigned char value : v)
+  // CHECK-FIXES-NEXT: if (value > 127)
+  }
+}
diff --git a/test/clang-tidy/modernize-loop-convert-lowercase.cpp b/test/clang-tidy/modernize-loop-convert-lowercase.cpp
new file mode 100644 (file)
index 0000000..c7bb221
--- /dev/null
@@ -0,0 +1,41 @@
+// RUN: %check_clang_tidy %s modernize-loop-convert %t -- \
+// RUN:   -config="{CheckOptions: [{key: modernize-loop-convert.NamingStyle, value: 'lower_case'}]}" \
+// RUN:   -- -std=c++11 -I %S/Inputs/modernize-loop-convert
+
+#include "structures.h"
+
+const int n = 10;
+int arr[n];
+int nums[n];
+int nums_[n];
+
+void naming() {
+  for (int i = 0; i < n; ++i) {
+    printf("%d\n", arr[i]);
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead [modernize-loop-convert]
+  // CHECK-FIXES: for (int i : arr)
+  // CHECK-FIXES-NEXT: printf("%d\n", i);
+
+  for (int i = 0; i < n; ++i) {
+    printf("%d\n", nums[i]);
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int num : nums)
+  // CHECK-FIXES-NEXT: printf("%d\n", num);
+
+  for (int i = 0; i < n; ++i) {
+    printf("%d\n", nums_[i]);
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int num : nums_)
+  // CHECK-FIXES-NEXT: printf("%d\n", num);
+
+  int num = 0;
+  for (int i = 0; i < n; ++i) {
+    printf("%d\n", nums[i] + num);
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int i : nums)
+  // CHECK-FIXES-NEXT: printf("%d\n", i + num);
+}
diff --git a/test/clang-tidy/modernize-loop-convert-negative.cpp b/test/clang-tidy/modernize-loop-convert-negative.cpp
new file mode 100644 (file)
index 0000000..c038437
--- /dev/null
@@ -0,0 +1,485 @@
+// RUN: %check_clang_tidy %s modernize-loop-convert %t -- -- -std=c++11 -I %S/Inputs/modernize-loop-convert
+
+#include "structures.h"
+
+// CHECK-FIXES-NOT: for ({{.*[^:]:[^:].*}})
+// CHECK-MESSAGES-NOT: modernize-loop-convert
+
+namespace Negative {
+
+const int N = 6;
+int Arr[N] = {1, 2, 3, 4, 5, 6};
+int (*pArr)[N] = &Arr;
+int Sum = 0;
+
+// Checks for the Index start and end:
+void IndexStartAndEnd() {
+  for (int I = 0; I < N + 1; ++I)
+    Sum += Arr[I];
+
+  for (int I = 0; I < N - 1; ++I)
+    Sum += Arr[I];
+
+  for (int I = 1; I < N; ++I)
+    Sum += Arr[I];
+
+  for (int I = 1; I < N; ++I)
+    Sum += Arr[I];
+
+  for (int I = 0;; ++I)
+    Sum += (*pArr)[I];
+}
+
+// Checks for invalid increment steps:
+void increment() {
+  for (int I = 0; I < N; --I)
+    Sum += Arr[I];
+
+  for (int I = 0; I < N; I)
+    Sum += Arr[I];
+
+  for (int I = 0; I < N;)
+    Sum += Arr[I];
+
+  for (int I = 0; I < N; I += 2)
+    Sum++;
+}
+
+// Checks to make sure that the Index isn't used outside of the array:
+void IndexUse() {
+  for (int I = 0; I < N; ++I)
+    Arr[I] += 1 + I;
+}
+
+// Check for loops that don't mention arrays
+void noArray() {
+  for (int I = 0; I < N; ++I)
+    Sum += I;
+
+  for (int I = 0; I < N; ++I) {
+  }
+
+  for (int I = 0; I < N; ++I)
+    ;
+}
+
+// Checks for incorrect loop variables.
+void mixedVariables() {
+  int BadIndex;
+  for (int I = 0; BadIndex < N; ++I)
+    Sum += Arr[I];
+
+  for (int I = 0; I < N; ++BadIndex)
+    Sum += Arr[I];
+
+  for (int I = 0; BadIndex < N; ++BadIndex)
+    Sum += Arr[I];
+
+  for (int I = 0; BadIndex < N; ++BadIndex)
+    Sum += Arr[BadIndex];
+}
+
+// Checks for multiple arrays Indexed.
+void multipleArrays() {
+  int BadArr[N];
+
+  for (int I = 0; I < N; ++I)
+    Sum += Arr[I] + BadArr[I];
+
+  for (int I = 0; I < N; ++I) {
+    int K = BadArr[I];
+    Sum += Arr[I] + K;
+  }
+}
+
+}
+
+namespace NegativeIterator {
+
+S Ss;
+T Tt;
+U Tu;
+
+struct BadBeginEnd : T {
+  iterator notBegin();
+  iterator notEnd();
+};
+
+void notBeginOrEnd() {
+  BadBeginEnd Bad;
+  for (T::iterator I = Bad.notBegin(), E = Bad.end();  I != E; ++I)
+    int K = *I;
+
+  for (T::iterator I = Bad.begin(), E = Bad.notEnd();  I != E; ++I)
+    int K = *I;
+}
+
+void badLoopShapes() {
+  for (T::iterator I = Tt.begin(), E = Tt.end(), F = E;  I != E; ++I)
+    int K = *I;
+
+  for (T::iterator I = Tt.begin(), E = Tt.end();  I != E;)
+    int K = *I;
+
+  for (T::iterator I = Tt.begin(), E = Tt.end();; ++I)
+    int K = *I;
+
+  T::iterator OutsideI;
+  T::iterator OutsideE;
+
+  for (; OutsideI != OutsideE; ++OutsideI)
+    int K = *OutsideI;
+}
+
+void iteratorArrayMix() {
+  int Lower;
+  const int N = 6;
+  for (T::iterator I = Tt.begin(), E = Tt.end(); Lower < N; ++I)
+    int K = *I;
+
+  for (T::iterator I = Tt.begin(), E = Tt.end(); Lower < N; ++Lower)
+    int K = *I;
+}
+
+struct ExtraConstructor : T::iterator {
+  ExtraConstructor(T::iterator, int);
+  explicit ExtraConstructor(T::iterator);
+};
+
+void badConstructor() {
+  for (T::iterator I = ExtraConstructor(Tt.begin(), 0), E = Tt.end();
+        I != E; ++I)
+    int K = *I;
+  for (T::iterator I = ExtraConstructor(Tt.begin()), E = Tt.end();  I != E; ++I)
+    int K = *I;
+}
+
+void foo(S::iterator It) {}
+class Foo {public: void bar(S::iterator It); };
+Foo Fo;
+
+void iteratorUsed() {
+  for (S::iterator I = Ss.begin(), E = Ss.end();  I != E; ++I)
+    foo(I);
+
+  for (S::iterator I = Ss.begin(), E = Ss.end();  I != E; ++I)
+    Fo.bar(I);
+
+  S::iterator Ret;
+  for (S::iterator I = Ss.begin(), E = Ss.end();  I != E; ++I)
+    Ret = I;
+}
+
+void iteratorMemberUsed() {
+  for (T::iterator I = Tt.begin(), E = Tt.end();  I != E; ++I)
+    I.X = *I;
+
+  for (T::iterator I = Tt.begin(), E = Tt.end();  I != E; ++I)
+    int K = I.X + *I;
+
+  for (T::iterator I = Tt.begin(), E = Tt.end();  I != E; ++I)
+    int K = E.X + *I;
+}
+
+void iteratorMethodCalled() {
+  for (T::iterator I = Tt.begin(), E = Tt.end();  I != E; ++I)
+    I.insert(3);
+
+  for (T::iterator I = Tt.begin(), E = Tt.end();  I != E; ++I)
+    if (I != I)
+      int K = 3;
+}
+
+void iteratorOperatorCalled() {
+  for (T::iterator I = Tt.begin(), E = Tt.end();  I != E; ++I)
+    int K = *(++I);
+
+  for (S::iterator I = Ss.begin(), E = Ss.end();  I != E; ++I)
+    MutableVal K = *(++I);
+}
+
+void differentContainers() {
+  T Other;
+  for (T::iterator I = Tt.begin(), E = Other.end();  I != E; ++I)
+    int K = *I;
+
+  for (T::iterator I = Other.begin(), E = Tt.end();  I != E; ++I)
+    int K = *I;
+
+  S OtherS;
+  for (S::iterator I = Ss.begin(), E = OtherS.end();  I != E; ++I)
+    MutableVal K = *I;
+
+  for (S::iterator I = OtherS.begin(), E = Ss.end();  I != E; ++I)
+    MutableVal K = *I;
+}
+
+void wrongIterators() {
+  T::iterator Other;
+  for (T::iterator I = Tt.begin(), E = Tt.end(); I != Other; ++I)
+    int K = *I;
+}
+
+struct EvilArrow : U {
+  // Please, no one ever write code like this.
+  U *operator->();
+};
+
+void differentMemberAccessTypes() {
+  EvilArrow A;
+  for (EvilArrow::iterator I = A.begin(), E = A->end();  I != E; ++I)
+    Val K = *I;
+  for (EvilArrow::iterator I = A->begin(), E = A.end();  I != E; ++I)
+    Val K = *I;
+}
+
+void f(const T::iterator &It, int);
+void f(const T &It, int);
+void g(T &It, int);
+
+void iteratorPassedToFunction() {
+  for (T::iterator I = Tt.begin(), E = Tt.end();  I != E; ++I)
+    f(I, *I);
+}
+
+// FIXME: These tests can be removed if this tool ever does enough analysis to
+// decide that this is a safe transformation. Until then, we don't want it
+// applied.
+void iteratorDefinedOutside() {
+  T::iterator TheEnd = Tt.end();
+  for (T::iterator I = Tt.begin(); I != TheEnd; ++I)
+    int K = *I;
+
+  T::iterator TheBegin = Tt.begin();
+  for (T::iterator E = Tt.end(); TheBegin != E; ++TheBegin)
+    int K = *TheBegin;
+}
+
+} // namespace NegativeIterator
+
+namespace NegativePseudoArray {
+
+const int N = 6;
+dependent<int> V;
+dependent<int> *Pv;
+
+int Sum = 0;
+
+// Checks for the Index start and end:
+void IndexStartAndEnd() {
+  for (int I = 0; I < V.size() + 1; ++I)
+    Sum += V[I];
+
+  for (int I = 0; I < V.size() - 1; ++I)
+    Sum += V[I];
+
+  for (int I = 1; I < V.size(); ++I)
+    Sum += V[I];
+
+  for (int I = 1; I < V.size(); ++I)
+    Sum += V[I];
+
+  for (int I = 0;; ++I)
+    Sum += (*Pv)[I];
+}
+
+// Checks for invalid increment steps:
+void increment() {
+  for (int I = 0; I < V.size(); --I)
+    Sum += V[I];
+
+  for (int I = 0; I < V.size(); I)
+    Sum += V[I];
+
+  for (int I = 0; I < V.size();)
+    Sum += V[I];
+
+  for (int I = 0; I < V.size(); I += 2)
+    Sum++;
+}
+
+// Checks to make sure that the Index isn't used outside of the container:
+void IndexUse() {
+  for (int I = 0; I < V.size(); ++I)
+    V[I] += 1 + I;
+}
+
+// Checks for incorrect loop variables.
+void mixedVariables() {
+  int BadIndex;
+  for (int I = 0; BadIndex < V.size(); ++I)
+    Sum += V[I];
+
+  for (int I = 0; I < V.size(); ++BadIndex)
+    Sum += V[I];
+
+  for (int I = 0; BadIndex < V.size(); ++BadIndex)
+    Sum += V[I];
+
+  for (int I = 0; BadIndex < V.size(); ++BadIndex)
+    Sum += V[BadIndex];
+}
+
+// Checks for an array Indexed in addition to the container.
+void multipleArrays() {
+  int BadArr[N];
+
+  for (int I = 0; I < V.size(); ++I)
+    Sum += V[I] + BadArr[I];
+
+  for (int I = 0; I < V.size(); ++I)
+    Sum += BadArr[I];
+
+  for (int I = 0; I < V.size(); ++I) {
+    int K = BadArr[I];
+    Sum += K + 2;
+  }
+
+  for (int I = 0; I < V.size(); ++I) {
+    int K = BadArr[I];
+    Sum += V[I] + K;
+  }
+}
+
+// Checks for multiple containers being Indexed container.
+void multipleContainers() {
+  dependent<int> BadArr;
+
+  for (int I = 0; I < V.size(); ++I)
+    Sum += V[I] + BadArr[I];
+
+  for (int I = 0; I < V.size(); ++I)
+    Sum += BadArr[I];
+
+  for (int I = 0; I < V.size(); ++I) {
+    int K = BadArr[I];
+    Sum += K + 2;
+  }
+
+  for (int I = 0; I < V.size(); ++I) {
+    int K = BadArr[I];
+    Sum += V[I] + K;
+  }
+}
+
+// Check to make sure that dereferenced pointers-to-containers behave nicely.
+void derefContainer() {
+  // Note the dependent<T>::operator*() returns another dependent<T>.
+  // This test makes sure that we don't allow an arbitrary number of *'s.
+  for (int I = 0; I < Pv->size(); ++I)
+    Sum += (**Pv).at(I);
+
+  for (int I = 0; I < Pv->size(); ++I)
+    Sum += (**Pv)[I];
+}
+
+void wrongEnd() {
+  int Bad;
+  for (int I = 0, E = V.size(); I < Bad; ++I)
+    Sum += V[I];
+}
+
+// Checks to see that non-const member functions are not called on the container
+// object.
+// These could be conceivably allowed with a lower required confidence level.
+void memberFunctionCalled() {
+  for (int I = 0; I < V.size(); ++I) {
+    Sum += V[I];
+    V.foo();
+  }
+
+  for (int I = 0; I < V.size(); ++I) {
+    Sum += V[I];
+    dependent<int>::iterator It = V.begin();
+  }
+}
+
+} // namespace NegativePseudoArray
+
+namespace NegativeMultiEndCall {
+
+S Ss;
+T Tt;
+U Uu;
+
+void f(X);
+void f(S);
+void f(T);
+
+void complexContainer() {
+  X Xx;
+  for (S::iterator I = Xx.Ss.begin(), E = Xx.Ss.end();  I != E; ++I) {
+    f(Xx);
+    MutableVal K = *I;
+  }
+
+  for (T::iterator I = Xx.Tt.begin(), E = Xx.Tt.end();  I != E; ++I) {
+    f(Xx);
+    int K = *I;
+  }
+
+  for (S::iterator I = Xx.Ss.begin(), E = Xx.Ss.end();  I != E; ++I) {
+    f(Xx.Ss);
+    MutableVal K = *I;
+  }
+
+  for (T::iterator I = Xx.Tt.begin(), E = Xx.Tt.end();  I != E; ++I) {
+    f(Xx.Tt);
+    int K = *I;
+  }
+
+  for (S::iterator I = Xx.getS().begin(), E = Xx.getS().end();  I != E; ++I) {
+    f(Xx.getS());
+    MutableVal K = *I;
+  }
+
+  X Exes[5];
+  int Index = 0;
+
+  for (S::iterator I = Exes[Index].getS().begin(),
+                   E = Exes[Index].getS().end();
+        I != E; ++I) {
+    Index++;
+    MutableVal K = *I;
+  }
+}
+
+} // namespace NegativeMultiEndCall
+
+namespace NoUsages {
+
+const int N = 6;
+int Arr[N] = {1, 2, 3, 4, 5, 6};
+S Ss;
+dependent<int> V;
+int Count = 0;
+
+void foo();
+
+void f() {
+  for (int I = 0; I < N; ++I) {}
+  for (int I = 0; I < N; ++I)
+    printf("Hello world\n");
+  for (int I = 0; I < N; ++I)
+    ++Count;
+  for (int I = 0; I < N; ++I)
+    foo();
+
+  for (S::iterator I = Ss.begin(), E = Ss.end(); I != E; ++I) {}
+  for (S::iterator I = Ss.begin(), E = Ss.end(); I != E; ++I)
+    printf("Hello world\n");
+  for (S::iterator I = Ss.begin(), E = Ss.end(); I != E; ++I)
+    ++Count;
+  for (S::iterator I = Ss.begin(), E = Ss.end(); I != E; ++I)
+    foo();
+
+  for (int I = 0; I < V.size(); ++I) {}
+  for (int I = 0; I < V.size(); ++I)
+    printf("Hello world\n");
+  for (int I = 0; I < V.size(); ++I)
+    ++Count;
+  for (int I = 0; I < V.size(); ++I)
+    foo();
+}
+
+} // namespace NoUsages
diff --git a/test/clang-tidy/modernize-loop-convert-uppercase.cpp b/test/clang-tidy/modernize-loop-convert-uppercase.cpp
new file mode 100644 (file)
index 0000000..4d1d5c2
--- /dev/null
@@ -0,0 +1,41 @@
+// RUN: %check_clang_tidy %s modernize-loop-convert %t -- \
+// RUN:   -config="{CheckOptions: [{key: modernize-loop-convert.NamingStyle, value: 'UPPER_CASE'}]}" \
+// RUN:   -- -std=c++11 -I %S/Inputs/modernize-loop-convert
+
+#include "structures.h"
+
+const int N = 10;
+int ARR[N];
+int NUMS[N];
+int NUMS_[N];
+
+void naming() {
+  for (int I = 0; I < N; ++I) {
+    printf("%d\n", ARR[I]);
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead [modernize-loop-convert]
+  // CHECK-FIXES: for (int I : ARR)
+  // CHECK-FIXES-NEXT: printf("%d\n", I);
+
+  for (int I = 0; I < N; ++I) {
+    printf("%d\n", NUMS[I]);
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int NUM : NUMS)
+  // CHECK-FIXES-NEXT: printf("%d\n", NUM);
+
+  for (int I = 0; I < N; ++I) {
+    printf("%d\n", NUMS_[I]);
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int NUM : NUMS_)
+  // CHECK-FIXES-NEXT: printf("%d\n", NUM);
+
+  int NUM = 0;
+  for (int I = 0; I < N; ++I) {
+    printf("%d\n", NUMS[I] + NUM);
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
+  // CHECK-FIXES: for (int I : NUMS)
+  // CHECK-FIXES-NEXT: printf("%d\n", I + NUM);
+}
diff --git a/test/clang-tidy/modernize-loop-convert.c b/test/clang-tidy/modernize-loop-convert.c
new file mode 100644 (file)
index 0000000..4c7d956
--- /dev/null
@@ -0,0 +1,12 @@
+// RUN: clang-tidy %s -checks=-*,modernize-loop-convert -- -std=c11 | count 0
+
+// Note: this test expects no diagnostics, but FileCheck cannot handle that,
+// hence the use of | count 0.
+
+int arr[6] = {1, 2, 3, 4, 5, 6};
+
+void f(void) {
+  for (int i = 0; i < 6; ++i) {
+    (void)arr[i];
+  }
+}
diff --git a/test/clang-tidy/modernize-make-shared-header.cpp b/test/clang-tidy/modernize-make-shared-header.cpp
new file mode 100644 (file)
index 0000000..21b07ee
--- /dev/null
@@ -0,0 +1,17 @@
+// RUN: %check_clang_tidy %s modernize-make-shared %t -- \
+// RUN:   -config="{CheckOptions: \
+// RUN:     [{key: modernize-make-shared.MakeSmartPtrFunction, \
+// RUN:       value: 'my::MakeShared'}, \
+// RUN:      {key: modernize-make-shared.MakeSmartPtrFunctionHeader, \
+// RUN:       value: 'make_shared_util.h'} \
+// RUN:     ]}" \
+// RUN:   -- -std=c++11 -I%S/Inputs/modernize-smart-ptr
+
+#include "shared_ptr.h"
+// CHECK-FIXES: #include "make_shared_util.h"
+
+void f() {
+  std::shared_ptr<int> P1 = std::shared_ptr<int>(new int());
+  // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: use my::MakeShared instead
+  // CHECK-FIXES: std::shared_ptr<int> P1 = my::MakeShared<int>();
+}
diff --git a/test/clang-tidy/modernize-make-shared.cpp b/test/clang-tidy/modernize-make-shared.cpp
new file mode 100644 (file)
index 0000000..ed4da99
--- /dev/null
@@ -0,0 +1,286 @@
+// RUN: %check_clang_tidy %s modernize-make-shared %t -- -- -std=c++11 \
+// RUN:   -I%S/Inputs/modernize-smart-ptr
+
+#include "shared_ptr.h"
+// CHECK-FIXES: #include <memory>
+
+struct Base {
+  Base();
+  Base(int, int);
+};
+
+struct Derived : public Base {
+  Derived();
+  Derived(int, int);
+};
+
+struct APair {
+  int a, b;
+};
+
+struct DPair {
+  DPair() : a(0), b(0) {}
+  DPair(int x, int y) : a(y), b(x) {}
+  int a, b;
+};
+
+struct Empty {};
+
+template <class T>
+using shared_ptr_ = std::shared_ptr<T>;
+
+void *operator new(__SIZE_TYPE__ Count, void *Ptr);
+
+int g(std::shared_ptr<int> P);
+
+std::shared_ptr<Base> getPointer() {
+  return std::shared_ptr<Base>(new Base);
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use std::make_shared instead
+  // CHECK-FIXES: return std::make_shared<Base>();
+}
+
+void basic() {
+  std::shared_ptr<int> P1 = std::shared_ptr<int>(new int());
+  // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: use std::make_shared instead [modernize-make-shared]
+  // CHECK-FIXES: std::shared_ptr<int> P1 = std::make_shared<int>();
+
+  P1.reset(new int());
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: use std::make_shared instead [modernize-make-shared]
+  // CHECK-FIXES: P1 = std::make_shared<int>();
+
+  P1 = std::shared_ptr<int>(new int());
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use std::make_shared instead [modernize-make-shared]
+  // CHECK-FIXES: P1 = std::make_shared<int>();
+
+  // Without parenthesis.
+  std::shared_ptr<int> P2 = std::shared_ptr<int>(new int);
+  // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: use std::make_shared instead [modernize-make-shared]
+  // CHECK-FIXES: std::shared_ptr<int> P2 = std::make_shared<int>();
+
+  P2.reset(new int);
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: use std::make_shared instead [modernize-make-shared]
+  // CHECK-FIXES: P2 = std::make_shared<int>();
+
+  P2 = std::shared_ptr<int>(new int);
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use std::make_shared instead [modernize-make-shared]
+  // CHECK-FIXES: P2 = std::make_shared<int>();
+
+  // With auto.
+  auto P3 = std::shared_ptr<int>(new int());
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use std::make_shared instead
+  // CHECK-FIXES: auto P3 = std::make_shared<int>();
+
+  {
+    // No std.
+    using namespace std;
+    shared_ptr<int> Q = shared_ptr<int>(new int());
+    // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: use std::make_shared instead
+    // CHECK-FIXES: shared_ptr<int> Q = std::make_shared<int>();
+
+    Q = shared_ptr<int>(new int());
+    // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use std::make_shared instead
+    // CHECK-FIXES: Q = std::make_shared<int>();
+  }
+
+  std::shared_ptr<int> R(new int());
+
+  // Create the shared_ptr as a parameter to a function.
+  int T = g(std::shared_ptr<int>(new int()));
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use std::make_shared instead
+  // CHECK-FIXES: int T = g(std::make_shared<int>());
+
+  // Only replace if the type in the template is the same as the type returned
+  // by the new operator.
+  auto Pderived = std::shared_ptr<Base>(new Derived());
+
+  // OK to replace for reset and assign
+  Pderived.reset(new Derived());
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use std::make_shared instead
+  // CHECK-FIXES: Pderived = std::make_shared<Derived>();
+
+  Pderived = std::shared_ptr<Derived>(new Derived());
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: use std::make_shared instead
+  // CHECK-FIXES: Pderived = std::make_shared<Derived>();
+
+  // FIXME: OK to replace if assigned to shared_ptr<Base>
+  Pderived = std::shared_ptr<Base>(new Derived());
+
+  // FIXME: OK to replace when auto is not used
+  std::shared_ptr<Base> PBase = std::shared_ptr<Base>(new Derived());
+
+  // The pointer is returned by the function, nothing to do.
+  std::shared_ptr<Base> RetPtr = getPointer();
+
+  // This emulates std::move.
+  std::shared_ptr<int> Move = static_cast<std::shared_ptr<int> &&>(P1);
+
+  // Placement arguments should not be removed.
+  int *PInt = new int;
+  std::shared_ptr<int> Placement = std::shared_ptr<int>(new (PInt) int{3});
+  Placement.reset(new (PInt) int{3});
+  Placement = std::shared_ptr<int>(new (PInt) int{3});
+}
+
+// Calling make_smart_ptr from within a member function of a type with a
+// private or protected constructor would be ill-formed.
+class Private {
+private:
+  Private(int z) {}
+
+public:
+  Private() {}
+  void create() {
+    auto callsPublic = std::shared_ptr<Private>(new Private);
+    // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: use std::make_shared instead
+    // CHECK-FIXES: auto callsPublic = std::make_shared<Private>();
+    auto ptr = std::shared_ptr<Private>(new Private(42));
+    ptr.reset(new Private(42));
+    ptr = std::shared_ptr<Private>(new Private(42));
+  }
+
+  virtual ~Private();
+};
+
+class Protected {
+protected:
+  Protected() {}
+
+public:
+  Protected(int, int) {}
+  void create() {
+    auto callsPublic = std::shared_ptr<Protected>(new Protected(1, 2));
+    // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: use std::make_shared instead
+    // CHECK-FIXES: auto callsPublic = std::make_shared<Protected>(1, 2);
+    auto ptr = std::shared_ptr<Protected>(new Protected);
+    ptr.reset(new Protected);
+    ptr = std::shared_ptr<Protected>(new Protected);
+  }
+};
+
+void initialization(int T, Base b) {
+  // Test different kinds of initialization of the pointee.
+
+  // Direct initialization with parenthesis.
+  std::shared_ptr<DPair> PDir1 = std::shared_ptr<DPair>(new DPair(1, T));
+  // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: use std::make_shared instead
+  // CHECK-FIXES: std::shared_ptr<DPair> PDir1 = std::make_shared<DPair>(1, T);
+  PDir1.reset(new DPair(1, T));
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use std::make_shared instead
+  // CHECK-FIXES: PDir1 = std::make_shared<DPair>(1, T);
+
+  // Direct initialization with braces.
+  std::shared_ptr<DPair> PDir2 = std::shared_ptr<DPair>(new DPair{2, T});
+  // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: use std::make_shared instead
+  // CHECK-FIXES: std::shared_ptr<DPair> PDir2 = std::make_shared<DPair>(2, T);
+  PDir2.reset(new DPair{2, T});
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use std::make_shared instead
+  // CHECK-FIXES: PDir2 = std::make_shared<DPair>(2, T);
+
+  // Aggregate initialization.
+  std::shared_ptr<APair> PAggr = std::shared_ptr<APair>(new APair{T, 1});
+  // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: use std::make_shared instead
+  // CHECK-FIXES: std::shared_ptr<APair> PAggr = std::make_shared<APair>(APair{T, 1});
+  PAggr.reset(new APair{T, 1});
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use std::make_shared instead
+  // CHECK-FIXES: std::make_shared<APair>(APair{T, 1});
+
+  // Test different kinds of initialization of the pointee, when the shared_ptr
+  // is initialized with braces.
+
+  // Direct initialization with parenthesis.
+  std::shared_ptr<DPair> PDir3 = std::shared_ptr<DPair>{new DPair(3, T)};
+  // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: use std::make_shared instead
+  // CHECK-FIXES: std::shared_ptr<DPair> PDir3 = std::make_shared<DPair>(3, T);
+
+  // Direct initialization with braces.
+  std::shared_ptr<DPair> PDir4 = std::shared_ptr<DPair>{new DPair{4, T}};
+  // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: use std::make_shared instead
+  // CHECK-FIXES: std::shared_ptr<DPair> PDir4 = std::make_shared<DPair>(4, T);
+
+  // Aggregate initialization.
+  std::shared_ptr<APair> PAggr2 = std::shared_ptr<APair>{new APair{T, 2}};
+  // CHECK-MESSAGES: :[[@LINE-1]]:35: warning: use std::make_shared instead
+  // CHECK-FIXES: std::shared_ptr<APair> PAggr2 = std::make_shared<APair>(APair{T, 2});
+
+  // Direct initialization with parenthesis, without arguments.
+  std::shared_ptr<DPair> PDir5 = std::shared_ptr<DPair>(new DPair());
+  // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: use std::make_shared instead
+  // CHECK-FIXES: std::shared_ptr<DPair> PDir5 = std::make_shared<DPair>();
+
+  // Direct initialization with braces, without arguments.
+  std::shared_ptr<DPair> PDir6 = std::shared_ptr<DPair>(new DPair{});
+  // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: use std::make_shared instead
+  // CHECK-FIXES: std::shared_ptr<DPair> PDir6 = std::make_shared<DPair>();
+
+  // Aggregate initialization without arguments.
+  std::shared_ptr<Empty> PEmpty = std::shared_ptr<Empty>(new Empty{});
+  // CHECK-MESSAGES: :[[@LINE-1]]:35: warning: use std::make_shared instead
+  // CHECK-FIXES: std::shared_ptr<Empty> PEmpty = std::make_shared<Empty>(Empty{});
+}
+
+void aliases() {
+  typedef std::shared_ptr<int> IntPtr;
+  IntPtr Typedef = IntPtr(new int);
+  // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: use std::make_shared instead
+  // CHECK-FIXES: IntPtr Typedef = std::make_shared<int>();
+
+  // We use 'bool' instead of '_Bool'.
+  typedef std::shared_ptr<bool> BoolPtr;
+  BoolPtr BoolType = BoolPtr(new bool);
+  // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: use std::make_shared instead
+  // CHECK-FIXES: BoolPtr BoolType = std::make_shared<bool>();
+
+  // We use 'Base' instead of 'struct Base'.
+  typedef std::shared_ptr<Base> BasePtr;
+  BasePtr StructType = BasePtr(new Base);
+// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: use std::make_shared instead
+// CHECK-FIXES: BasePtr StructType = std::make_shared<Base>();
+
+#define PTR shared_ptr<int>
+  std::shared_ptr<int> Macro = std::PTR(new int);
+// CHECK-MESSAGES: :[[@LINE-1]]:32: warning: use std::make_shared instead
+// CHECK-FIXES: std::shared_ptr<int> Macro = std::make_shared<int>();
+#undef PTR
+
+  std::shared_ptr<int> Using = shared_ptr_<int>(new int);
+  // CHECK-MESSAGES: :[[@LINE-1]]:32: warning: use std::make_shared instead
+  // CHECK-FIXES: std::shared_ptr<int> Using = std::make_shared<int>();
+}
+
+void whitespaces() {
+  // clang-format off
+  auto Space = std::shared_ptr <int>(new int());
+  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: use std::make_shared instead
+  // CHECK-FIXES: auto Space = std::make_shared<int>();
+
+  auto Spaces = std  ::    shared_ptr  <int>(new int());
+  // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: use std::make_shared instead
+  // CHECK-FIXES: auto Spaces = std::make_shared<int>();
+  // clang-format on
+}
+
+void nesting() {
+  auto Nest = std::shared_ptr<std::shared_ptr<int>>(new std::shared_ptr<int>(new int));
+  // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: use std::make_shared instead
+  // CHECK-FIXES: auto Nest = std::make_shared<std::shared_ptr<int>>(new int);
+  Nest.reset(new std::shared_ptr<int>(new int));
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use std::make_shared instead
+  // CHECK-FIXES: Nest = std::make_shared<std::shared_ptr<int>>(new int);
+  Nest->reset(new int);
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use std::make_shared instead
+  // CHECK-FIXES: *Nest = std::make_shared<int>();
+}
+
+void reset() {
+  std::shared_ptr<int> P;
+  P.reset();
+  P.reset(nullptr);
+  P.reset(new int());
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use std::make_shared instead
+  // CHECK-FIXES: P = std::make_shared<int>();
+
+  auto Q = &P;
+  Q->reset(new int());
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: use std::make_shared instead
+  // CHECK-FIXES: *Q = std::make_shared<int>();
+}
diff --git a/test/clang-tidy/modernize-make-unique-header.cpp b/test/clang-tidy/modernize-make-unique-header.cpp
new file mode 100644 (file)
index 0000000..e6c2a61
--- /dev/null
@@ -0,0 +1,17 @@
+// RUN: %check_clang_tidy %s modernize-make-unique %t -- \
+// RUN:   -config="{CheckOptions: \
+// RUN:     [{key: modernize-make-unique.MakeSmartPtrFunction, \
+// RUN:       value: 'my::MakeUnique'}, \
+// RUN:      {key: modernize-make-unique.MakeSmartPtrFunctionHeader, \
+// RUN:       value: 'make_unique_util.h'} \
+// RUN:     ]}" \
+// RUN:   -- -std=c++11 -I%S/Inputs/modernize-smart-ptr
+
+#include "unique_ptr.h"
+// CHECK-FIXES: #include "make_unique_util.h"
+
+void f() {
+  std::unique_ptr<int> P1 = std::unique_ptr<int>(new int());
+  // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: use my::MakeUnique instead
+  // CHECK-FIXES: std::unique_ptr<int> P1 = my::MakeUnique<int>();
+}
diff --git a/test/clang-tidy/modernize-make-unique.cpp b/test/clang-tidy/modernize-make-unique.cpp
new file mode 100644 (file)
index 0000000..551f38d
--- /dev/null
@@ -0,0 +1,338 @@
+// RUN: %check_clang_tidy %s modernize-make-unique %t -- -- -std=c++11 \
+// RUN:   -I%S/Inputs/modernize-smart-ptr
+
+#include "unique_ptr.h"
+// CHECK-FIXES: #include <memory>
+
+struct Base {
+  Base();
+  Base(int, int);
+};
+
+struct Derived : public Base {
+  Derived();
+  Derived(int, int);
+};
+
+struct APair {
+  int a, b;
+};
+
+struct DPair {
+  DPair() : a(0), b(0) {}
+  DPair(int x, int y) : a(y), b(x) {}
+  int a, b;
+};
+
+struct Empty {};
+
+namespace {
+class Foo {};
+} // namespace
+
+namespace bar {
+class Bar {};
+} // namespace bar
+
+template <class T>
+using unique_ptr_ = std::unique_ptr<T>;
+
+void *operator new(__SIZE_TYPE__ Count, void *Ptr);
+
+int g(std::unique_ptr<int> P);
+
+std::unique_ptr<Base> getPointer() {
+  return std::unique_ptr<Base>(new Base);
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use std::make_unique instead
+  // CHECK-FIXES: return std::make_unique<Base>();
+}
+
+void basic() {
+  std::unique_ptr<int> P1 = std::unique_ptr<int>(new int());
+  // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: use std::make_unique instead [modernize-make-unique]
+  // CHECK-FIXES: std::unique_ptr<int> P1 = std::make_unique<int>();
+
+  P1.reset(new int());
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: use std::make_unique instead [modernize-make-unique]
+  // CHECK-FIXES: P1 = std::make_unique<int>();
+
+  P1 = std::unique_ptr<int>(new int());
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use std::make_unique instead [modernize-make-unique]
+  // CHECK-FIXES: P1 = std::make_unique<int>();
+
+  // Without parenthesis.
+  std::unique_ptr<int> P2 = std::unique_ptr<int>(new int);
+  // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: use std::make_unique instead [modernize-make-unique]
+  // CHECK-FIXES: std::unique_ptr<int> P2 = std::make_unique<int>();
+
+  P2.reset(new int);
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: use std::make_unique instead [modernize-make-unique]
+  // CHECK-FIXES: P2 = std::make_unique<int>();
+
+  P2 = std::unique_ptr<int>(new int);
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use std::make_unique instead [modernize-make-unique]
+  // CHECK-FIXES: P2 = std::make_unique<int>();
+
+  // With auto.
+  auto P3 = std::unique_ptr<int>(new int());
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use std::make_unique instead
+  // CHECK-FIXES: auto P3 = std::make_unique<int>();
+
+  {
+    // No std.
+    using namespace std;
+    unique_ptr<int> Q = unique_ptr<int>(new int());
+    // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: use std::make_unique instead
+    // CHECK-FIXES: unique_ptr<int> Q = std::make_unique<int>();
+
+    Q = unique_ptr<int>(new int());
+    // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use std::make_unique instead
+    // CHECK-FIXES: Q = std::make_unique<int>();
+  }
+
+  std::unique_ptr<int> R(new int());
+
+  // Create the unique_ptr as a parameter to a function.
+  int T = g(std::unique_ptr<int>(new int()));
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use std::make_unique instead
+  // CHECK-FIXES: int T = g(std::make_unique<int>());
+
+  // Only replace if the type in the template is the same as the type returned
+  // by the new operator.
+  auto Pderived = std::unique_ptr<Base>(new Derived());
+
+  // OK to replace for reset and assign
+  Pderived.reset(new Derived());
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use std::make_unique instead
+  // CHECK-FIXES: Pderived = std::make_unique<Derived>();
+
+  Pderived = std::unique_ptr<Derived>(new Derived());
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: use std::make_unique instead
+  // CHECK-FIXES: Pderived = std::make_unique<Derived>();
+
+  // FIXME: OK to replace if assigned to unique_ptr<Base>
+  Pderived = std::unique_ptr<Base>(new Derived());
+
+  // FIXME: OK to replace when auto is not used
+  std::unique_ptr<Base> PBase = std::unique_ptr<Base>(new Derived());
+
+  // The pointer is returned by the function, nothing to do.
+  std::unique_ptr<Base> RetPtr = getPointer();
+
+  // This emulates std::move.
+  std::unique_ptr<int> Move = static_cast<std::unique_ptr<int> &&>(P1);
+
+  // Placement arguments should not be removed.
+  int *PInt = new int;
+  std::unique_ptr<int> Placement = std::unique_ptr<int>(new (PInt) int{3});
+  Placement.reset(new (PInt) int{3});
+  Placement = std::unique_ptr<int>(new (PInt) int{3});
+}
+
+// Calling make_smart_ptr from within a member function of a type with a
+// private or protected constructor would be ill-formed.
+class Private {
+private:
+  Private(int z) {}
+
+public:
+  Private() {}
+  void create() {
+    auto callsPublic = std::unique_ptr<Private>(new Private);
+    // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: use std::make_unique instead
+    // CHECK-FIXES: auto callsPublic = std::make_unique<Private>();
+    auto ptr = std::unique_ptr<Private>(new Private(42));
+    ptr.reset(new Private(42));
+    ptr = std::unique_ptr<Private>(new Private(42));
+  }
+
+  virtual ~Private();
+};
+
+class Protected {
+protected:
+  Protected() {}
+
+public:
+  Protected(int, int) {}
+  void create() {
+    auto callsPublic = std::unique_ptr<Protected>(new Protected(1, 2));
+    // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: use std::make_unique instead
+    // CHECK-FIXES: auto callsPublic = std::make_unique<Protected>(1, 2);
+    auto ptr = std::unique_ptr<Protected>(new Protected);
+    ptr.reset(new Protected);
+    ptr = std::unique_ptr<Protected>(new Protected);
+  }
+};
+
+void initialization(int T, Base b) {
+  // Test different kinds of initialization of the pointee.
+
+  // Direct initialization with parenthesis.
+  std::unique_ptr<DPair> PDir1 = std::unique_ptr<DPair>(new DPair(1, T));
+  // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: use std::make_unique instead
+  // CHECK-FIXES: std::unique_ptr<DPair> PDir1 = std::make_unique<DPair>(1, T);
+  PDir1.reset(new DPair(1, T));
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use std::make_unique instead
+  // CHECK-FIXES: PDir1 = std::make_unique<DPair>(1, T);
+
+  // Direct initialization with braces.
+  std::unique_ptr<DPair> PDir2 = std::unique_ptr<DPair>(new DPair{2, T});
+  // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: use std::make_unique instead
+  // CHECK-FIXES: std::unique_ptr<DPair> PDir2 = std::make_unique<DPair>(2, T);
+  PDir2.reset(new DPair{2, T});
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use std::make_unique instead
+  // CHECK-FIXES: PDir2 = std::make_unique<DPair>(2, T);
+
+  // Aggregate initialization.
+  std::unique_ptr<APair> PAggr = std::unique_ptr<APair>(new APair{T, 1});
+  // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: use std::make_unique instead
+  // CHECK-FIXES: std::unique_ptr<APair> PAggr = std::make_unique<APair>(APair{T, 1});
+  PAggr.reset(new APair{T, 1});
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use std::make_unique instead
+  // CHECK-FIXES: std::make_unique<APair>(APair{T, 1});
+
+  // Test different kinds of initialization of the pointee, when the unique_ptr
+  // is initialized with braces.
+
+  // Direct initialization with parenthesis.
+  std::unique_ptr<DPair> PDir3 = std::unique_ptr<DPair>{new DPair(3, T)};
+  // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: use std::make_unique instead
+  // CHECK-FIXES: std::unique_ptr<DPair> PDir3 = std::make_unique<DPair>(3, T);
+
+  // Direct initialization with braces.
+  std::unique_ptr<DPair> PDir4 = std::unique_ptr<DPair>{new DPair{4, T}};
+  // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: use std::make_unique instead
+  // CHECK-FIXES: std::unique_ptr<DPair> PDir4 = std::make_unique<DPair>(4, T);
+
+  // Aggregate initialization.
+  std::unique_ptr<APair> PAggr2 = std::unique_ptr<APair>{new APair{T, 2}};
+  // CHECK-MESSAGES: :[[@LINE-1]]:35: warning: use std::make_unique instead
+  // CHECK-FIXES: std::unique_ptr<APair> PAggr2 = std::make_unique<APair>(APair{T, 2});
+
+  // Direct initialization with parenthesis, without arguments.
+  std::unique_ptr<DPair> PDir5 = std::unique_ptr<DPair>(new DPair());
+  // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: use std::make_unique instead
+  // CHECK-FIXES: std::unique_ptr<DPair> PDir5 = std::make_unique<DPair>();
+
+  // Direct initialization with braces, without arguments.
+  std::unique_ptr<DPair> PDir6 = std::unique_ptr<DPair>(new DPair{});
+  // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: use std::make_unique instead
+  // CHECK-FIXES: std::unique_ptr<DPair> PDir6 = std::make_unique<DPair>();
+
+  // Aggregate initialization without arguments.
+  std::unique_ptr<Empty> PEmpty = std::unique_ptr<Empty>(new Empty{});
+  // CHECK-MESSAGES: :[[@LINE-1]]:35: warning: use std::make_unique instead
+  // CHECK-FIXES: std::unique_ptr<Empty> PEmpty = std::make_unique<Empty>(Empty{});
+
+  std::unique_ptr<Foo> FF = std::unique_ptr<Foo>(new Foo());
+  // CHECK-MESSAGES: :[[@LINE-1]]:29: warning:
+  // CHECK-FIXES: std::unique_ptr<Foo> FF = std::make_unique<Foo>();
+  FF.reset(new Foo());
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning:
+  // CHECK-FIXES: FF = std::make_unique<Foo>();
+
+  std::unique_ptr<bar::Bar> BB = std::unique_ptr<bar::Bar>(new bar::Bar());
+  // CHECK-MESSAGES: :[[@LINE-1]]:34: warning:
+  // CHECK-FIXES: std::unique_ptr<bar::Bar> BB = std::make_unique<bar::Bar>();
+  BB.reset(new bar::Bar());
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning:
+  // CHECK-FIXES: BB = std::make_unique<bar::Bar>();
+
+  std::unique_ptr<Foo[]> FFs;
+  FFs.reset(new Foo[5]);
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning:
+  // CHECK-FIXES: FFs = std::make_unique<Foo[]>(5);
+  FFs.reset(new Foo[5]());
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning:
+  // CHECK-FIXES: FFs = std::make_unique<Foo[]>(5);
+  const int Num = 1;
+  FFs.reset(new Foo[Num]);
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning:
+  // CHECK-FIXES: FFs = std::make_unique<Foo[]>(Num);
+  int Num2 = 1;
+  FFs.reset(new Foo[Num2]);
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning:
+  // CHECK-FIXES: FFs = std::make_unique<Foo[]>(Num2);
+
+  std::unique_ptr<int[]> FI;
+  FI.reset(new int[5]);
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning:
+  // CHECK-FIXES: FI = std::make_unique<int[]>(5);
+  FI.reset(new int[5]());
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning:
+  // CHECK-FIXES: FI = std::make_unique<int[]>(5);
+  FI.reset(new int[Num]);
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning:
+  // CHECK-FIXES: FI = std::make_unique<int[]>(Num);
+  FI.reset(new int[Num2]);
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning:
+  // CHECK-FIXES: FI = std::make_unique<int[]>(Num2);
+}
+
+void aliases() {
+  typedef std::unique_ptr<int> IntPtr;
+  IntPtr Typedef = IntPtr(new int);
+  // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: use std::make_unique instead
+  // CHECK-FIXES: IntPtr Typedef = std::make_unique<int>();
+
+  // We use 'bool' instead of '_Bool'.
+  typedef std::unique_ptr<bool> BoolPtr;
+  BoolPtr BoolType = BoolPtr(new bool);
+  // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: use std::make_unique instead
+  // CHECK-FIXES: BoolPtr BoolType = std::make_unique<bool>();
+
+  // We use 'Base' instead of 'struct Base'.
+  typedef std::unique_ptr<Base> BasePtr;
+  BasePtr StructType = BasePtr(new Base);
+// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: use std::make_unique instead
+// CHECK-FIXES: BasePtr StructType = std::make_unique<Base>();
+
+#define PTR unique_ptr<int>
+  std::unique_ptr<int> Macro = std::PTR(new int);
+// CHECK-MESSAGES: :[[@LINE-1]]:32: warning: use std::make_unique instead
+// CHECK-FIXES: std::unique_ptr<int> Macro = std::make_unique<int>();
+#undef PTR
+
+  std::unique_ptr<int> Using = unique_ptr_<int>(new int);
+  // CHECK-MESSAGES: :[[@LINE-1]]:32: warning: use std::make_unique instead
+  // CHECK-FIXES: std::unique_ptr<int> Using = std::make_unique<int>();
+}
+
+void whitespaces() {
+  // clang-format off
+  auto Space = std::unique_ptr <int>(new int());
+  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: use std::make_unique instead
+  // CHECK-FIXES: auto Space = std::make_unique<int>();
+
+  auto Spaces = std  ::    unique_ptr  <int>(new int());
+  // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: use std::make_unique instead
+  // CHECK-FIXES: auto Spaces = std::make_unique<int>();
+  // clang-format on
+}
+
+void nesting() {
+  auto Nest = std::unique_ptr<std::unique_ptr<int>>(new std::unique_ptr<int>(new int));
+  // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: use std::make_unique instead
+  // CHECK-FIXES: auto Nest = std::make_unique<std::unique_ptr<int>>(new int);
+  Nest.reset(new std::unique_ptr<int>(new int));
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use std::make_unique instead
+  // CHECK-FIXES: Nest = std::make_unique<std::unique_ptr<int>>(new int);
+  Nest->reset(new int);
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use std::make_unique instead
+  // CHECK-FIXES: *Nest = std::make_unique<int>();
+}
+
+void reset() {
+  std::unique_ptr<int> P;
+  P.reset();
+  P.reset(nullptr);
+  P.reset(new int());
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use std::make_unique instead
+  // CHECK-FIXES: P = std::make_unique<int>();
+
+  auto Q = &P;
+  Q->reset(new int());
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: use std::make_unique instead
+  // CHECK-FIXES: *Q = std::make_unique<int>();
+}
diff --git a/test/clang-tidy/modernize-pass-by-value-header.cpp b/test/clang-tidy/modernize-pass-by-value-header.cpp
new file mode 100644 (file)
index 0000000..95ad623
--- /dev/null
@@ -0,0 +1,8 @@
+// RUN: cp %S/Inputs/modernize-pass-by-value/header.h %T/pass-by-value-header.h
+// RUN: clang-tidy %s -checks='-*,modernize-pass-by-value' -header-filter='.*' -fix -- -std=c++11 -I %T | FileCheck %s -check-prefix=CHECK-MESSAGES -implicit-check-not="{{warning|error}}:"
+// RUN: FileCheck -input-file=%T/pass-by-value-header.h %s -check-prefix=CHECK-FIXES
+
+#include "pass-by-value-header.h"
+// CHECK-MESSAGES: :8:5: warning: pass by value and use std::move [modernize-pass-by-value]
+// CHECK-FIXES: #include <utility>
+// CHECK-FIXES: A(ThreadId tid) : threadid(std::move(tid)) {}
diff --git a/test/clang-tidy/modernize-pass-by-value-macro-header.cpp b/test/clang-tidy/modernize-pass-by-value-macro-header.cpp
new file mode 100644 (file)
index 0000000..543b7e4
--- /dev/null
@@ -0,0 +1,18 @@
+// RUN: %check_clang_tidy %s modernize-pass-by-value %t -- -- -std=c++11 -isystem %S/Inputs/Headers
+
+// CHECK-FIXES: #include <utility>
+
+#define HEADER <./a.h>
+#include HEADER
+
+struct A {
+  A(const A &) {}
+  A(A &&) {}
+};
+
+struct B {
+  B(const A &a) : a(a) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: pass by value and use std::move [modernize-pass-by-value]
+// CHECK-FIXES: B(A a) : a(std::move(a)) {}
+  A a;
+};
diff --git a/test/clang-tidy/modernize-pass-by-value-multi-fixes.cpp b/test/clang-tidy/modernize-pass-by-value-multi-fixes.cpp
new file mode 100644 (file)
index 0000000..7980c30
--- /dev/null
@@ -0,0 +1,12 @@
+// RUN: cat %S/Inputs/modernize-pass-by-value/header-with-fix.h > %T/pass-by-value-header-with-fix.h
+// RUN: sed -e 's#//.*$##' %s > %t.cpp
+// RUN: clang-tidy %t.cpp -checks='-*,modernize-pass-by-value' -header-filter='.*' -fix -- -std=c++11 -I %T | FileCheck %s -check-prefix=CHECK-MESSAGES -implicit-check-not="{{warning|error}}:"
+// RUN: FileCheck -input-file=%t.cpp %s -check-prefix=CHECK-FIXES
+// RUN: FileCheck -input-file=%T/pass-by-value-header-with-fix.h %s -check-prefix=CHECK-HEADER-FIXES
+
+#include "pass-by-value-header-with-fix.h"
+// CHECK-HEADER-FIXES: Foo(S s);
+Foo::Foo(const S &s) : s(s) {}
+// CHECK-MESSAGES: :9:10: warning: pass by value and use std::move [modernize-pass-by-value]
+// CHECK-FIXES: #include <utility>
+// CHECK-FIXES: Foo::Foo(S s) : s(std::move(s)) {}
diff --git a/test/clang-tidy/modernize-pass-by-value.cpp b/test/clang-tidy/modernize-pass-by-value.cpp
new file mode 100644 (file)
index 0000000..eb9a305
--- /dev/null
@@ -0,0 +1,215 @@
+// RUN: %check_clang_tidy %s modernize-pass-by-value %t -- -- -std=c++11 -fno-delayed-template-parsing
+
+namespace {
+// POD types are trivially move constructible.
+struct POD {
+  int a, b, c;
+};
+
+struct Movable {
+  int a, b, c;
+  Movable() = default;
+  Movable(const Movable &) {}
+  Movable(Movable &&) {}
+};
+
+struct NotMovable {
+  NotMovable() = default;
+  NotMovable(const NotMovable &) = default;
+  NotMovable(NotMovable &&) = delete;
+  int a, b, c;
+};
+}
+
+struct A {
+  A(const Movable &M) : M(M) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: pass by value and use std::move [modernize-pass-by-value]
+  // CHECK-FIXES: A(Movable M) : M(std::move(M)) {}
+  Movable M;
+};
+
+// Test that we aren't modifying other things than a parameter.
+Movable GlobalObj;
+struct B {
+  B(const Movable &M) : M(GlobalObj) {}
+  // CHECK-FIXES: B(const Movable &M) : M(GlobalObj) {}
+  Movable M;
+};
+
+// Test that a parameter with more than one reference to it won't be changed.
+struct C {
+  // Tests extra-reference in body.
+  C(const Movable &M) : M(M) { this->i = M.a; }
+  // CHECK-FIXES: C(const Movable &M) : M(M) { this->i = M.a; }
+
+  // Tests extra-reference in init-list.
+  C(const Movable &M, int) : M(M), i(M.a) {}
+  // CHECK-FIXES: C(const Movable &M, int) : M(M), i(M.a) {}
+  Movable M;
+  int i;
+};
+
+// Test that both declaration and definition are updated.
+struct D {
+  D(const Movable &M);
+  // CHECK-FIXES: D(Movable M);
+  Movable M;
+};
+D::D(const Movable &M) : M(M) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: pass by value and use std::move
+// CHECK-FIXES: D::D(Movable M) : M(std::move(M)) {}
+
+// Test with default parameter.
+struct E {
+  E(const Movable &M = Movable()) : M(M) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: pass by value and use std::move
+  // CHECK-FIXES: E(Movable M = Movable()) : M(std::move(M)) {}
+  Movable M;
+};
+
+// Test with object that can't be moved.
+struct F {
+  F(const NotMovable &NM) : NM(NM) {}
+  // CHECK-FIXES: F(const NotMovable &NM) : NM(NM) {}
+  NotMovable NM;
+};
+
+// Test unnamed parameter in declaration.
+struct G {
+  G(const Movable &);
+  // CHECK-FIXES: G(Movable );
+  Movable M;
+};
+G::G(const Movable &M) : M(M) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: pass by value and use std::move
+// CHECK-FIXES: G::G(Movable M) : M(std::move(M)) {}
+
+// Test parameter with and without qualifier.
+namespace ns_H {
+typedef ::Movable HMovable;
+}
+struct H {
+  H(const ns_H::HMovable &M);
+  // CHECK-FIXES: H(ns_H::HMovable M);
+  ns_H::HMovable M;
+};
+using namespace ns_H;
+H::H(const HMovable &M) : M(M) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: pass by value and use std::move
+// CHECK-FIXES: H(HMovable M) : M(std::move(M)) {}
+
+// Try messing up with macros.
+#define MOVABLE_PARAM(Name) const Movable & Name
+// CHECK-FIXES: #define MOVABLE_PARAM(Name) const Movable & Name
+struct I {
+  I(MOVABLE_PARAM(M)) : M(M) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: pass by value and use std::move
+  // CHECK-FIXES: I(MOVABLE_PARAM(M)) : M(M) {}
+  Movable M;
+};
+#undef MOVABLE_PARAM
+
+// Test that templates aren't modified.
+template <typename T> struct J {
+  J(const T &M) : M(M) {}
+  // CHECK-FIXES: J(const T &M) : M(M) {}
+  T M;
+};
+J<Movable> j1(Movable());
+J<NotMovable> j2(NotMovable());
+
+struct K_Movable {
+  K_Movable() = default;
+  K_Movable(const K_Movable &) = default;
+  K_Movable(K_Movable &&o) { dummy = o.dummy; }
+  int dummy;
+};
+
+// Test with movable type with an user defined move constructor.
+struct K {
+  K(const K_Movable &M) : M(M) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: pass by value and use std::move
+  // CHECK-FIXES: K(K_Movable M) : M(std::move(M)) {}
+  K_Movable M;
+};
+
+template <typename T> struct L {
+  L(const Movable &M) : M(M) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: pass by value and use std::move
+  // CHECK-FIXES: L(Movable M) : M(std::move(M)) {}
+  Movable M;
+};
+L<int> l(Movable());
+
+// Test with a non-instantiated template class.
+template <typename T> struct N {
+  N(const Movable &M) : M(M) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: pass by value and use std::move
+  // CHECK-FIXES: N(Movable M) : M(std::move(M)) {}
+
+  Movable M;
+  T A;
+};
+
+// Test with value parameter.
+struct O {
+  O(Movable M) : M(M) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: pass by value and use std::move
+  // CHECK-FIXES: O(Movable M) : M(std::move(M)) {}
+  Movable M;
+};
+
+// Test with a const-value parameter.
+struct P {
+  P(const Movable M) : M(M) {}
+  // CHECK-FIXES: P(const Movable M) : M(M) {}
+  Movable M;
+};
+
+// Test with multiples parameters where some need to be changed and some don't.
+// need to.
+struct Q {
+  Q(const Movable &A, const Movable &B, const Movable &C, double D)
+      : A(A), B(B), C(C), D(D) {}
+  // CHECK-MESSAGES: :[[@LINE-2]]:23: warning: pass by value and use std::move
+  // CHECK-MESSAGES: :[[@LINE-3]]:41: warning: pass by value and use std::move
+  // CHECK-FIXES:      Q(const Movable &A, Movable B, Movable C, double D)
+  // CHECK-FIXES:     : A(A), B(std::move(B)), C(std::move(C)), D(D) {}
+  const Movable &A;
+  Movable B;
+  Movable C;
+  double D;
+};
+
+// Test that value-parameters with a nested name specifier are left as-is.
+namespace ns_R {
+typedef ::Movable RMovable;
+}
+struct R {
+  R(ns_R::RMovable M) : M(M) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: pass by value and use std::move
+  // CHECK-FIXES: R(ns_R::RMovable M) : M(std::move(M)) {}
+  ns_R::RMovable M;
+};
+
+// Test with rvalue parameter.
+struct S {
+  S(Movable &&M) : M(M) {}
+  // CHECK-FIXES: S(Movable &&M) : M(M) {}
+  Movable M;
+};
+
+template <typename T, int N> struct array { T A[N]; };
+
+// Test that types that are trivially copyable will not use std::move. This will
+// cause problems with misc-move-const-arg, as it will revert it.
+struct T {
+  T(array<int, 10> a) : a_(a) {}
+  // CHECK-FIXES: T(array<int, 10> a) : a_(a) {}
+  array<int, 10> a_;
+};
+
+struct U {
+  U(const POD &M) : M(M) {}
+  POD M;
+};
diff --git a/test/clang-tidy/modernize-raw-string-literal-delimiter.cpp b/test/clang-tidy/modernize-raw-string-literal-delimiter.cpp
new file mode 100644 (file)
index 0000000..1dd3e13
--- /dev/null
@@ -0,0 +1,9 @@
+// RUN: %check_clang_tidy %s modernize-raw-string-literal %t -- -config='{CheckOptions: [{key: "modernize-raw-string-literal.DelimiterStem", value: "str"}, {key: modernize-raw-string-literal.ReplaceShorterLiterals, value: 1}]}' -- -std=c++11
+
+char const *const ContainsSentinel{"who\\ops)\""};
+// CHECK-MESSAGES: :[[@LINE-1]]:36: warning: {{.*}} can be written as a raw string literal
+// CHECK-FIXES: {{^}}char const *const ContainsSentinel{R"str(who\ops)")str"};{{$}}
+
+//char const *const ContainsDelim{"whoops)\")lit\""};
+// CHECK-XMESSAGES: :[[@LINE-1]]:33: warning: {{.*}} can be written as a raw string literal
+// CHECK-XFIXES: {{^}}char const *const ContainsDelim{R"lit1(whoops)")lit")lit1"};{{$}}
diff --git a/test/clang-tidy/modernize-raw-string-literal-replace-shorter.cpp b/test/clang-tidy/modernize-raw-string-literal-replace-shorter.cpp
new file mode 100644 (file)
index 0000000..673a8aa
--- /dev/null
@@ -0,0 +1,13 @@
+// RUN: %check_clang_tidy %s modernize-raw-string-literal %t
+
+// Don't replace these, because the raw literal would be longer.
+char const *const JustAQuote("quote:\'");
+char const *const NeedDelimiter("\":)\"");
+
+char const *const ManyQuotes("quotes:\'\'\'\'");
+// CHECK-MESSAGES: :[[@LINE-1]]:30: warning: {{.*}} can be written as a raw string literal
+// CHECK-FIXES: {{^}}char const *const ManyQuotes(R"(quotes:'''')");{{$}}
+
+char const *const LongOctal("\042\072\051\042");
+// CHECK-MESSAGES: :[[@LINE-1]]:29: warning: {{.*}} can be written as a raw string literal
+// CHECK-FIXES: {{^}}char const *const LongOctal(R"lit(":)")lit");{{$}}
diff --git a/test/clang-tidy/modernize-raw-string-literal.cpp b/test/clang-tidy/modernize-raw-string-literal.cpp
new file mode 100644 (file)
index 0000000..f104b09
--- /dev/null
@@ -0,0 +1,129 @@
+// RUN: %check_clang_tidy %s modernize-raw-string-literal %t -- -config="{CheckOptions: [{key: modernize-raw-string-literal.ReplaceShorterLiterals, value: 1}]}" -- -std=c++11
+
+char const *const BackSlash("goink\\frob");
+// CHECK-MESSAGES: :[[@LINE-1]]:29: warning: escaped string literal can be written as a raw string literal [modernize-raw-string-literal]
+// CHECK-FIXES: {{^}}char const *const BackSlash(R"(goink\frob)");{{$}}
+
+char const *const PlainLiteral("plain literal");
+
+// Non-printable ASCII characters.
+char const *const Nul("goink\\\000");
+char const *const Soh("goink\\\001");
+char const *const Stx("goink\\\002");
+char const *const Etx("goink\\\003");
+char const *const Enq("goink\\\004");
+char const *const Ack("goink\\\005");
+char const *const Bell("goink\\\afrob");
+char const *const BackSpace("goink\\\bfrob");
+char const *const HorizontalTab("goink\\\tfrob");
+char const *const NewLine("goink\nfrob");
+char const *const VerticalTab("goink\\\vfrob");
+char const *const FormFeed("goink\\\ffrob");
+char const *const CarraigeReturn("goink\\\rfrob");
+char const *const So("goink\\\016");
+char const *const Si("goink\\\017");
+char const *const Dle("goink\\\020");
+char const *const Dc1("goink\\\021");
+char const *const Dc2("goink\\\022");
+char const *const Dc3("goink\\\023");
+char const *const Dc4("goink\\\024");
+char const *const Nak("goink\\\025");
+char const *const Syn("goink\\\026");
+char const *const Etb("goink\\\027");
+char const *const Can("goink\\\030");
+char const *const Em("goink\\\031");
+char const *const Sub("goink\\\032");
+char const *const Esc("goink\\\033");
+char const *const Fs("goink\\\034");
+char const *const Gs("goink\\\035");
+char const *const Rs("goink\\\036");
+char const *const Us("goink\\\037");
+char const *const HexNonPrintable("\\\x03");
+char const *const Delete("\\\177");
+
+char const *const TrailingSpace("A line \\with space. \n");
+char const *const TrailingNewLine("A single \\line.\n");
+char const *const AlreadyRaw(R"(foobie\\bletch)");
+char const *const UTF8Literal(u8"foobie\\bletch");
+char const *const UTF8RawLiteral(u8R"(foobie\\bletch)");
+// TODO: enable these tests once all supported compilers
+// support char16_t and char32_t (VS2013 does not)
+// char16_t const *const UTF16Literal(u"foobie\\bletch");
+// char16_t const *const UTF16RawLiteral(uR"(foobie\\bletch)");
+// char32_t const *const UTF32Literal(U"foobie\\bletch");
+// char32_t const *const UTF32RawLiteral(UR"(foobie\\bletch)");
+wchar_t const *const WideLiteral(L"foobie\\bletch");
+wchar_t const *const WideRawLiteral(LR"(foobie\\bletch)");
+
+char const *const SingleQuote("goink\'frob");
+// CHECK-MESSAGES: :[[@LINE-1]]:31: warning: {{.*}} can be written as a raw string literal
+// CHECK-XFIXES: {{^}}char const *const SingleQuote(R"(goink'frob)");{{$}}
+
+char const *const DoubleQuote("goink\"frob");
+// CHECK-MESSAGES: :[[@LINE-1]]:31: warning: {{.*}} can be written as a raw string literal
+// CHECK-FIXES: {{^}}char const *const DoubleQuote(R"(goink"frob)");{{$}}
+
+char const *const QuestionMark("goink\?frob");
+// CHECK-MESSAGES: :[[@LINE-1]]:32: warning: {{.*}} can be written as a raw string literal
+// CHECK-FIXES: {{^}}char const *const QuestionMark(R"(goink?frob)");{{$}}
+
+char const *const RegEx("goink\\(one|two\\)\\\\\\?.*\\nfrob");
+// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: {{.*}} can be written as a raw string literal
+// CHECK-FIXES: {{^}}char const *const RegEx(R"(goink\(one|two\)\\\?.*\nfrob)");{{$}}
+
+char const *const Path("C:\\Program Files\\Vendor\\Application\\Application.exe");
+// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: {{.*}} can be written as a raw string literal
+// CHECK-FIXES: {{^}}char const *const Path(R"(C:\Program Files\Vendor\Application\Application.exe)");{{$}}
+
+char const *const ContainsSentinel("who\\ops)\"");
+// CHECK-MESSAGES: :[[@LINE-1]]:36: warning: {{.*}} can be written as a raw string literal
+// CHECK-FIXES: {{^}}char const *const ContainsSentinel(R"lit(who\ops)")lit");{{$}}
+
+char const *const ContainsDelim("whoops)\")lit\"");
+// CHECK-MESSAGES: :[[@LINE-1]]:33: warning: {{.*}} can be written as a raw string literal
+// CHECK-FIXES: {{^}}char const *const ContainsDelim(R"lit1(whoops)")lit")lit1");{{$}}
+
+char const *const OctalPrintable("\100\\");
+// CHECK-MESSAGES: :[[@LINE-1]]:34: warning: {{.*}} can be written as a raw string literal
+// CHECK-FIXES: {{^}}char const *const OctalPrintable(R"(@\)");{{$}}
+
+char const *const HexPrintable("\x40\\");
+// CHECK-MESSAGES: :[[@LINE-1]]:32: warning: {{.*}} can be written as a raw string literal
+// CHECK-FIXES: {{^}}char const *const HexPrintable(R"(@\)");{{$}}
+
+char const *const prettyFunction(__PRETTY_FUNCTION__);
+char const *const function(__FUNCTION__);
+char const *const func(__func__);
+
+#define TRICK(arg_) #arg_
+char const *const MacroBody = TRICK(foo\\bar);
+
+#define HAT(rabbit_) #rabbit_ "foo\\bar"
+char const *const StringizedMacroArgument = HAT(foo\\bar);
+
+#define SUBST(lit_) lit_
+char const *const MacroArgument = SUBST("foo\\bar");
+// FIXME: We should be able to replace this string literal macro argument
+
+template <typename T>
+void fn(char const *const Arg) {
+  char const *const Str("foo\\bar");
+  // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: {{.*}} can be written as a raw string literal
+  // CHECK-FIXES: {{^}}  char const *const Str(R"(foo\bar)");{{$}}
+}
+
+template <>
+void fn<int>(char const *const Arg) {
+  char const *const Str("foo\\bar");
+  // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: {{.*}} can be written as a raw string literal
+  // CHECK-FIXES: {{^}}  char const *const Str(R"(foo\bar)");{{$}}
+}
+
+void callFn() {
+  fn<int>("foo\\bar");
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: {{.*}} can be written as a raw string literal
+  // CHECK-FIXES: {{^}}  fn<int>(R"(foo\bar)");{{$}}
+  fn<double>("foo\\bar");
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: {{.*}} can be written as a raw string literal
+  // CHECK-FIXES: {{^}}  fn<double>(R"(foo\bar)");{{$}}
+}
diff --git a/test/clang-tidy/modernize-redundant-void-arg-delayed.cpp b/test/clang-tidy/modernize-redundant-void-arg-delayed.cpp
new file mode 100644 (file)
index 0000000..dd88775
--- /dev/null
@@ -0,0 +1,28 @@
+// RUN: %check_clang_tidy %s modernize-redundant-void-arg %t -- -- -fdelayed-template-parsing
+
+int foo(void) {
+// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: redundant void argument list in function definition [modernize-redundant-void-arg]
+// CHECK-FIXES: {{^}}int foo() {{{$}}
+    return 0;
+}
+
+template <class T>
+struct MyFoo {
+  int foo(void) {
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: redundant void argument list in function definition [modernize-redundant-void-arg]
+// CHECK-FIXES: {{^}}  int foo() {{{$}}
+    return 0;
+  }
+};
+// Explicit instantiation.
+template class MyFoo<int>;
+
+template <class T>
+struct MyBar {
+  // This declaration isn't instantiated and won't be parsed 'delayed-template-parsing'.
+  int foo(void) {
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: redundant void argument list in function definition [modernize-redundant-void-arg]
+// CHECK-FIXES: {{^}}  int foo() {{{$}}
+    return 0;
+  }
+};
diff --git a/test/clang-tidy/modernize-redundant-void-arg.c b/test/clang-tidy/modernize-redundant-void-arg.c
new file mode 100644 (file)
index 0000000..fdaf375
--- /dev/null
@@ -0,0 +1,58 @@
+// RUN: clang-tidy -checks=-*,modernize-redundant-void-arg %s -- -x c | count 0
+
+#define NULL 0
+
+extern int i;
+
+int foo2() {
+  return 0;
+}
+
+int j = 1;
+
+int foo(void) {
+  return 0;
+}
+
+typedef unsigned int my_uint;
+
+typedef void my_void;
+
+// A function taking void and returning a pointer to function taking void
+// and returning int.
+int (*returns_fn_void_int(void))(void);
+
+typedef int (*returns_fn_void_int_t(void))(void);
+
+int (*returns_fn_void_int(void))(void) {
+  return NULL;
+}
+
+// A function taking void and returning a pointer to a function taking void
+// and returning a pointer to a function taking void and returning void.
+void (*(*returns_fn_returns_fn_void_void(void))(void))(void);
+
+typedef void (*(*returns_fn_returns_fn_void_void_t(void))(void))(void);
+
+void (*(*returns_fn_returns_fn_void_void(void))(void))(void) {
+  return NULL;
+}
+
+void bar() {
+  int i;
+  int *pi = NULL;
+  void *pv = (void *) pi;
+  float f;
+  float *fi;
+  double d;
+  double *pd;
+}
+
+void (*f1)(void);
+void (*f2)(void) = NULL;
+void (*f3)(void) = bar;
+void (*fa)();
+void (*fb)() = NULL;
+void (*fc)() = bar;
+
+typedef void (function_ptr)(void);
diff --git a/test/clang-tidy/modernize-redundant-void-arg.cpp b/test/clang-tidy/modernize-redundant-void-arg.cpp
new file mode 100644 (file)
index 0000000..ea4b75f
--- /dev/null
@@ -0,0 +1,447 @@
+// RUN: %check_clang_tidy %s modernize-redundant-void-arg %t
+
+#define NULL 0
+
+int foo();
+
+void bar();
+
+void bar2();
+
+extern "C" void ecfoo(void);
+
+extern "C" void ecfoo(void) {
+}
+
+extern int i;
+
+int j = 1;
+
+int foo(void) {
+// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: redundant void argument list in function definition [modernize-redundant-void-arg]
+// CHECK-FIXES: {{^}}int foo() {{{$}}
+    return 0;
+}
+
+typedef unsigned int my_uint;
+
+typedef void my_void;
+
+// A function taking void and returning a pointer to function taking void
+// and returning int.
+int (*returns_fn_void_int(void))(void);
+// CHECK-MESSAGES: :[[@LINE-1]]:27: warning: {{.*}} in function declaration
+// CHECK-MESSAGES: :[[@LINE-2]]:34: warning: {{.*}} in function declaration
+// CHECK-FIXES: {{^}}int (*returns_fn_void_int())();{{$}}
+
+typedef int (*returns_fn_void_int_t(void))(void);
+// CHECK-MESSAGES: :[[@LINE-1]]:37: warning: {{.*}} in typedef
+// CHECK-MESSAGES: :[[@LINE-2]]:44: warning: {{.*}} in typedef
+// CHECK-FIXES: {{^}}typedef int (*returns_fn_void_int_t())();{{$}}
+
+// Should work for type aliases as well as typedef.
+using returns_fn_void_int_t2 = int (*(void))(void);
+// CHECK-MESSAGES: :[[@LINE-1]]:39: warning: {{.*}} in type alias
+// CHECK-MESSAGES: :[[@LINE-2]]:46: warning: {{.*}} in type alias
+// CHECK-FIXES: {{^}}using returns_fn_void_int_t2 = int (*())();{{$}}
+
+int (*returns_fn_void_int(void))(void) {
+// CHECK-MESSAGES: :[[@LINE-1]]:27: warning: {{.*}} in function definition
+// CHECK-MESSAGES: :[[@LINE-2]]:34: warning: {{.*}} in function definition
+// CHECK-FIXES: {{^}}int (*returns_fn_void_int())() {{{$}}
+  return nullptr;
+}
+
+// A function taking void and returning a pointer to a function taking void
+// and returning a pointer to a function taking void and returning void.
+void (*(*returns_fn_returns_fn_void_void(void))(void))(void);
+// CHECK-MESSAGES: :[[@LINE-1]]:42: warning: {{.*}} in function declaration
+// CHECK-MESSAGES: :[[@LINE-2]]:49: warning: {{.*}} in function declaration
+// CHECK-MESSAGES: :[[@LINE-3]]:56: warning: {{.*}} in function declaration
+// CHECK-FIXES: {{^}}void (*(*returns_fn_returns_fn_void_void())())();{{$}}
+
+typedef void (*(*returns_fn_returns_fn_void_void_t(void))(void))(void);
+// CHECK-MESSAGES: :[[@LINE-1]]:52: warning: {{.*}} in typedef
+// CHECK-MESSAGES: :[[@LINE-2]]:59: warning: {{.*}} in typedef
+// CHECK-MESSAGES: :[[@LINE-3]]:66: warning: {{.*}} in typedef
+// CHECK-FIXES: {{^}}typedef void (*(*returns_fn_returns_fn_void_void_t())())();{{$}}
+
+void (*(*returns_fn_returns_fn_void_void(void))(void))(void) {
+// CHECK-MESSAGES: :[[@LINE-1]]:42: warning: {{.*}} in function definition
+// CHECK-MESSAGES: :[[@LINE-2]]:49: warning: {{.*}} in function definition
+// CHECK-MESSAGES: :[[@LINE-3]]:56: warning: {{.*}} in function definition
+// CHECK-FIXES: {{^}}void (*(*returns_fn_returns_fn_void_void())())() {{{$}}
+    return nullptr;
+}
+
+void bar(void) {
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: {{.*}} in function definition
+// CHECK-FIXES: {{^}}void bar() {{{$}}
+}
+
+void op_fn(int i) {
+}
+
+class gronk {
+public:
+  gronk();
+  ~gronk();
+
+    void foo();
+    void bar();
+    void bar2
+        ();
+    void operation(int i) { }
+
+private:
+    int m_i;
+    int *m_pi;
+    float m_f;
+    float *m_pf;
+    double m_d;
+    double *m_pd;
+
+    void (*f1)(void);
+    // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: {{.*}} in field declaration
+    // CHECK-FIXES: {{^    }}void (*f1)();{{$}}
+
+  void (*op)(int i);
+
+  void (gronk::*p1)(void);
+  // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: {{.*}} in field declaration
+  // CHECK-FIXES: {{^  }}void (gronk::*p1)();{{$}}
+
+  int (gronk::*p_mi);
+
+  void (gronk::*p2)(int);
+
+  void (*(*returns_fn_returns_fn_void_void(void))(void))(void);
+  // CHECK-MESSAGES: :[[@LINE-1]]:44: warning: {{.*}} in function declaration
+  // CHECK-MESSAGES: :[[@LINE-2]]:51: warning: {{.*}} in function declaration
+  // CHECK-MESSAGES: :[[@LINE-3]]:58: warning: {{.*}} in function declaration
+  // CHECK-FIXES: {{^}}  void (*(*returns_fn_returns_fn_void_void())())();{{$}}
+
+  void (*(*(gronk::*returns_fn_returns_fn_void_void_mem)(void))(void))(void);
+  // CHECK-MESSAGES: :[[@LINE-1]]:58: warning: {{.*}} in field declaration
+  // CHECK-MESSAGES: :[[@LINE-2]]:65: warning: {{.*}} in field declaration
+  // CHECK-MESSAGES: :[[@LINE-3]]:72: warning: {{.*}} in field declaration
+  // CHECK-FIXES: {{^}}  void (*(*(gronk::*returns_fn_returns_fn_void_void_mem)())())();{{$}}
+};
+
+int i;
+int *pi;
+void *pv = (void *) pi;
+float f;
+float *fi;
+double d;
+double *pd;
+
+void (*f1)(void);
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: {{.*}} in variable declaration
+// CHECK-FIXES: {{^}}void (*f1)();{{$}}
+
+void (*f2)(void) = nullptr;
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: {{.*}} in variable declaration with initializer
+// CHECK-FIXES: {{^}}void (*f2)() = nullptr;{{$}}
+
+void (*f2b)(void)(nullptr);
+// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: {{.*}} in variable declaration with initializer
+// CHECK-FIXES: {{^}}void (*f2b)()(nullptr);{{$}}
+
+void (*f2c)(void){nullptr};
+// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: {{.*}} in variable declaration with initializer
+// CHECK-FIXES: {{^}}void (*f2c)(){nullptr};{{$}}
+
+void (*f2d)(void) = NULL;
+// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: {{.*}} in variable declaration with initializer
+// CHECK-FIXES: {{^}}void (*f2d)() = NULL;{{$}}
+
+void (*f2e)(void)(NULL);
+// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: {{.*}} in variable declaration with initializer
+// CHECK-FIXES: {{^}}void (*f2e)()(NULL);{{$}}
+
+void (*f2f)(void){NULL};
+// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: {{.*}} in variable declaration with initializer
+// CHECK-FIXES: {{^}}void (*f2f)(){NULL};{{$}}
+
+void (*f3)(void) = bar;
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: {{.*}} in variable declaration with initializer
+// CHECK-FIXES: {{^}}void (*f3)() = bar;{{$}}
+
+void (*o1)(int i);
+void (*o2)(int i) = nullptr;
+void (*o3)(int i)(nullptr);
+void (*o4)(int i){nullptr};
+void (*o5)(int i) = NULL;
+void (*o6)(int i)(NULL);
+void (*o7)(int i){NULL};
+void (*o8)(int i) = op_fn;
+
+void (*fa)();
+
+void (*fb)() = nullptr;
+
+void (*fc)() = bar;
+
+typedef void (function_ptr)(void);
+// CHECK-MESSAGES: :[[@LINE-1]]:29: warning: {{.*}} in typedef
+// CHECK-FIXES: {{^}}typedef void (function_ptr)();{{$}}
+
+// intentionally not LLVM style to check preservation of whitesapce
+typedef void (function_ptr2)
+    (
+        void
+    );
+// CHECK-MESSAGES: :[[@LINE-2]]:9: warning: {{.*}} in typedef
+// CHECK-FIXES:      {{^typedef void \(function_ptr2\)$}}
+// CHECK-FIXES-NEXT: {{^    \($}}
+// CHECK-FIXES-NEXT: {{^        $}}
+// CHECK-FIXES-NEXT: {{^    \);$}}
+
+// intentionally not LLVM style to check preservation of whitesapce
+typedef
+void
+(
+*
+(
+*
+returns_fn_returns_fn_void_void_t2
+(
+void
+)
+)
+(
+void
+)
+)
+(
+void
+)
+;
+// CHECK-MESSAGES: :[[@LINE-11]]:1: warning: {{.*}} in typedef
+// CHECK-MESSAGES: :[[@LINE-8]]:1: warning: {{.*}} in typedef
+// CHECK-MESSAGES: :[[@LINE-5]]:1: warning: {{.*}} in typedef
+// CHECK-FIXES:      {{^typedef$}}
+// CHECK-FIXES-NEXT: {{^void$}}
+// CHECK-FIXES-NEXT: {{^\($}}
+// CHECK-FIXES-NEXT: {{^\*$}}
+// CHECK-FIXES-NEXT: {{^\($}}
+// CHECK-FIXES-NEXT: {{^\*$}}
+// CHECK-FIXES-NEXT: {{^returns_fn_returns_fn_void_void_t2$}}
+// CHECK-FIXES-NEXT: {{^\($}}
+// CHECK-FIXES-NOT:  {{[^ ]}}
+// CHECK-FIXES:      {{^\)$}}
+// CHECK-FIXES-NEXT: {{^\)$}}
+// CHECK-FIXES-NEXT: {{^\($}}
+// CHECK-FIXES-NOT:  {{[^ ]}}
+// CHECK-FIXES:      {{^\)$}}
+// CHECK-FIXES-NEXT: {{^\)$}}
+// CHECK-FIXES-NEXT: {{^\($}}
+// CHECK-FIXES-NOT:  {{[^ ]}}
+// CHECK-FIXES:      {{^\)$}}
+// CHECK-FIXES-NEXT: {{^;$}}
+
+
+void (gronk::*p1)(void);
+// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: {{.*}} in variable declaration
+// CHECK-FIXES: {{^}}void (gronk::*p1)();{{$}}
+
+void (gronk::*p2)(void) = &gronk::foo;
+// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: {{.*}} in variable declaration with initializer
+// CHECK-FIXES: {{^}}void (gronk::*p2)() = &gronk::foo;{{$}}
+
+typedef void (gronk::*member_function_ptr)(void);
+// CHECK-MESSAGES: :[[@LINE-1]]:44: warning: {{.*}} in typedef
+// CHECK-FIXES: {{^}}typedef void (gronk::*member_function_ptr)();{{$}}
+
+// intentionally not LLVM style to check preservation of whitesapce
+typedef void (gronk::*member_function_ptr2)
+    (
+        void
+    );
+// CHECK-MESSAGES: :[[@LINE-2]]:9: warning: {{.*}} in typedef
+// CHECK-FIXES:      {{^typedef void \(gronk::\*member_function_ptr2\)$}}
+// CHECK-FIXES-NEXT: {{^    \($}}
+// CHECK-FIXES-NEXT: {{^        $}}
+// CHECK-FIXES-NEXT: {{^    \);$}}
+
+void gronk::foo() {
+  void (*f1)(void) = &::bar;
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: {{.*}} in variable declaration with initializer
+  // CHECK-FIXES: {{^  }}void (*f1)() = &::bar;{{$}}
+
+  void (*f2)(void);
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: {{.*}} in variable declaration
+  // CHECK-FIXES: {{^  }}void (*f2)();{{$}}
+
+  // intentionally not LLVM style to check preservation of whitesapce
+  void (*f3)
+      (
+          void
+      );
+  // CHECK-MESSAGES: :[[@LINE-2]]:11: warning: {{.*}} in variable declaration
+  // CHECK-FIXES:      {{^  }}void (*f3){{$}}
+  // CHECK-FIXES-NEXT: {{^      \($}}
+  // CHECK-FIXES-NEXT: {{^          $}}
+  // CHECK-FIXES-NEXT: {{^      \);$}}
+}
+
+void gronk::bar(void) {
+// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: {{.*}} in function definition
+// CHECK-FIXES: {{^}}void gronk::bar() {{{$}}
+  void (gronk::*p3)(void) = &gronk::foo;
+  // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: {{.*}} in variable declaration with initializer
+  // CHECK-FIXES: {{^  }}void (gronk::*p3)() = &gronk::foo;{{$}}
+
+  void (gronk::*p4)(void);
+  // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: {{.*}} in variable declaration
+  // CHECK-FIXES: {{^  }}void (gronk::*p4)();{{$}}
+
+  // intentionally not LLVM style to check preservation of whitesapce
+  void (gronk::*p5)
+      (
+          void
+      );
+  // CHECK-MESSAGES: :[[@LINE-2]]:11: warning: {{.*}} in variable declaration
+  // CHECK-FIXES:      {{^  }}void (gronk::*p5){{$}}
+  // CHECK-FIXES-NEXT: {{^      \($}}
+  // CHECK-FIXES-NExT: {{^          $}}
+  // CHECK-FIXES-NExT: {{^      \);$}}
+}
+
+// intentionally not LLVM style to check preservation of whitesapce
+void gronk::bar2
+  (
+  void
+  )
+// CHECK-MESSAGES: :[[@LINE-2]]:3: warning: {{.*}} in function definition
+// CHECK-FIXES:      {{^void gronk::bar2$}}
+// CHECK-FIXES-NEXT: {{^  \($}}
+// CHECK-FIXES-NEXT: {{^  $}}
+// CHECK-FIXES-NEXT: {{^  \)$}}
+{
+}
+
+gronk::gronk(void)
+// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: {{.*}} in function definition
+// CHECK-FIXES: {{^}}gronk::gronk(){{$}}
+  : f1(nullptr),
+  p1(nullptr) {
+}
+
+gronk::~gronk(void) {
+// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: {{.*}} in function definition
+// CHECK-FIXES: {{^}}gronk::~gronk() {{{$}}
+}
+
+class nutter {
+public:
+  nutter();
+};
+
+nutter::nutter(void) {
+// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: {{.*}} in function definition
+// CHECK-FIXES: {{^}}nutter::nutter() {{{$}}
+  void (*f3)(void) = static_cast<void (*)(void)>(0);
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: {{.*}} in variable declaration with initializer
+  // CHECK-MESSAGES: :[[@LINE-2]]:43: warning: {{.*}} in named cast
+  // CHECK-FIXES: void (*f3)() = static_cast<void (*)()>(0);{{$}}
+
+  void (*f4)(void) = (void (*)(void)) 0;
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: {{.*}} in variable declaration with initializer
+  // CHECK-MESSAGES: :[[@LINE-2]]:32: warning: {{.*}} in cast expression
+  // CHECK-FIXES: void (*f4)() = (void (*)()) 0;{{$}}
+
+  void (*f5)(void) = reinterpret_cast<void (*)(void)>(0);
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: {{.*}} in variable declaration with initializer
+  // CHECK-MESSAGES: :[[@LINE-2]]:48: warning: {{.*}} in named cast
+  // CHECK-FIXES: void (*f5)() = reinterpret_cast<void (*)()>(0);{{$}}
+
+  // intentionally not LLVM style to check preservation of whitesapce
+  void (*f6)(void) = static_cast<void (*)
+      (
+          void
+      )>(0);
+  // CHECK-MESSAGES: :[[@LINE-4]]:14: warning: {{.*}} in variable declaration with initializer
+  // CHECK-MESSAGES: :[[@LINE-3]]:11: warning: {{.*}} in named cast
+  // CHECK-FIXES:      {{^  }}void (*f6)() = static_cast<void (*){{$}}
+  // CHECK-FIXES-NEXT: {{^      \($}}
+  // CHECK-FIXES-NEXT: {{^          $}}
+  // CHECK-FIXES-NEXT: {{^      }})>(0);{{$}}
+
+  // intentionally not LLVM style to check preservation of whitesapce
+  void (*f7)(void) = (void (*)
+      (
+          void
+      )) 0;
+  // CHECK-MESSAGES: :[[@LINE-4]]:14: warning: {{.*}} in variable declaration with initializer
+  // CHECK-MESSAGES: :[[@LINE-3]]:11: warning: {{.*}} in cast expression
+  // CHECK-FIXES:      {{^  }}void (*f7)() = (void (*){{$}}
+  // CHECK-FIXES-NEXT: {{^      \($}}
+  // CHECK-FIXES-NEXT: {{^          $}}
+  // CHECK-FIXES-NEXT: {{^      \)\) 0;$}}
+
+  // intentionally not LLVM style to check preservation of whitesapce
+  void (*f8)(void) = reinterpret_cast<void (*)
+      (
+          void
+      )>(0);
+  // CHECK-MESSAGES: :[[@LINE-4]]:14: warning: {{.*}} in variable declaration with initializer
+  // CHECK-MESSAGES: :[[@LINE-3]]:11: warning: {{.*}} in named cast
+  // CHECK-FIXES:      {{^  }}void (*f8)() = reinterpret_cast<void (*){{$}}
+  // CHECK-FIXES-NEXT: {{^      \($}}
+  // CHECK-FIXES-NEXT: {{^          $}}
+  // CHECK-FIXES-NEXT: {{^      \)>\(0\);$}}
+
+  void (*o1)(int) = static_cast<void (*)(int)>(0);
+  void (*o2)(int) = (void (*)(int)) 0;
+  void (*o3)(int) = reinterpret_cast<void (*)(int)>(0);
+}
+
+class generator {
+public:
+  int operator()(void) { return 1; }
+  // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: {{.*}} in function definition
+  // CHECK-FIXES: {{^  }}int operator()() { return 1; }{{$}}
+};
+
+void test_lambda_functions() {
+  auto lamb_duh = [](void (*fn)(void)) { (*fn)(); };
+  // CHECK-MESSAGES: :[[@LINE-1]]:33: warning: {{.*}} in variable declaration
+  // CHECK-FIXES: {{^  }}auto lamb_duh = [](void (*fn)()) { (*fn)(); };{{$}}
+
+  auto lambda_generator = [](void) { return 1; };
+  // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: {{.*}} in lambda expression
+  // CHECK-FIXES: {{^  }}auto lambda_generator = []() { return 1; };{{$}}
+
+  auto gen2 = []() { return 1; };
+
+  auto gen3 = []{ return 1; };
+
+  auto void_returner = [](void) -> void (*)(void) { return f1; };
+  // CHECK-MESSAGES: [[@LINE-1]]:27: warning: {{.*}} in lambda expression
+  // CHECK-MESSAGES: [[@LINE-2]]:45: warning: {{.*}} in lambda expression
+  // CHECK-FIXES: {{^  }}auto void_returner = []() -> void (*)() { return f1; };{{$}}
+}
+
+#define M(x) x
+
+M(void inmacro(void) {})
+// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: {{.*}} in function definition
+// CHECK-FIXES: M(void inmacro() {})
+
+#define F(A, B)        \
+  struct F_##A##_##B { \
+    F_##A##_##B(void); \
+  };                   \
+  F_##A##_##B::F_##A##_##B(void)
+
+F(Foo, Bar) {
+
+}
+
+struct DefinitionWithNoBody {
+  DefinitionWithNoBody(void) = delete;
+  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: {{.*}} in function definition
+  // CHECK-FIXES: DefinitionWithNoBody() = delete;
+};
diff --git a/test/clang-tidy/modernize-replace-auto-ptr.cpp b/test/clang-tidy/modernize-replace-auto-ptr.cpp
new file mode 100644 (file)
index 0000000..13405b2
--- /dev/null
@@ -0,0 +1,304 @@
+// RUN: %check_clang_tidy %s modernize-replace-auto-ptr %t -- -- \
+// RUN:   -std=c++11 -I %S/Inputs/modernize-replace-auto-ptr
+
+// CHECK-FIXES: #include <utility>
+
+#include "memory.h"
+
+// Instrumentation for auto_ptr_ref test.
+struct Base {};
+struct Derived : Base {};
+std::auto_ptr<Derived> create_derived_ptr();
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: auto_ptr is deprecated, use unique_ptr instead [modernize-replace-auto-ptr]
+// CHECK-FIXES: std::unique_ptr<Derived> create_derived_ptr();
+
+
+// Test function return values (declaration)
+std::auto_ptr<char> f_5();
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: auto_ptr is deprecated
+// CHECK-FIXES: std::unique_ptr<char> f_5()
+
+
+// Test function parameters.
+void f_6(std::auto_ptr<int>);
+// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: auto_ptr is deprecated
+// CHECK-FIXES: void f_6(std::unique_ptr<int>);
+void f_7(const std::auto_ptr<int> &);
+// CHECK-MESSAGES: :[[@LINE-1]]:21: warning: auto_ptr is deprecated
+// CHECK-FIXES: void f_7(const std::unique_ptr<int> &);
+
+
+// Test on record type fields.
+struct A {
+  std::auto_ptr<int> field;
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: auto_ptr is deprecated
+  // CHECK-FIXES: std::unique_ptr<int> field;
+
+  typedef std::auto_ptr<int> int_ptr_type;
+  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: auto_ptr is deprecated
+  // CHECK-FIXES: typedef std::unique_ptr<int> int_ptr_type;
+};
+
+
+// FIXME: Test template WITH instantiation.
+template <typename T> struct B {
+  typedef typename std::auto_ptr<T> created_type;
+  // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: auto_ptr is deprecated
+  // CHECK-FIXES: typedef typename std::unique_ptr<T> created_type;
+
+  created_type create() { return std::auto_ptr<T>(new T()); }
+  // CHECK-MESSAGES: :[[@LINE-1]]:39: warning: auto_ptr is deprecated
+  // CHECK-FIXES: created_type create() { return std::unique_ptr<T>(new T()); }
+};
+
+
+// Test 'using' in a namespace (declaration)
+namespace ns_1 {
+// Test multiple using declarations.
+  using std::auto_ptr;
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: auto_ptr is deprecated
+  // CHECK-FIXES: using std::unique_ptr;
+  using std::auto_ptr;
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: auto_ptr is deprecated
+  // CHECK-FIXES: using std::unique_ptr;
+}
+
+
+namespace ns_2 {
+template <typename T> struct auto_ptr {};
+}
+
+void f_1() {
+  std::auto_ptr<int> a;
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: auto_ptr is deprecated
+  // CHECK-FIXES: std::unique_ptr<int> a;
+
+  // Check that spaces aren't modified unnecessarily.
+  std:: auto_ptr <int> b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: auto_ptr is deprecated
+  // CHECK-FIXES: std:: unique_ptr <int> b;
+  std :: auto_ptr < char > c(new char());
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: auto_ptr is deprecated
+  // CHECK-FIXES: std :: unique_ptr < char > c(new char());
+
+  // Test construction from a temporary.
+  std::auto_ptr<char> d = std::auto_ptr<char>();
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: auto_ptr is deprecated
+  // CHECK-MESSAGES: :[[@LINE-2]]:32: warning: auto_ptr is deprecated
+  // CHECK-FIXES: std::unique_ptr<char> d = std::unique_ptr<char>();
+
+  typedef std::auto_ptr<int> int_ptr_t;
+  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: auto_ptr is deprecated
+  // CHECK-FIXES: typedef std::unique_ptr<int> int_ptr_t;
+  int_ptr_t e(new int());
+
+  // Test pointers.
+  std::auto_ptr<int> *f;
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: auto_ptr is deprecated
+  // CHECK-FIXES: std::unique_ptr<int> *f;
+
+  // Test 'static' declarations.
+  static std::auto_ptr<int> g;
+  // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: auto_ptr is deprecated
+  // CHECK-FIXES: static std::unique_ptr<int> g;
+
+  // Test with cv-qualifiers.
+  const std::auto_ptr<int> h;
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: auto_ptr is deprecated
+  // CHECK-FIXES: const std::unique_ptr<int> h;
+  volatile std::auto_ptr<int> i;
+  // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: auto_ptr is deprecated
+  // CHECK-FIXES: volatile std::unique_ptr<int> i;
+  const volatile std::auto_ptr<int> j;
+  // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: auto_ptr is deprecated
+  // CHECK-FIXES: const volatile std::unique_ptr<int> j;
+
+  // Test auto and initializer-list.
+  auto k = std::auto_ptr<int>{};
+  // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: auto_ptr is deprecated
+  // CHECK-FIXES: auto k = std::unique_ptr<int>{};
+  std::auto_ptr<int> l{std::auto_ptr<int>()};
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: auto_ptr is deprecated
+  // CHECK-MESSAGES: :[[@LINE-2]]:29: warning: auto_ptr is deprecated
+  // CHECK-FIXES: std::unique_ptr<int> l{std::unique_ptr<int>()};
+
+  // Test interlocked auto_ptr.
+  std::auto_ptr<std::auto_ptr<int> > m;
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: auto_ptr is deprecated
+  // CHECK-MESSAGES: :[[@LINE-2]]:22: warning: auto_ptr is deprecated
+  // CHECK-FIXES: std::unique_ptr<std::unique_ptr<int> > m;
+
+  // Test temporaries.
+  std::auto_ptr<char>();
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: auto_ptr is deprecated
+  // CHECK-FIXES: std::unique_ptr<char>();
+
+  // Test void-specialization.
+  std::auto_ptr<void> n;
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: auto_ptr is deprecated
+  // CHECK-FIXES: std::unique_ptr<void> n;
+
+  // Test template WITH instantiation (instantiation).
+  B<double> o;
+  std::auto_ptr<double> p(o.create());
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: auto_ptr is deprecated
+  // CHECK-FIXES: std::unique_ptr<double> p(o.create());
+
+  // Test 'using' in a namespace ("definition").
+  ns_1::auto_ptr<int> q;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: auto_ptr is deprecated
+  // CHECK-FIXES: ns_1::unique_ptr<int> q;
+
+  // Test construction with an 'auto_ptr_ref'.
+  std::auto_ptr<Base> r(create_derived_ptr());
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: auto_ptr is deprecated
+  // CHECK-FIXES: std::unique_ptr<Base> r(create_derived_ptr());
+}
+
+// Test without the nested name specifiers.
+void f_2() {
+  using namespace std;
+
+  auto_ptr<int> a;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: auto_ptr is deprecated
+  // CHECK-FIXES: unique_ptr<int> a;
+}
+
+// Test using declaration.
+void f_3() {
+  using std::auto_ptr;
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: auto_ptr is deprecated
+  // CHECK-FIXES: using std::unique_ptr;
+
+  auto_ptr<int> a;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: auto_ptr is deprecated
+  // CHECK-FIXES: unique_ptr<int> a;
+}
+
+// Test messing-up with macros.
+void f_4() {
+#define MACRO_1 <char>
+  std::auto_ptr MACRO_1 p(new char());
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: auto_ptr is deprecated
+  // CHECK-FIXES: std::unique_ptr MACRO_1 p(new char());
+#define MACRO_2 auto_ptr
+  // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: auto_ptr is deprecated
+  // CHECK-FIXES: #define MACRO_2 unique_ptr
+  std::MACRO_2<int> q;
+#define MACRO_3(Type) std::auto_ptr<Type>
+  // CHECK-MESSAGES: :[[@LINE-1]]:28: warning: auto_ptr is deprecated
+  // CHECK-FIXES: #define MACRO_3(Type) std::unique_ptr<Type>
+  MACRO_3(float)r(new float());
+#define MACRO_4 std::auto_ptr
+  // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: auto_ptr is deprecated
+  // CHECK-FIXES: #define MACRO_4 std::unique_ptr
+  using MACRO_4;
+#undef MACRO_1
+#undef MACRO_2
+#undef MACRO_3
+#undef MACRO_4
+}
+
+// Test function return values (definition).
+std::auto_ptr<char> f_5()
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: auto_ptr is deprecated
+  // CHECK-FIXES: std::unique_ptr<char> f_5()
+{
+  // Test constructor.
+  return std::auto_ptr<char>(new char());
+  // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: auto_ptr is deprecated
+  // CHECK-FIXES: return std::unique_ptr<char>(new char());
+}
+
+// Test that non-std auto_ptr aren't replaced.
+void f_8() {
+  ns_2::auto_ptr<char> a;
+  using namespace ns_2;
+  auto_ptr<int> b;
+}
+
+// Fail to modify when the template is never instantiated.
+//
+// This might not be an issue. If it's never used it doesn't really matter if
+// it's changed or not. If it's a header and one of the source use it, then it
+// will still be changed.
+template <typename X>
+void f() {
+  std::auto_ptr<X> p;
+}
+
+// FIXME: Alias template could be replaced if a matcher existed.
+namespace std {
+template <typename T> using aaaaaaaa = auto_ptr<T>;
+}
+
+// We want to avoid replacing 'aaaaaaaa' by unique_ptr here. It's better to
+// change the type alias directly.
+std::aaaaaaaa<int> d;
+
+
+void takes_ownership_fn(std::auto_ptr<int> x);
+// CHECK-MESSAGES: :[[@LINE-1]]:30: warning: auto_ptr is deprecated
+// CHECK-FIXES: void takes_ownership_fn(std::unique_ptr<int> x);
+
+std::auto_ptr<int> get_by_value();
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: auto_ptr is deprecated
+// CHECK-FIXES: std::unique_ptr<int> get_by_value();
+
+class Wrapper {
+ public:
+  std::auto_ptr<int> &get_wrapped();
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: auto_ptr is deprecated
+
+ private:
+  std::auto_ptr<int> wrapped;
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: auto_ptr is deprecated
+};
+
+void f() {
+  std::auto_ptr<int> a, b, c;
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: auto_ptr is deprecated
+  // CHECK-FIXES: std::unique_ptr<int> a, b, c;
+  Wrapper wrapper_a, wrapper_b;
+
+  a = b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use std::move to transfer ownership
+  // CHECK-FIXES: a = std::move(b);
+
+  wrapper_a.get_wrapped() = wrapper_b.get_wrapped();
+  // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: use std::move to transfer ownership
+  // CHECK-FIXES: wrapper_a.get_wrapped() = std::move(wrapper_b.get_wrapped());
+
+  // Test that 'std::move()' is inserted when call to the
+  // copy-constructor are made.
+  takes_ownership_fn(c);
+  // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: use std::move to transfer ownership
+  // CHECK-FIXES: takes_ownership_fn(std::move(c));
+  takes_ownership_fn(wrapper_a.get_wrapped());
+  // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: use std::move to transfer ownership
+  // CHECK-FIXES: takes_ownership_fn(std::move(wrapper_a.get_wrapped()));
+
+  std::auto_ptr<int> d[] = { std::auto_ptr<int>(new int(1)),
+                             std::auto_ptr<int>(new int(2)) };
+  // CHECK-MESSAGES: :[[@LINE-2]]:8: warning: auto_ptr is deprecated
+  // CHECK-MESSAGES: :[[@LINE-3]]:35: warning: auto_ptr is deprecated
+  // CHECK-MESSAGES: :[[@LINE-3]]:35: warning: auto_ptr is deprecated
+  // CHECK-FIXES: std::unique_ptr<int> d[] = { std::unique_ptr<int>(new int(1)),
+  // CHECK-FIXES-NEXT:                         std::unique_ptr<int>(new int(2)) };
+  std::auto_ptr<int> e = d[0];
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: auto_ptr is deprecated
+  // CHECK-MESSAGES: :[[@LINE-2]]:26: warning: use std::move to transfer ownership
+  // CHECK: std::unique_ptr<int> e = std::move(d[0]);
+
+  // Test that std::move() is not used when assigning an rvalue
+  std::auto_ptr<int> f;
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: auto_ptr is deprecated
+  // CHECK-FIXES: std::unique_ptr<int> f;
+  f = std::auto_ptr<int>(new int(0));
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: auto_ptr is deprecated
+  // CHECK-NEXT: f = std::unique_ptr<int>(new int(0));
+
+  std::auto_ptr<int> g = get_by_value();
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: auto_ptr is deprecated
+  // CHECK-FIXES: std::unique_ptr<int> g = get_by_value();
+}
diff --git a/test/clang-tidy/modernize-replace-random-shuffle.cpp b/test/clang-tidy/modernize-replace-random-shuffle.cpp
new file mode 100644 (file)
index 0000000..2d4d4e6
--- /dev/null
@@ -0,0 +1,57 @@
+// RUN: %check_clang_tidy %s modernize-replace-random-shuffle %t -- -- -std=c++11
+
+//CHECK-FIXES: #include <random>
+
+namespace std {
+template <typename T> struct vec_iterator {
+  T *ptr;
+  vec_iterator operator++(int);
+};
+
+template <typename T> struct vector {
+  typedef vec_iterator<T> iterator;
+
+  iterator begin();
+  iterator end();
+};
+
+template <typename FwIt>
+void random_shuffle(FwIt begin, FwIt end);
+
+template <typename FwIt, typename randomFunc>
+void random_shuffle(FwIt begin, FwIt end, randomFunc& randomfunc);
+
+template <typename FwIt>
+void shuffle(FwIt begin, FwIt end);
+} // namespace std
+
+// Random Func
+int myrandom (int i) { return i;}
+
+using namespace std;
+
+int main() {
+  std::vector<int> vec;
+
+  std::random_shuffle(vec.begin(), vec.end());
+  // CHECK-MESSAGE: [[@LINE-1]]:3: warning: 'std::random_shuffle' has been removed in C++17; use 'std::shuffle' instead
+  // CHECK-FIXES: std::shuffle(vec.begin(), vec.end(), std::mt19937(std::random_device()()));
+
+  std::shuffle(vec.begin(), vec.end());
+
+  random_shuffle(vec.begin(), vec.end());
+  // CHECK-MESSAGE: [[@LINE-1]]:3: warning: 'std::random_shuffle' has been removed in C++17; use 'std::shuffle' instead
+  // CHECK-FIXES: shuffle(vec.begin(), vec.end(), std::mt19937(std::random_device()()));
+  
+  std::random_shuffle(vec.begin(), vec.end(), myrandom);
+  // CHECK-MESSAGE: [[@LINE-1]]:3: warning: 'std::random_shuffle' has been removed in C++17; use 'std::shuffle' and an alternative random mechanism instead
+  // CHECK-FIXES: std::shuffle(vec.begin(), vec.end(), std::mt19937(std::random_device()()));
+
+  random_shuffle(vec.begin(), vec.end(), myrandom);
+  // CHECK-MESSAGE: [[@LINE-1]]:3: warning: 'std::random_shuffle' has been removed in C++17; use 'std::shuffle' and an alternative random mechanism instead
+  // CHECK-FIXES: shuffle(vec.begin(), vec.end(), std::mt19937(std::random_device()()));
+
+  shuffle(vec.begin(), vec.end());
+
+  return 0;
+}
diff --git a/test/clang-tidy/modernize-return-braced-init-list.cpp b/test/clang-tidy/modernize-return-braced-init-list.cpp
new file mode 100644 (file)
index 0000000..49a41ae
--- /dev/null
@@ -0,0 +1,198 @@
+// RUN: %check_clang_tidy %s modernize-return-braced-init-list %t -- -- -std=c++14
+
+namespace std {
+typedef decltype(sizeof(int)) size_t;
+
+// libc++'s implementation
+template <class _E>
+class initializer_list {
+  const _E *__begin_;
+  size_t __size_;
+
+  initializer_list(const _E *__b, size_t __s)
+      : __begin_(__b),
+        __size_(__s) {}
+
+public:
+  typedef _E value_type;
+  typedef const _E &reference;
+  typedef const _E &const_reference;
+  typedef size_t size_type;
+
+  typedef const _E *iterator;
+  typedef const _E *const_iterator;
+
+  initializer_list() : __begin_(nullptr), __size_(0) {}
+
+  size_t size() const { return __size_; }
+  const _E *begin() const { return __begin_; }
+  const _E *end() const { return __begin_ + __size_; }
+};
+
+template <typename T>
+class vector {
+public:
+  vector(T) {}
+  vector(std::initializer_list<T>) {}
+};
+}
+
+class Bar {};
+
+Bar b0;
+
+class Foo {
+public:
+  Foo(Bar) {}
+  explicit Foo(Bar, unsigned int) {}
+  Foo(unsigned int) {}
+};
+
+class Baz {
+public:
+  Foo m() {
+    Bar bm;
+    return Foo(bm);
+    // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: avoid repeating the return type from the declaration; use a braced initializer list instead [modernize-return-braced-init-list]
+    // CHECK-FIXES: return {bm};
+  }
+};
+
+class Quux : public Foo {
+public:
+  Quux(Bar bar) : Foo(bar) {}
+  Quux(unsigned, unsigned, unsigned k = 0) : Foo(k) {}
+};
+
+Foo f() {
+  Bar b1;
+  return Foo(b1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: avoid repeating the return type
+  // CHECK-FIXES: return {b1};
+}
+
+Foo f2() {
+  Bar b2;
+  return {b2};
+}
+
+auto f3() {
+  Bar b3;
+  return Foo(b3);
+}
+
+#define A(b) Foo(b)
+
+Foo f4() {
+  Bar b4;
+  return A(b4);
+}
+
+Foo f5() {
+  Bar b5;
+  return Quux(b5);
+}
+
+Foo f6() {
+  Bar b6;
+  return Foo(b6, 1);
+}
+
+std::vector<int> f7() {
+  int i7 = 1;
+  return std::vector<int>(i7);
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: avoid repeating the return type
+}
+
+Bar f8() {
+  return {};
+}
+
+Bar f9() {
+  return Bar();
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: avoid repeating the return type
+}
+
+Bar f10() {
+  return Bar{};
+}
+
+Foo f11(Bar b11) {
+  return Foo(b11);
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: avoid repeating the return type
+  // CHECK-FIXES: return {b11};
+}
+
+Foo f12() {
+  return f11(Bar());
+}
+
+Foo f13() {
+  return Foo(Bar()); // 13
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: avoid repeating the return type
+  // CHECK-FIXES: return {Bar()}; // 13
+}
+
+Foo f14() {
+  // FIXME: Type narrowing should not occur!
+  return Foo(-1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: avoid repeating the return type
+  // CHECK-FIXES: return {-1};
+}
+
+Foo f15() {
+  return Foo(f10());
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: avoid repeating the return type
+  // CHECK-FIXES: return {f10()};
+}
+
+Quux f16() {
+  return Quux(1, 2);
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: avoid repeating the return type
+  // CHECK-FIXES: return {1, 2};
+}
+
+Quux f17() {
+  return Quux(1, 2, 3);
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: avoid repeating the return type
+  // CHECK-FIXES: return {1, 2, 3};
+}
+
+template <typename T>
+T f19() {
+  return T();
+}
+
+Bar i1 = f19<Bar>();
+Baz i2 = f19<Baz>();
+
+template <typename T>
+Foo f20(T t) {
+  return Foo(t);
+}
+
+Foo i3 = f20(b0);
+
+template <typename T>
+class BazT {
+public:
+  T m() {
+    Bar b;
+    return T(b);
+  }
+
+  Foo m2(T t) {
+    return Foo(t);
+  }
+};
+
+BazT<Foo> bazFoo;
+Foo i4 = bazFoo.m();
+Foo i5 = bazFoo.m2(b0);
+
+BazT<Quux> bazQuux;
+Foo i6 = bazQuux.m();
+Foo i7 = bazQuux.m2(b0);
+
+auto v1 = []() { return std::vector<int>({1, 2}); }();
+auto v2 = []() -> std::vector<int> { return std::vector<int>({1, 2}); }();
diff --git a/test/clang-tidy/modernize-shrink-to-fit.cpp b/test/clang-tidy/modernize-shrink-to-fit.cpp
new file mode 100644 (file)
index 0000000..f4f3388
--- /dev/null
@@ -0,0 +1,74 @@
+// RUN: %check_clang_tidy %s modernize-shrink-to-fit %t
+
+namespace std {
+template <typename T> struct vector { void swap(vector &other); };
+}
+
+void f() {
+  std::vector<int> v;
+
+  std::vector<int>(v).swap(v);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the shrink_to_fit method should be used to reduce the capacity of a shrinkable container [modernize-shrink-to-fit]
+  // CHECK-FIXES: {{^  }}v.shrink_to_fit();{{$}}
+
+  std::vector<int> &vref = v;
+  std::vector<int>(vref).swap(vref);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the shrink_to_fit method should
+  // CHECK-FIXES: {{^  }}vref.shrink_to_fit();{{$}}
+
+  std::vector<int> *vptr = &v;
+  std::vector<int>(*vptr).swap(*vptr);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the shrink_to_fit method should
+  // CHECK-FIXES: {{^  }}vptr->shrink_to_fit();{{$}}
+}
+
+struct X {
+  std::vector<int> v;
+  void f() {
+    std::vector<int>(v).swap(v);
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: the shrink_to_fit method should
+    // CHECK-FIXES: {{^    }}v.shrink_to_fit();{{$}}
+
+    std::vector<int> *vptr = &v;
+    std::vector<int>(*vptr).swap(*vptr);
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: the shrink_to_fit method should
+    // CHECK-FIXES: {{^    }}vptr->shrink_to_fit();{{$}}
+  }
+};
+
+template <typename T> void g() {
+  std::vector<int> v;
+  std::vector<int>(v).swap(v);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the shrink_to_fit method should
+  // CHECK-FIXES: {{^  }}v.shrink_to_fit();{{$}}
+
+  std::vector<T> v2;
+  std::vector<T>(v2).swap(v2);
+  // CHECK-FIXES: {{^  }}std::vector<T>(v2).swap(v2);{{$}}
+}
+
+template <typename T> void g2() {
+  std::vector<int> v;
+  std::vector<int>(v).swap(v);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the shrink_to_fit method should
+  // CHECK-FIXES: {{^  }}v.shrink_to_fit();{{$}}
+
+  T v3;
+  T(v3).swap(v3);
+  // CHECK-FIXES: {{^  }}T(v3).swap(v3);{{$}}
+}
+
+#define COPY_AND_SWAP_INT_VEC(x) std::vector<int>(x).swap(x)
+// CHECK-FIXES: #define COPY_AND_SWAP_INT_VEC(x) std::vector<int>(x).swap(x)
+
+void h() {
+  g<int>();
+  g<double>();
+  g<bool>();
+  g2<std::vector<int>>();
+  std::vector<int> v;
+  COPY_AND_SWAP_INT_VEC(v);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: the shrink_to_fit method should
+  // CHECK-FIXES: {{^  }}COPY_AND_SWAP_INT_VEC(v);{{$}}
+}
+
diff --git a/test/clang-tidy/modernize-unary-static-assert.cpp b/test/clang-tidy/modernize-unary-static-assert.cpp
new file mode 100644 (file)
index 0000000..fd39d5b
--- /dev/null
@@ -0,0 +1,25 @@
+// RUN: %check_clang_tidy %s modernize-unary-static-assert %t -- -- -std=c++1z
+
+#define FOO static_assert(sizeof(a) <= 15, "");
+#define MSG ""
+
+void f_textless(int a) {
+  static_assert(sizeof(a) <= 10, "");
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use unary 'static_assert' when the string literal is an empty string [modernize-unary-static-assert]
+  // CHECK-FIXES: {{^}}  static_assert(sizeof(a) <= 10 );{{$}}
+  static_assert(sizeof(a) <= 12, L"");
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use unary 'static_assert' when
+  // CHECK-FIXES: {{^}}  static_assert(sizeof(a) <= 12 );{{$}}
+  FOO
+  // CHECK-FIXES: {{^}}  FOO{{$}}
+  static_assert(sizeof(a) <= 17, MSG);
+  // CHECK-FIXES: {{^}}  static_assert(sizeof(a) <= 17, MSG);{{$}}
+}
+
+void f_with_tex(int a) {
+  static_assert(sizeof(a) <= 10, "Size of variable a is out of range!");
+}
+
+void f_unary(int a) { static_assert(sizeof(a) <= 10); }
+
+void f_incorrect_assert() { static_assert(""); }
diff --git a/test/clang-tidy/modernize-use-auto-cast-remove-stars.cpp b/test/clang-tidy/modernize-use-auto-cast-remove-stars.cpp
new file mode 100644 (file)
index 0000000..c1f2ed8
--- /dev/null
@@ -0,0 +1,233 @@
+// RUN: %check_clang_tidy %s modernize-use-auto %t -- \
+// RUN:   -config="{CheckOptions: [{key: modernize-use-auto.RemoveStars, value: '1'}]}" \
+// RUN:   -- -std=c++11 -frtti
+
+struct A {
+  virtual ~A() {}
+};
+
+struct B : public A {};
+
+struct C {};
+
+void f_static_cast() {
+  long l = 1;
+  int i1 = static_cast<int>(l);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+  // CHECK-FIXES: auto  i1 = static_cast<int>(l);
+
+  const int i2 = static_cast<int>(l);
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use auto when initializing with a cast to avoid duplicating the type name
+  // CHECK-FIXES: const auto  i2 = static_cast<int>(l);
+
+  long long ll = static_cast<long long>(l);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+  // CHECK-FIXES: auto  ll = static_cast<long long>(l);
+  unsigned long long ull = static_cast<unsigned long long>(l);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+  // CHECK-FIXES: auto  ull = static_cast<unsigned long long>(l);
+  unsigned int ui = static_cast<unsigned int>(l);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+  // CHECK-FIXES: auto  ui = static_cast<unsigned int>(l);
+  long double ld = static_cast<long double>(l);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+  // CHECK-FIXES: auto  ld = static_cast<long double>(l);
+
+  A *a = new B();
+  B *b1 = static_cast<B *>(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+  // CHECK-FIXES: auto b1 = static_cast<B *>(a);
+
+  B *const b2 = static_cast<B *>(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+  // CHECK-FIXES: auto const b2 = static_cast<B *>(a);
+
+  const B *b3 = static_cast<const B *>(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use auto when initializing with a cast to avoid duplicating the type name
+  // CHECK-FIXES: const auto b3 = static_cast<const B *>(a);
+
+  B &b4 = static_cast<B &>(*a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+  // CHECK-FIXES: auto  &b4 = static_cast<B &>(*a);
+
+  const B &b5 = static_cast<const B &>(*a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use auto when initializing with a cast to avoid duplicating the type name
+  // CHECK-FIXES: const auto  &b5 = static_cast<const B &>(*a);
+
+  B &b6 = static_cast<B &>(*a), &b7 = static_cast<B &>(*a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+  // CHECK-FIXES: auto  &b6 = static_cast<B &>(*a), &b7 = static_cast<B &>(*a);
+
+  // Don't warn when non-cast involved
+  long double cast = static_cast<long double>(l), noncast = 5;
+
+  // Don't warn when auto is already being used.
+  auto i3 = static_cast<int>(l);
+  auto *b8 = static_cast<B *>(a);
+  auto &b9 = static_cast<B &>(*a);
+}
+
+void f_dynamic_cast() {
+  A *a = new B();
+  B *b1 = dynamic_cast<B *>(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+  // CHECK-FIXES: auto b1 = dynamic_cast<B *>(a);
+
+  B &b2 = dynamic_cast<B &>(*a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+  // CHECK-FIXES: auto  &b2 = dynamic_cast<B &>(*a);
+}
+
+void f_reinterpret_cast() {
+  auto *a = new A();
+  C *c1 = reinterpret_cast<C *>(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+  // CHECK-FIXES: auto c1 = reinterpret_cast<C *>(a);
+
+  C &c2 = reinterpret_cast<C &>(*a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+  // CHECK-FIXES: auto  &c2 = reinterpret_cast<C &>(*a);
+}
+
+void f_const_cast() {
+  const A *a1 = new A();
+  A *a2 = const_cast<A *>(a1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+  // CHECK-FIXES: auto a2 = const_cast<A *>(a1);
+  A &a3 = const_cast<A &>(*a1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+  // CHECK-FIXES: auto  &a3 = const_cast<A &>(*a1);
+}
+
+typedef unsigned char xmlChar;
+#define BAD_CAST (xmlChar *)
+
+#define XMLCHAR_CAST(x) (xmlChar *)(x)
+
+#define CAST_IN_MACRO(x)         \
+  do {                           \
+    xmlChar *s = (xmlChar *)(x); \
+  } while (false);
+// CHECK-FIXES: xmlChar *s = (xmlChar *)(x);
+
+void f_cstyle_cast() {
+  auto *a = new A();
+  C *c1 = (C *)a;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+  // CHECK-FIXES: auto c1 = (C *)a;
+
+  C &c2 = (C &)*a;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+  // CHECK-FIXES: auto  &c2 = (C &)*a;
+
+  xmlChar  *s = BAD_CAST "xml";
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+  // CHECK-FIXES: auto s = BAD_CAST "xml";
+  xmlChar  *t = XMLCHAR_CAST("xml");
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+  // CHECK-FIXES: auto t = XMLCHAR_CAST("xml");
+  CAST_IN_MACRO("xml");
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+}
+
+void f_functional_cast() {
+  long l = 1;
+  int i1 = int(l);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+  // CHECK-FIXES: auto  i1 = int(l);
+
+  B b;
+  A a = A(b);
+}
+
+class StringRef
+{
+public:
+  StringRef(const char *);
+  const char *begin() const;
+  const char *end() const;
+};
+
+template <typename T, typename U>
+T template_value_cast(const U &u);
+
+template <typename T, typename U>
+T *template_pointer_cast(U *u);
+
+template <typename T, typename U>
+T &template_reference_cast(U &u);
+
+template <typename T, typename U>
+const T *template_const_pointer_cast(const U *u);
+
+template <typename T, typename U>
+const T &template_const_reference_cast(const U &u);
+
+template <typename T>
+T template_value_get(StringRef s);
+
+struct S {
+  template <typename T>
+  const T *template_member_get();
+};
+
+template <typename T>
+T max(T t1, T t2);
+
+void f_template_cast()
+{
+  double d = 0;
+  int i1 = template_value_cast<int>(d);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a template cast to avoid duplicating the type name
+  // CHECK-FIXES: auto  i1 = template_value_cast<int>(d);
+
+  A *a = new B();
+  B *b1 = template_value_cast<B *>(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a template cast to avoid duplicating the type name
+  // CHECK-FIXES: auto b1 = template_value_cast<B *>(a);
+  B &b2 = template_value_cast<B &>(*a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a template cast to avoid duplicating the type name
+  // CHECK-FIXES: auto  &b2 = template_value_cast<B &>(*a);
+  B *b3 = template_pointer_cast<B>(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a template cast to avoid duplicating the type name
+  // CHECK-FIXES: auto b3 = template_pointer_cast<B>(a);
+  B &b4 = template_reference_cast<B>(*a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a template cast to avoid duplicating the type name
+  // CHECK-FIXES: auto  &b4 = template_reference_cast<B>(*a);
+  const B *b5 = template_const_pointer_cast<B>(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use auto when initializing with a template cast to avoid duplicating the type name
+  // CHECK-FIXES: const auto b5 = template_const_pointer_cast<B>(a);
+  const B &b6 = template_const_reference_cast<B>(*a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use auto when initializing with a template cast to avoid duplicating the type name
+  // CHECK-FIXES: const auto  &b6 = template_const_reference_cast<B>(*a);
+  B *b7 = template_value_get<B *>("foo");
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a template cast to avoid duplicating the type name
+  // CHECK-FIXES: auto b7 = template_value_get<B *>("foo");
+  B *b8 = template_value_get<B *>("foo"), *b9 = template_value_get<B *>("bar");
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a template cast to avoid duplicating the type name
+  // CHECK-FIXES: auto b8 = template_value_get<B *>("foo"), b9 = template_value_get<B *>("bar");
+
+  S s;
+  const B *b10 = s.template_member_get<B>();
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use auto when initializing with a template cast to avoid duplicating the type name
+  // CHECK-FIXES: const auto b10 = s.template_member_get<B>();
+
+  // Don't warn when auto is already being used.
+  auto i2 = template_value_cast<int>(d);
+  auto *i3 = template_value_cast<int *>(d);
+  auto **i4 = template_value_cast<int **>(d);
+  auto &i5 = template_reference_cast<int>(d);
+
+  // Don't warn for implicit template arguments.
+  int i6 = max(i1, i2);
+
+  // Don't warn for mismatched var and initializer types.
+  A *a1 = template_value_cast<B *>(a);
+
+  // Don't warn for mismatched var types.
+  B *b11 = template_value_get<B *>("foo"), b12 = template_value_get<B>("bar");
+
+  // Don't warn for implicit variables.
+  for (auto &c : template_reference_cast<StringRef>(*a)) {
+  }
+}
diff --git a/test/clang-tidy/modernize-use-auto-cast.cpp b/test/clang-tidy/modernize-use-auto-cast.cpp
new file mode 100644 (file)
index 0000000..6be46e8
--- /dev/null
@@ -0,0 +1,232 @@
+// RUN: %check_clang_tidy %s modernize-use-auto %t -- -- \
+// RUN:   -std=c++11 -I %S/Inputs/modernize-use-auto -frtti
+
+struct A {
+  virtual ~A() {}
+};
+
+struct B : public A {};
+
+struct C {};
+
+void f_static_cast() {
+  long l = 1;
+  int i1 = static_cast<int>(l);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+  // CHECK-FIXES: auto i1 = static_cast<int>(l);
+
+  const int i2 = static_cast<int>(l);
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use auto when initializing with a cast to avoid duplicating the type name
+  // CHECK-FIXES: const auto i2 = static_cast<int>(l);
+
+  long long ll = static_cast<long long>(l);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+  // CHECK-FIXES: auto ll = static_cast<long long>(l);
+  unsigned long long ull = static_cast<unsigned long long>(l);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+  // CHECK-FIXES: auto ull = static_cast<unsigned long long>(l);
+  unsigned int ui = static_cast<unsigned int>(l);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+  // CHECK-FIXES: auto ui = static_cast<unsigned int>(l);
+  long double ld = static_cast<long double>(l);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+  // CHECK-FIXES: auto ld = static_cast<long double>(l);
+
+  A *a = new B();
+  B *b1 = static_cast<B *>(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+  // CHECK-FIXES: auto *b1 = static_cast<B *>(a);
+
+  B *const b2 = static_cast<B *>(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+  // CHECK-FIXES: auto *const b2 = static_cast<B *>(a);
+
+  const B *b3 = static_cast<const B *>(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use auto when initializing with a cast to avoid duplicating the type name
+  // CHECK-FIXES: const auto *b3 = static_cast<const B *>(a);
+
+  B &b4 = static_cast<B &>(*a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+  // CHECK-FIXES: auto &b4 = static_cast<B &>(*a);
+
+  const B &b5 = static_cast<const B &>(*a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use auto when initializing with a cast to avoid duplicating the type name
+  // CHECK-FIXES: const auto &b5 = static_cast<const B &>(*a);
+
+  B &b6 = static_cast<B &>(*a), &b7 = static_cast<B &>(*a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+  // CHECK-FIXES: auto &b6 = static_cast<B &>(*a), &b7 = static_cast<B &>(*a);
+
+  // Don't warn when non-cast involved
+  long double cast = static_cast<long double>(l), noncast = 5;
+
+  // Don't warn when auto is already being used.
+  auto i3 = static_cast<int>(l);
+  auto *b8 = static_cast<B *>(a);
+  auto &b9 = static_cast<B &>(*a);
+}
+
+void f_dynamic_cast() {
+  A *a = new B();
+  B *b1 = dynamic_cast<B *>(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+  // CHECK-FIXES: auto *b1 = dynamic_cast<B *>(a);
+
+  B &b2 = dynamic_cast<B &>(*a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+  // CHECK-FIXES: auto &b2 = dynamic_cast<B &>(*a);
+}
+
+void f_reinterpret_cast() {
+  auto *a = new A();
+  C *c1 = reinterpret_cast<C *>(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+  // CHECK-FIXES: auto *c1 = reinterpret_cast<C *>(a);
+
+  C &c2 = reinterpret_cast<C &>(*a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+  // CHECK-FIXES: auto &c2 = reinterpret_cast<C &>(*a);
+}
+
+void f_const_cast() {
+  const A *a1 = new A();
+  A *a2 = const_cast<A *>(a1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+  // CHECK-FIXES: auto *a2 = const_cast<A *>(a1);
+  A &a3 = const_cast<A &>(*a1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+  // CHECK-FIXES: auto &a3 = const_cast<A &>(*a1);
+}
+
+typedef unsigned char xmlChar;
+#define BAD_CAST (xmlChar *)
+
+#define XMLCHAR_CAST(x) (xmlChar *)(x)
+
+#define CAST_IN_MACRO(x)         \
+  do {                           \
+    xmlChar *s = (xmlChar *)(x); \
+  } while (false);
+// CHECK-FIXES: xmlChar *s = (xmlChar *)(x);
+
+void f_cstyle_cast() {
+  auto *a = new A();
+  C *c1 = (C *)a;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+  // CHECK-FIXES: auto *c1 = (C *)a;
+
+  C &c2 = (C &)*a;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+  // CHECK-FIXES: auto &c2 = (C &)*a;
+
+  xmlChar *s = BAD_CAST "xml";
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+  // CHECK-FIXES: auto *s = BAD_CAST "xml";
+  xmlChar *t = XMLCHAR_CAST("xml");
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+  // CHECK-FIXES: auto *t = XMLCHAR_CAST("xml");
+  CAST_IN_MACRO("xml");
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+}
+
+void f_functional_cast() {
+  long l = 1;
+  int i1 = int(l);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a cast to avoid duplicating the type name
+  // CHECK-FIXES: auto i1 = int(l);
+
+  B b;
+  A a = A(b);
+}
+
+class StringRef
+{
+public:
+  StringRef(const char *);
+  const char *begin() const;
+  const char *end() const;
+};
+
+template <typename T, typename U>
+T template_value_cast(const U &u);
+
+template <typename T, typename U>
+T *template_pointer_cast(U *u);
+
+template <typename T, typename U>
+T &template_reference_cast(U &u);
+
+template <typename T, typename U>
+const T *template_const_pointer_cast(const U *u);
+
+template <typename T, typename U>
+const T &template_const_reference_cast(const U &u);
+
+template <typename T>
+T template_value_get(StringRef s);
+
+struct S {
+  template <typename T>
+  const T *template_member_get();
+};
+
+template <typename T>
+T max(T t1, T t2);
+
+void f_template_cast()
+{
+  double d = 0;
+  int i1 = template_value_cast<int>(d);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a template cast to avoid duplicating the type name
+  // CHECK-FIXES: auto i1 = template_value_cast<int>(d);
+
+  A *a = new B();
+  B *b1 = template_value_cast<B *>(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a template cast to avoid duplicating the type name
+  // CHECK-FIXES: auto *b1 = template_value_cast<B *>(a);
+  B &b2 = template_value_cast<B &>(*a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a template cast to avoid duplicating the type name
+  // CHECK-FIXES: auto &b2 = template_value_cast<B &>(*a);
+  B *b3 = template_pointer_cast<B>(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a template cast to avoid duplicating the type name
+  // CHECK-FIXES: auto *b3 = template_pointer_cast<B>(a);
+  B &b4 = template_reference_cast<B>(*a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a template cast to avoid duplicating the type name
+  // CHECK-FIXES: auto &b4 = template_reference_cast<B>(*a);
+  const B *b5 = template_const_pointer_cast<B>(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use auto when initializing with a template cast to avoid duplicating the type name
+  // CHECK-FIXES: const auto *b5 = template_const_pointer_cast<B>(a);
+  const B &b6 = template_const_reference_cast<B>(*a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use auto when initializing with a template cast to avoid duplicating the type name
+  // CHECK-FIXES: const auto &b6 = template_const_reference_cast<B>(*a);
+  B *b7 = template_value_get<B *>("foo");
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a template cast to avoid duplicating the type name
+  // CHECK-FIXES: auto *b7 = template_value_get<B *>("foo");
+  B *b8 = template_value_get<B *>("foo"), *b9 = template_value_get<B *>("bar");
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with a template cast to avoid duplicating the type name
+  // CHECK-FIXES: auto *b8 = template_value_get<B *>("foo"), *b9 = template_value_get<B *>("bar");
+
+  S s;
+  const B *b10 = s.template_member_get<B>();
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use auto when initializing with a template cast to avoid duplicating the type name
+  // CHECK-FIXES: const auto *b10 = s.template_member_get<B>();
+
+  // Don't warn when auto is already being used.
+  auto i2 = template_value_cast<int>(d);
+  auto *i3 = template_value_cast<int *>(d);
+  auto **i4 = template_value_cast<int **>(d);
+  auto &i5 = template_reference_cast<int>(d);
+
+  // Don't warn for implicit template arguments.
+  int i6 = max(i1, i2);
+
+  // Don't warn for mismatched var and initializer types.
+  A *a1 = template_value_cast<B *>(a);
+
+  // Don't warn for mismatched var types.
+  B *b11 = template_value_get<B *>("foo"), b12 = template_value_get<B>("bar");
+
+  // Don't warn for implicit variables.
+  for (auto &c : template_reference_cast<StringRef>(*a)) {
+  }
+}
diff --git a/test/clang-tidy/modernize-use-auto-iterator.cpp b/test/clang-tidy/modernize-use-auto-iterator.cpp
new file mode 100644 (file)
index 0000000..98117fc
--- /dev/null
@@ -0,0 +1,320 @@
+// RUN: %check_clang_tidy %s modernize-use-auto %t -- -- \
+// RUN:   -std=c++11 -I %S/Inputs/modernize-use-auto
+
+#include "containers.h"
+
+void f_array() {
+  std::array<int, 4> C;
+  std::array<int, 4>::iterator ArrayI1 = C.begin();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators [modernize-use-auto]
+  // CHECK-FIXES: auto ArrayI1 = C.begin();
+
+  std::array<int, 5>::reverse_iterator ArrayI2 = C.rbegin();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+  // CHECK-FIXES: auto ArrayI2 = C.rbegin();
+
+  const std::array<int, 3> D;
+  std::array<int, 3>::const_iterator ArrayI3 = D.begin();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+  // CHECK-FIXES: auto ArrayI3 = D.begin();
+
+  std::array<int, 5>::const_reverse_iterator ArrayI4 = D.rbegin();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+  // CHECK-FIXES: auto ArrayI4 = D.rbegin();
+}
+
+void f_deque() {
+  std::deque<int> C;
+  std::deque<int>::iterator DequeI1 = C.begin();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+  // CHECK-FIXES: auto DequeI1 = C.begin();
+
+  std::deque<int>::reverse_iterator DequeI2 = C.rbegin();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+  // CHECK-FIXES: auto DequeI2 = C.rbegin();
+
+  const std::deque<int> D;
+  std::deque<int>::const_iterator DequeI3 = D.begin();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+  // CHECK-FIXES: auto DequeI3 = D.begin();
+
+  std::deque<int>::const_reverse_iterator DequeI4 = D.rbegin();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+  // CHECK-FIXES: auto DequeI4 = D.rbegin();
+}
+
+void f_forward_list() {
+  std::forward_list<int> C;
+  std::forward_list<int>::iterator FListI1 = C.begin();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+  // CHECK-FIXES: auto FListI1 = C.begin();
+
+  const std::forward_list<int> D;
+  std::forward_list<int>::const_iterator FListI2 = D.begin();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+  // CHECK-FIXES: auto FListI2 = D.begin();
+}
+
+void f_list() {
+  std::list<int> C;
+  std::list<int>::iterator ListI1 = C.begin();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+  // CHECK-FIXES: auto ListI1 = C.begin();
+  std::list<int>::reverse_iterator ListI2 = C.rbegin();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+  // CHECK-FIXES: auto ListI2 = C.rbegin();
+
+  const std::list<int> D;
+  std::list<int>::const_iterator ListI3 = D.begin();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+  // CHECK-FIXES: auto ListI3 = D.begin();
+  std::list<int>::const_reverse_iterator ListI4 = D.rbegin();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+  // CHECK-FIXES: auto ListI4 = D.rbegin();
+}
+
+void f_vector() {
+  std::vector<int> C;
+  std::vector<int>::iterator VecI1 = C.begin();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+  // CHECK-FIXES: auto VecI1 = C.begin();
+
+  std::vector<int>::reverse_iterator VecI2 = C.rbegin();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+  // CHECK-FIXES: auto VecI2 = C.rbegin();
+
+  const std::vector<int> D;
+  std::vector<int>::const_iterator VecI3 = D.begin();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+  // CHECK-FIXES: auto VecI3 = D.begin();
+
+  std::vector<int>::const_reverse_iterator VecI4 = D.rbegin();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+  // CHECK-FIXES: auto VecI4 = D.rbegin();
+}
+
+void f_map() {
+  std::map<int, int> C;
+  std::map<int, int>::iterator MapI1 = C.begin();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+  // CHECK-FIXES: auto MapI1 = C.begin();
+
+  std::map<int, int>::reverse_iterator MapI2 = C.rbegin();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+  // CHECK-FIXES: auto MapI2 = C.rbegin();
+
+  const std::map<int, int> D;
+  std::map<int, int>::const_iterator MapI3 = D.begin();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+  // CHECK-FIXES: auto MapI3 = D.begin();
+
+  std::map<int, int>::const_reverse_iterator MapI4 = D.rbegin();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+  // CHECK-FIXES: auto MapI4 = D.rbegin();
+}
+
+void f_multimap() {
+  std::multimap<int, int> C;
+  std::multimap<int, int>::iterator MMapI1 = C.begin();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+  // CHECK-FIXES: auto MMapI1 = C.begin();
+
+  std::multimap<int, int>::reverse_iterator MMapI2 = C.rbegin();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+  // CHECK-FIXES: auto MMapI2 = C.rbegin();
+
+  const std::multimap<int, int> D;
+  std::multimap<int, int>::const_iterator MMapI3 = D.begin();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+  // CHECK-FIXES: auto MMapI3 = D.begin();
+
+  std::multimap<int, int>::const_reverse_iterator MMapI4 = D.rbegin();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+  // CHECK-FIXES: auto MMapI4 = D.rbegin();
+}
+
+void f_set() {
+  std::set<int> C;
+  std::set<int>::iterator SetI1 = C.begin();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+  // CHECK-FIXES: auto SetI1 = C.begin();
+
+  std::set<int>::reverse_iterator SetI2 = C.rbegin();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+  // CHECK-FIXES: auto SetI2 = C.rbegin();
+
+  const std::set<int> D;
+  std::set<int>::const_iterator SetI3 = D.begin();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+  // CHECK-FIXES: auto SetI3 = D.begin();
+
+  std::set<int>::const_reverse_iterator SetI4 = D.rbegin();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+  // CHECK-FIXES: auto SetI4 = D.rbegin();
+}
+
+void f_multiset() {
+  std::multiset<int> C;
+  std::multiset<int>::iterator MSetI1 = C.begin();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+  // CHECK-FIXES: auto MSetI1 = C.begin();
+
+  std::multiset<int>::reverse_iterator MSetI2 = C.rbegin();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+  // CHECK-FIXES: auto MSetI2 = C.rbegin();
+
+  const std::multiset<int> D;
+  std::multiset<int>::const_iterator MSetI3 = D.begin();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+  // CHECK-FIXES: auto MSetI3 = D.begin();
+
+  std::multiset<int>::const_reverse_iterator MSetI4 = D.rbegin();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+  // CHECK-FIXES: auto MSetI4 = D.rbegin();
+}
+
+void f_unordered_map() {
+  std::unordered_map<int, int> C;
+  std::unordered_map<int, int>::iterator UMapI1 = C.begin();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+  // CHECK-FIXES: auto UMapI1 = C.begin();
+
+  const std::unordered_map<int, int> D;
+  std::unordered_map<int, int>::const_iterator UMapI2 = D.begin();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+  // CHECK-FIXES: auto UMapI2 = D.begin();
+}
+
+void f_unordered_multimap() {
+  std::unordered_multimap<int, int> C;
+  std::unordered_multimap<int, int>::iterator UMMapI1 = C.begin();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+  // CHECK-FIXES: auto UMMapI1 = C.begin();
+
+  const std::unordered_multimap<int, int> D;
+  std::unordered_multimap<int, int>::const_iterator UMMapI2 = D.begin();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+  // CHECK-FIXES: auto UMMapI2 = D.begin();
+}
+
+void f_unordered_set() {
+  std::unordered_set<int> C;
+  std::unordered_set<int>::iterator USetI1 = C.begin();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+  // CHECK-FIXES: auto USetI1 = C.begin();
+
+  const std::unordered_set<int> D;
+  std::unordered_set<int>::const_iterator USetI2 = D.begin();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+  // CHECK-FIXES: auto USetI2 = D.begin();
+}
+
+void f_unordered_multiset() {
+  std::unordered_multiset<int> C;
+  std::unordered_multiset<int>::iterator UMSetI1 = C.begin();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+  // CHECK-FIXES: auto UMSetI1 = C.begin();
+
+  const std::unordered_multiset<int> D;
+  std::unordered_multiset<int>::const_iterator UMSetI2 = D.begin();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+  // CHECK-FIXES: auto UMSetI2 = D.begin();
+}
+
+typedef std::vector<int>::iterator int_iterator;
+
+std::vector<int> Vec;
+std::unordered_map<int, int> Map;
+
+void sugar() {
+  // Types with more sugar should work. Types with less should not.
+  int_iterator more_sugar = Vec.begin();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+  // CHECK-FIXES: auto more_sugar = Vec.begin();
+}
+
+void initializer_list() {
+  // Initialization from initializer lists isn't allowed. Using 'auto' would
+  // result in std::initializer_list being deduced for the type.
+  std::unordered_map<int, int>::iterator I{Map.begin()};
+  std::unordered_map<int, int>::iterator I2 = {Map.begin()};
+}
+
+void construction() {
+  // Various forms of construction. Default constructors and constructors with
+  // all-default parameters shouldn't get transformed. Construction from other
+  // types is also not allowed.
+
+  std::unordered_map<int, int>::iterator copy(Map.begin());
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+  // CHECK-FIXES: auto copy(Map.begin());
+
+  std::unordered_map<int, int>::iterator def;
+  std::unordered_map<int, int>::const_iterator constI;
+
+  // Implicit conversion.
+  std::unordered_map<int, int>::const_iterator constI2 = def;
+  std::unordered_map<int, int>::const_iterator constI3(def);
+
+  // Explicit conversion
+  std::unordered_map<int, int>::const_iterator constI4
+      = std::unordered_map<int, int>::const_iterator(def);
+  // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use auto when declaring iterators
+  // CHECK-FIXES: auto constI4
+  // CHECK-FIXES-NEXT: = std::unordered_map<int, int>::const_iterator(def);
+}
+
+void pointer_to_iterator() {
+  int_iterator I = Vec.begin();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+  // CHECK-FIXES: auto I = Vec.begin();
+
+  // Pointers and references to iterators are not transformed.
+  int_iterator *IPtr = &I;
+  int_iterator &IRef = I;
+}
+
+void loop() {
+  for (std::vector<int>::iterator I = Vec.begin(); I != Vec.end(); ++I) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use auto when declaring iterators
+    // CHECK-FIXES: for (auto I = Vec.begin(); I != Vec.end(); ++I)
+  }
+
+  for (int_iterator I = Vec.begin(), E = Vec.end(); I != E; ++I) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use auto when declaring iterators
+    // CHECK-FIXES: for (auto I = Vec.begin(), E = Vec.end(); I != E; ++I)
+  }
+
+  std::vector<std::vector<int>::iterator> IterVec;
+  for (std::vector<int>::iterator I : IterVec) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use auto when declaring iterators
+    // CHECK-FIXES: for (auto I : IterVec)
+  }
+}
+
+void cv_qualifiers() {
+  // Make sure references and cv qualifiers don't get removed (i.e. replaced
+  // with just 'auto').
+  const auto & I = Vec.begin();
+  auto && I2 = Vec.begin();
+}
+
+void cleanup() {
+  // Passing a string as an argument to introduce a temporary object that will
+  // create an expression with cleanups.
+  std::map<std::string, int> MapFind;
+  std::map<std::string, int>::iterator I = MapFind.find("foo");
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+  // CHECK-FIXES: auto I = MapFind.find("foo");
+}
+
+void declaration_lists() {
+  // Declaration lists that match the declaration type with written no-list
+  // initializer are transformed.
+  std::vector<int>::iterator I = Vec.begin(), E = Vec.end();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when declaring iterators
+  // CHECK-FIXES: auto I = Vec.begin(), E = Vec.end();
+
+  // Declaration lists with non-initialized variables should not be transformed.
+  std::vector<int>::iterator J = Vec.begin(), K;
+}
diff --git a/test/clang-tidy/modernize-use-auto-new-remove-stars.cpp b/test/clang-tidy/modernize-use-auto-new-remove-stars.cpp
new file mode 100644 (file)
index 0000000..42859b8
--- /dev/null
@@ -0,0 +1,106 @@
+// RUN: %check_clang_tidy %s modernize-use-auto %t -- \
+// RUN:   -config="{CheckOptions: [{key: modernize-use-auto.RemoveStars, value: '1'}]}" \
+// RUN:   -- -std=c++11
+
+class MyType {};
+
+class MyDerivedType : public MyType {};
+
+// FIXME: the replacement sometimes results in two consecutive spaces after
+// the word 'auto' (due to the presence of spaces at both sides of '*').
+void auto_new() {
+  MyType *a_new = new MyType();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with new
+  // CHECK-FIXES: auto a_new = new MyType();
+
+  static MyType *a_static = new MyType();
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use auto when initializing with new
+  // CHECK-FIXES: static auto a_static = new MyType();
+
+  MyType *derived = new MyDerivedType();
+
+  void *vd = new MyType();
+
+  // CV-qualifier tests.
+  //
+  // NOTE : the form "type const" is expected here because of a deficiency in
+  // TypeLoc where CV qualifiers are not considered part of the type location
+  // info. That is, all that is being replaced in each case is "MyType *" and
+  // not "MyType * const".
+  static MyType * const d_static = new MyType();
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use auto when initializing with new
+  // CHECK-FIXES: static auto  const d_static = new MyType();
+
+  MyType * const a_const = new MyType();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with new
+  // CHECK-FIXES: auto  const a_const = new MyType();
+
+  MyType * volatile vol = new MyType();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with new
+  // CHECK-FIXES: auto  volatile vol = new MyType();
+
+  struct SType {} *stype = new SType;
+
+  int (**func)(int, int) = new (int(*[5])(int,int));
+
+  int *array = new int[5];
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with new
+  // CHECK-FIXES: auto array = new int[5];
+
+  MyType *ptr(new MyType);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with new
+  // CHECK-FIXES: auto ptr(new MyType);
+
+  MyType *ptr2{new MyType};
+
+  {
+    // Test for declaration lists.
+    MyType *a = new MyType(), *b = new MyType(), *c = new MyType();
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use auto when initializing with new
+    // CHECK-FIXES: auto a = new MyType(), b = new MyType(), c = new MyType();
+
+    // Non-initialized declaration should not be transformed.
+    MyType *d = new MyType(), *e;
+
+    MyType **f = new MyType*(), **g = new MyType*();
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use auto when initializing with new
+    // CHECK-FIXES: auto f = new MyType*(), g = new MyType*();
+
+    // Mismatching types in declaration lists should not be transformed.
+    MyType *h = new MyType(), **i = new MyType*();
+
+    // '*' shouldn't be removed in case of mismatching types with multiple
+    // declarations.
+    MyType *j = new MyType(), *k = new MyType(), **l = new MyType*();
+  }
+
+  {
+    // Test for typedefs.
+    typedef int * int_p;
+    // CHECK-FIXES: typedef int * int_p;
+
+    int_p a = new int;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use auto when initializing with new
+    // CHECK-FIXES: auto  a = new int;
+    int_p *b = new int*;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use auto when initializing with new
+    // CHECK-FIXES: auto b = new int*;
+
+    // Test for typedefs in declarations lists.
+    int_p c = new int, d = new int;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use auto when initializing with new
+    // CHECK-FIXES: auto  c = new int, d = new int;
+
+    // Different types should not be transformed.
+    int_p e = new int, *f = new int_p;
+
+    int_p *g = new int*, *h = new int_p;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use auto when initializing with new
+    // CHECK-FIXES: auto g = new int*, h = new int_p;
+  }
+
+  // Don't warn when 'auto' is already being used.
+  auto aut = new MyType();
+  auto *paut = new MyType();
+  const auto *pcaut = new MyType();
+}
diff --git a/test/clang-tidy/modernize-use-auto-new.cpp b/test/clang-tidy/modernize-use-auto-new.cpp
new file mode 100644 (file)
index 0000000..f6984a1
--- /dev/null
@@ -0,0 +1,108 @@
+// RUN: %check_clang_tidy %s modernize-use-auto %t
+
+class MyType {};
+
+class MyDerivedType : public MyType {};
+
+// FIXME: the replacement sometimes results in two consecutive spaces after
+// the word 'auto' (due to the presence of spaces at both sides of '*').
+void auto_new() {
+  MyType *a_new = new MyType();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with new
+  // CHECK-FIXES: auto *a_new = new MyType();
+
+  static MyType *a_static = new MyType();
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use auto when initializing with new
+  // CHECK-FIXES: static auto *a_static = new MyType();
+
+  long long *ll = new long long();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with new
+  // CHECK-FIXES: auto *ll = new long long();
+
+  MyType *derived = new MyDerivedType();
+
+  void *vd = new MyType();
+
+  // CV-qualifier tests.
+  //
+  // NOTE : the form "type const" is expected here because of a deficiency in
+  // TypeLoc where CV qualifiers are not considered part of the type location
+  // info. That is, all that is being replaced in each case is "MyType *" and
+  // not "MyType * const".
+  static MyType * const d_static = new MyType();
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use auto when initializing with new
+  // CHECK-FIXES: static auto * const d_static = new MyType();
+
+  MyType * const a_const = new MyType();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with new
+  // CHECK-FIXES: auto * const a_const = new MyType();
+
+  MyType * volatile vol = new MyType();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with new
+  // CHECK-FIXES: auto * volatile vol = new MyType();
+
+  struct SType {} *stype = new SType;
+
+  int (**func)(int, int) = new (int(*[5])(int,int));
+
+  int *array = new int[5];
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with new
+  // CHECK-FIXES: auto *array = new int[5];
+
+  MyType *ptr(new MyType);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with new
+  // CHECK-FIXES: auto *ptr(new MyType);
+
+  MyType *ptr2{new MyType};
+
+  {
+    // Test for declaration lists.
+    MyType *a = new MyType(), *b = new MyType(), *c = new MyType();
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use auto when initializing with new
+    // CHECK-FIXES: auto *a = new MyType(), *b = new MyType(), *c = new MyType();
+
+    // Non-initialized declaration should not be transformed.
+    MyType *d = new MyType(), *e;
+
+    MyType **f = new MyType*(), **g = new MyType*();
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use auto when initializing with new
+    // CHECK-FIXES: auto **f = new MyType*(), **g = new MyType*();
+
+    // Mismatching types in declaration lists should not be transformed.
+    MyType *h = new MyType(), **i = new MyType*();
+
+    // '*' shouldn't be removed in case of mismatching types with multiple
+    // declarations.
+    MyType *j = new MyType(), *k = new MyType(), **l = new MyType*();
+  }
+
+  {
+    // Test for typedefs.
+    typedef int * int_p;
+    // CHECK-FIXES: typedef int * int_p;
+
+    int_p a = new int;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use auto when initializing with new
+    // CHECK-FIXES: auto a = new int;
+    int_p *b = new int*;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use auto when initializing with new
+    // CHECK-FIXES: auto *b = new int*;
+
+    // Test for typedefs in declarations lists.
+    int_p c = new int, d = new int;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use auto when initializing with new
+    // CHECK-FIXES: auto c = new int, d = new int;
+
+    // Different types should not be transformed.
+    int_p e = new int, *f = new int_p;
+
+    int_p *g = new int*, *h = new int_p;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use auto when initializing with new
+    // CHECK-FIXES: auto *g = new int*, *h = new int_p;
+  }
+
+  // Don't warn when 'auto' is already being used.
+  auto aut = new MyType();
+  auto *paut = new MyType();
+  const auto *pcaut = new MyType();
+}
diff --git a/test/clang-tidy/modernize-use-bool-literals-ignore-macros.cpp b/test/clang-tidy/modernize-use-bool-literals-ignore-macros.cpp
new file mode 100644 (file)
index 0000000..cf8f72c
--- /dev/null
@@ -0,0 +1,148 @@
+// RUN: %check_clang_tidy %s modernize-use-bool-literals %t -- \
+// RUN:   -config="{CheckOptions: \
+// RUN:             [{key: modernize-use-bool-literals.IgnoreMacros, \
+// RUN:               value: 1}]}" \
+// RUN:   -- -std=c++11
+
+bool IntToTrue = 1;
+// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: converting integer literal to bool, use bool literal instead [modernize-use-bool-literals]
+// CHECK-FIXES: {{^}}bool IntToTrue = true;{{$}}
+
+bool IntToFalse(0);
+// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: converting integer literal to bool
+// CHECK-FIXES: {{^}}bool IntToFalse(false);{{$}}
+
+bool LongLongToTrue{0x1LL};
+// CHECK-MESSAGES: :[[@LINE-1]]:21: warning: converting integer literal to bool
+// CHECK-FIXES: {{^}}bool LongLongToTrue{true};{{$}}
+
+bool ExplicitCStyleIntToFalse = (bool)0;
+// CHECK-MESSAGES: :[[@LINE-1]]:33: warning: converting integer literal to bool
+// CHECK-FIXES: {{^}}bool ExplicitCStyleIntToFalse = false;{{$}}
+
+bool ExplicitFunctionalIntToFalse = bool(0);
+// CHECK-MESSAGES: :[[@LINE-1]]:37: warning: converting integer literal to bool
+// CHECK-FIXES: {{^}}bool ExplicitFunctionalIntToFalse = false;{{$}}
+
+bool ExplicitStaticIntToFalse = static_cast<bool>(0);
+// CHECK-MESSAGES: :[[@LINE-1]]:33: warning: converting integer literal to bool
+// CHECK-FIXES: {{^}}bool ExplicitStaticIntToFalse = false;{{$}}
+
+#define TRUE_MACRO 1
+// CHECK-FIXES: {{^}}#define TRUE_MACRO 1{{$}}
+
+bool MacroIntToTrue = TRUE_MACRO;
+// CHECK-FIXES: {{^}}bool MacroIntToTrue = TRUE_MACRO;{{$}}
+
+#define FALSE_MACRO bool(0)
+// CHECK-FIXES: {{^}}#define FALSE_MACRO bool(0){{$}}
+
+bool TrueBool = true; // OK
+
+bool FalseBool = bool(FALSE_MACRO);
+// CHECK-FIXES: {{^}}bool FalseBool = bool(FALSE_MACRO);{{$}}
+
+void boolFunction(bool bar) {
+
+}
+
+char Character = 0; // OK
+
+unsigned long long LongInteger = 1; // OK
+
+#define MACRO_DEPENDENT_CAST(x) static_cast<bool>(x)
+// CHECK-FIXES: {{^}}#define MACRO_DEPENDENT_CAST(x) static_cast<bool>(x){{$}}
+
+bool MacroDependentBool = MACRO_DEPENDENT_CAST(0);
+// CHECK-FIXES: {{^}}bool MacroDependentBool = MACRO_DEPENDENT_CAST(0);{{$}}
+
+bool ManyMacrosDependent = MACRO_DEPENDENT_CAST(FALSE_MACRO);
+// CHECK-FIXES: {{^}}bool ManyMacrosDependent = MACRO_DEPENDENT_CAST(FALSE_MACRO);{{$}}
+
+class FooClass {
+  public:
+  FooClass() : JustBool(0) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: converting integer literal to bool
+  // CHECK-FIXES: {{^ *}}FooClass() : JustBool(false) {}{{$}}
+  FooClass(int) : JustBool{0} {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:28: warning: converting integer literal to bool
+  // CHECK-FIXES: {{^ *}}FooClass(int) : JustBool{false} {}{{$}}
+  private:
+  bool JustBool;
+  bool BoolWithBraces{0};
+  // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: converting integer literal to bool
+  // CHECK-FIXES: {{^ *}}bool BoolWithBraces{false};{{$}}
+  bool BoolFromInt = 0;
+  // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: converting integer literal to bool
+  // CHECK-FIXES: {{^ *}}bool BoolFromInt = false;{{$}}
+  bool SimpleBool = true; // OK
+};
+
+template<typename type>
+void templateFunction(type) {
+  type TemplateType = 0;
+  // CHECK-FIXES: {{^ *}}type TemplateType = 0;{{$}}
+}
+
+template<int c>
+void valueDependentTemplateFunction() {
+  bool Boolean = c;
+  // CHECK-FIXES: {{^ *}}bool Boolean = c;{{$}}
+}
+
+template<typename type>
+void anotherTemplateFunction(type) {
+  bool JustBool = 0;
+  // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: converting integer literal to bool
+  // CHECK-FIXES: {{^ *}}bool JustBool = false;{{$}}
+}
+
+int main() {
+  boolFunction(1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: converting integer literal to bool
+  // CHECK-FIXES: {{^ *}}boolFunction(true);{{$}}
+
+  boolFunction(false);
+
+  templateFunction(0);
+
+  templateFunction(false);
+
+  valueDependentTemplateFunction<1>();
+
+  anotherTemplateFunction(1);
+
+  IntToTrue = 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: converting integer literal to bool
+  // CHECK-FIXES: {{^ *}}IntToTrue = true;{{$}}
+}
+
+static int Value = 1;
+
+bool Function1() {
+  bool Result = Value == 1 ? 1 : 0;
+  // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: converting integer literal to bool
+  // CHECK-MESSAGES: :[[@LINE-2]]:34: warning: converting integer literal to bool
+  // CHECK-FIXES: {{^ *}}bool Result = Value == 1 ? true : false;{{$}}
+  return Result;
+}
+
+bool Function2() {
+  return Value == 1 ? 1 : 0;
+  // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: converting integer literal to bool
+  // CHECK-MESSAGES: :[[@LINE-2]]:27: warning: converting integer literal to bool
+  // CHECK-FIXES: {{^ *}}return Value == 1 ? true : false;{{$}}
+}
+
+void foo() {
+  bool Result;
+  Result = Value == 1 ? true : 0;
+  // CHECK-MESSAGES: :[[@LINE-1]]:32: warning: converting integer literal to bool
+  // CHECK-FIXES: {{^ *}}Result = Value == 1 ? true : false;{{$}}
+  Result = Value == 1 ? false : bool(0);
+  // CHECK-MESSAGES: :[[@LINE-1]]:33: warning: converting integer literal to bool
+  // CHECK-FIXES: {{^ *}}Result = Value == 1 ? false : false;{{$}}
+  Result = Value == 1 ? (bool)0 : false;
+  // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: converting integer literal to bool
+  // CHECK-FIXES: {{^ *}}Result = Value == 1 ? false : false;{{$}}
+}
diff --git a/test/clang-tidy/modernize-use-bool-literals.cpp b/test/clang-tidy/modernize-use-bool-literals.cpp
new file mode 100644 (file)
index 0000000..f2ab9ce
--- /dev/null
@@ -0,0 +1,152 @@
+// RUN: %check_clang_tidy %s modernize-use-bool-literals %t -- \
+// RUN:   -config="{CheckOptions: \
+// RUN:             [{key: modernize-use-bool-literals.IgnoreMacros, \
+// RUN:               value: 0}]}" \
+// RUN:   -- -std=c++11
+
+bool IntToTrue = 1;
+// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: converting integer literal to bool, use bool literal instead [modernize-use-bool-literals]
+// CHECK-FIXES: {{^}}bool IntToTrue = true;{{$}}
+
+bool IntToFalse(0);
+// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: converting integer literal to bool
+// CHECK-FIXES: {{^}}bool IntToFalse(false);{{$}}
+
+bool LongLongToTrue{0x1LL};
+// CHECK-MESSAGES: :[[@LINE-1]]:21: warning: converting integer literal to bool
+// CHECK-FIXES: {{^}}bool LongLongToTrue{true};{{$}}
+
+bool ExplicitCStyleIntToFalse = (bool)0;
+// CHECK-MESSAGES: :[[@LINE-1]]:33: warning: converting integer literal to bool
+// CHECK-FIXES: {{^}}bool ExplicitCStyleIntToFalse = false;{{$}}
+
+bool ExplicitFunctionalIntToFalse = bool(0);
+// CHECK-MESSAGES: :[[@LINE-1]]:37: warning: converting integer literal to bool
+// CHECK-FIXES: {{^}}bool ExplicitFunctionalIntToFalse = false;{{$}}
+
+bool ExplicitStaticIntToFalse = static_cast<bool>(0);
+// CHECK-MESSAGES: :[[@LINE-1]]:33: warning: converting integer literal to bool
+// CHECK-FIXES: {{^}}bool ExplicitStaticIntToFalse = false;{{$}}
+
+#define TRUE_MACRO 1
+// CHECK-FIXES: {{^}}#define TRUE_MACRO 1{{$}}
+
+bool MacroIntToTrue = TRUE_MACRO;
+// CHECK-MESSAGES: :[[@LINE-1]]:23: warning: converting integer literal to bool
+// CHECK-FIXES: {{^}}bool MacroIntToTrue = TRUE_MACRO;{{$}}
+
+#define FALSE_MACRO bool(0)
+// CHECK-FIXES: {{^}}#define FALSE_MACRO bool(0){{$}}
+
+bool TrueBool = true; // OK
+
+bool FalseBool = bool(FALSE_MACRO);
+// CHECK-MESSAGES: :[[@LINE-1]]:23: warning: converting integer literal to bool
+// CHECK-FIXES: {{^}}bool FalseBool = bool(FALSE_MACRO);{{$}}
+
+void boolFunction(bool bar) {
+
+}
+
+char Character = 0; // OK
+
+unsigned long long LongInteger = 1; // OK
+
+#define MACRO_DEPENDENT_CAST(x) static_cast<bool>(x)
+// CHECK-FIXES: {{^}}#define MACRO_DEPENDENT_CAST(x) static_cast<bool>(x){{$}}
+
+bool MacroDependentBool = MACRO_DEPENDENT_CAST(0);
+// CHECK-MESSAGES: :[[@LINE-1]]:27: warning: converting integer literal to bool
+// CHECK-FIXES: {{^}}bool MacroDependentBool = MACRO_DEPENDENT_CAST(0);{{$}}
+
+bool ManyMacrosDependent = MACRO_DEPENDENT_CAST(FALSE_MACRO);
+// CHECK-MESSAGES: :[[@LINE-1]]:49: warning: converting integer literal to bool
+// CHECK-FIXES: {{^}}bool ManyMacrosDependent = MACRO_DEPENDENT_CAST(FALSE_MACRO);{{$}}
+
+class FooClass {
+  public:
+  FooClass() : JustBool(0) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: converting integer literal to bool
+  // CHECK-FIXES: {{^ *}}FooClass() : JustBool(false) {}{{$}}
+  FooClass(int) : JustBool{0} {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:28: warning: converting integer literal to bool
+  // CHECK-FIXES: {{^ *}}FooClass(int) : JustBool{false} {}{{$}}
+  private:
+  bool JustBool;
+  bool BoolWithBraces{0};
+  // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: converting integer literal to bool
+  // CHECK-FIXES: {{^ *}}bool BoolWithBraces{false};{{$}}
+  bool BoolFromInt = 0;
+  // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: converting integer literal to bool
+  // CHECK-FIXES: {{^ *}}bool BoolFromInt = false;{{$}}
+  bool SimpleBool = true; // OK
+};
+
+template<typename type>
+void templateFunction(type) {
+  type TemplateType = 0;
+  // CHECK-FIXES: {{^ *}}type TemplateType = 0;{{$}}
+}
+
+template<int c>
+void valueDependentTemplateFunction() {
+  bool Boolean = c;
+  // CHECK-FIXES: {{^ *}}bool Boolean = c;{{$}}
+}
+
+template<typename type>
+void anotherTemplateFunction(type) {
+  bool JustBool = 0;
+  // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: converting integer literal to bool
+  // CHECK-FIXES: {{^ *}}bool JustBool = false;{{$}}
+}
+
+int main() {
+  boolFunction(1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: converting integer literal to bool
+  // CHECK-FIXES: {{^ *}}boolFunction(true);{{$}}
+
+  boolFunction(false);
+
+  templateFunction(0);
+
+  templateFunction(false);
+
+  valueDependentTemplateFunction<1>();
+
+  anotherTemplateFunction(1);
+
+  IntToTrue = 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: converting integer literal to bool
+  // CHECK-FIXES: {{^ *}}IntToTrue = true;{{$}}
+}
+
+static int Value = 1;
+
+bool Function1() {
+  bool Result = Value == 1 ? 1 : 0;
+  // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: converting integer literal to bool
+  // CHECK-MESSAGES: :[[@LINE-2]]:34: warning: converting integer literal to bool
+  // CHECK-FIXES: {{^ *}}bool Result = Value == 1 ? true : false;{{$}}
+  return Result;
+}
+
+bool Function2() {
+  return Value == 1 ? 1 : 0;
+  // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: converting integer literal to bool
+  // CHECK-MESSAGES: :[[@LINE-2]]:27: warning: converting integer literal to bool
+  // CHECK-FIXES: {{^ *}}return Value == 1 ? true : false;{{$}}
+}
+
+void foo() {
+  bool Result;
+  Result = Value == 1 ? true : 0;
+  // CHECK-MESSAGES: :[[@LINE-1]]:32: warning: converting integer literal to bool
+  // CHECK-FIXES: {{^ *}}Result = Value == 1 ? true : false;{{$}}
+  Result = Value == 1 ? false : bool(0);
+  // CHECK-MESSAGES: :[[@LINE-1]]:33: warning: converting integer literal to bool
+  // CHECK-FIXES: {{^ *}}Result = Value == 1 ? false : false;{{$}}
+  Result = Value == 1 ? (bool)0 : false;
+  // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: converting integer literal to bool
+  // CHECK-FIXES: {{^ *}}Result = Value == 1 ? false : false;{{$}}
+}
diff --git a/test/clang-tidy/modernize-use-default-member-init-assignment.cpp b/test/clang-tidy/modernize-use-default-member-init-assignment.cpp
new file mode 100644 (file)
index 0000000..fdc0db1
--- /dev/null
@@ -0,0 +1,184 @@
+// RUN: %check_clang_tidy %s modernize-use-default-member-init %t -- \
+// RUN: -config="{CheckOptions: [{key: modernize-use-default-member-init.UseAssignment, value: 1}]}" -- -std=c++11
+
+struct S {
+};
+
+struct PositiveValueChar {
+  PositiveValueChar() : c0(), c1()/*, c2(), c3()*/ {}
+  // CHECK-FIXES: PositiveValueChar()  {}
+  const char c0;
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: use default member initializer for 'c0' [modernize-use-default-member-init]
+  // CHECK-FIXES: const char c0 = '\0';
+  wchar_t c1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: use default member initializer for 'c1'
+  // CHECK-FIXES: wchar_t c1 = L'\0';
+  // FIXME: char16_t c2;
+  // C HECK-MESSAGES: :[[@LINE-1]]:12: warning: use default member initializer for 'c2'
+  // C HECK-FIXES: char16_t c2 = u'\0';
+  // FIXME: char32_t c3;
+  // C HECK-MESSAGES: :[[@LINE-1]]:12: warning: use default member initializer for 'c3'
+  // C HECK-FIXES: char32_t c3 = U'\0';
+};
+
+struct PositiveChar {
+  PositiveChar() : d('a') {}
+  // CHECK-FIXES: PositiveChar()  {}
+  char d;
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use default member initializer for 'd'
+  // CHECK-FIXES: char d = 'a';
+};
+
+struct PositiveValueInt {
+  PositiveValueInt() : i() {}
+  // CHECK-FIXES: PositiveValueInt()  {}
+  const int i;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use default member initializer for 'i'
+  // CHECK-FIXES: const int i = 0;
+};
+
+struct PositiveInt {
+  PositiveInt() : j(1) {}
+  // CHECK-FIXES: PositiveInt()  {}
+  int j;
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use default member initializer for 'j'
+  // CHECK-FIXES: int j = 1;
+};
+
+struct PositiveUnaryMinusInt {
+  PositiveUnaryMinusInt() : j(-1) {}
+  // CHECK-FIXES: PositiveUnaryMinusInt()  {}
+  int j;
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use default member initializer for 'j'
+  // CHECK-FIXES: int j = -1;
+};
+
+struct PositiveUnaryPlusInt {
+  PositiveUnaryPlusInt() : j(+1) {}
+  // CHECK-FIXES: PositiveUnaryPlusInt()  {}
+  int j;
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use default member initializer for 'j'
+  // CHECK-FIXES: int j = +1;
+};
+
+struct PositiveValueComplexInt {
+  PositiveValueComplexInt() : i() {}
+  // CHECK-FIXES: PositiveValueComplexInt()  {}
+  _Complex int i;
+  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: use default member initializer for 'i'
+  // CHECK-FIXES: _Complex int i = 0;
+};
+
+struct PositiveValueFloat {
+  PositiveValueFloat() : f() {}
+  // CHECK-FIXES: PositiveValueFloat()  {}
+  float f;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use default member initializer for 'f'
+  // CHECK-FIXES: float f = 0.0f;
+};
+
+struct PositiveValueDouble {
+  PositiveValueDouble() : d() {}
+  // CHECK-FIXES: PositiveValueDouble()  {}
+  double d;
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use default member initializer for 'd'
+  // CHECK-FIXES: double d = 0.0;
+};
+
+struct PositiveDouble {
+  PositiveDouble() : f(2.5463e43) {}
+  // CHECK-FIXES: PositiveDouble()  {}
+  double f;
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use default member initializer for 'f'
+  // CHECK-FIXES: double f = 2.5463e43;
+};
+
+struct PositiveValueComplexFloat {
+  PositiveValueComplexFloat() : f() {}
+  // CHECK-FIXES: PositiveValueComplexFloat()  {}
+  _Complex float f;
+  // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: use default member initializer for 'f'
+  // CHECK-FIXES: _Complex float f = 0.0f;
+};
+
+struct PositiveValueComplexDouble {
+  PositiveValueComplexDouble() : f() {}
+  // CHECK-FIXES: PositiveValueComplexDouble()  {}
+  _Complex double f;
+  // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use default member initializer for 'f'
+  // CHECK-FIXES: _Complex double f = 0.0;
+};
+
+struct PositiveUnaryMinusDouble {
+  PositiveUnaryMinusDouble() : f(-2.5463e43) {}
+  // CHECK-FIXES: PositiveUnaryMinusDouble()  {}
+  double f;
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use default member initializer for 'f'
+  // CHECK-FIXES: double f = -2.5463e43;
+};
+
+struct PositiveUnaryPlusDouble {
+  PositiveUnaryPlusDouble() : f(+2.5463e43) {}
+  // CHECK-FIXES: PositiveUnaryPlusDouble()  {}
+  double f;
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use default member initializer for 'f'
+  // CHECK-FIXES: double f = +2.5463e43;
+};
+
+struct PositiveValueBool {
+  PositiveValueBool() : b() {}
+  // CHECK-FIXES: PositiveValueBool()  {}
+  bool b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use default member initializer for 'b'
+  // CHECK-FIXES: bool b = false;
+};
+
+struct PositiveBool {
+  PositiveBool() : a(true) {}
+  // CHECK-FIXES: PositiveBool()  {}
+  bool a;
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use default member initializer for 'a'
+  // CHECK-FIXES: bool a = true;
+};
+
+struct PositiveValuePointer {
+  PositiveValuePointer() : p() {}
+  // CHECK-FIXES: PositiveValuePointer()  {}
+  int *p;
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use default member initializer for 'p'
+  // CHECK-FIXES: int *p = nullptr;
+};
+
+struct PositiveNullPointer {
+  PositiveNullPointer() : q(nullptr) {}
+  // CHECK-FIXES: PositiveNullPointer()  {}
+  int *q;
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use default member initializer for 'q'
+  // CHECK-FIXES: int *q = nullptr;
+};
+
+enum Enum { Foo };
+struct PositiveEnum {
+  PositiveEnum() : e(Foo) {}
+  // CHECK-FIXES: PositiveEnum()  {}
+  Enum e;
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use default member initializer for 'e'
+  // CHECK-FIXES: Enum e = Foo;
+};
+
+struct PositiveString {
+  PositiveString() : s("foo") {}
+  // CHECK-FIXES: PositiveString()  {}
+  const char *s;
+  // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: use default member initializer for 's'
+  // CHECK-FIXES: const char *s = "foo";
+};
+
+template <typename T>
+struct NegativeTemplate {
+    NegativeTemplate() : t() {}
+    T t;
+};
+
+NegativeTemplate<int> nti;
+NegativeTemplate<double> ntd;
diff --git a/test/clang-tidy/modernize-use-default-member-init-macros.cpp b/test/clang-tidy/modernize-use-default-member-init-macros.cpp
new file mode 100644 (file)
index 0000000..5b366b5
--- /dev/null
@@ -0,0 +1,18 @@
+// RUN: %check_clang_tidy %s modernize-use-default-member-init %t -- \
+// RUN:   -config="{CheckOptions: [{key: modernize-use-default-member-init.IgnoreMacros, value: 0}]}" \
+// RUN:   -- -std=c++11
+
+#define MACRO() \
+  struct S { \
+    void *P; \
+    S() : P(nullptr) {} \
+  };
+
+MACRO();
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use default member initializer for 'P'
+
+struct S2 {
+  void *P;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use default member initializer for 'P'
+  S2() : P(nullptr) {}
+};
diff --git a/test/clang-tidy/modernize-use-default-member-init.cpp b/test/clang-tidy/modernize-use-default-member-init.cpp
new file mode 100644 (file)
index 0000000..5b9b93c
--- /dev/null
@@ -0,0 +1,391 @@
+// RUN: %check_clang_tidy %s modernize-use-default-member-init %t -- -- -std=c++11
+
+struct S {
+};
+
+struct PositiveValueChar {
+  PositiveValueChar() : c0(), c1()/*, c2(), c3()*/ {}
+  // CHECK-FIXES: PositiveValueChar()  {}
+  const char c0;
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: use default member initializer for 'c0' [modernize-use-default-member-init]
+  // CHECK-FIXES: const char c0{};
+  wchar_t c1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: use default member initializer for 'c1'
+  // CHECK-FIXES: wchar_t c1{};
+  // FIXME: char16_t c2;
+  // C HECK-MESSAGES: :[[@LINE-1]]:12: warning: use default member initializer for 'c2'
+  // C HECK-FIXES: char16_t c2{};
+  // FIXME: char32_t c3;
+  // C HECK-MESSAGES: :[[@LINE-1]]:12: warning: use default member initializer for 'c3'
+  // C HECK-FIXES: char32_t c3{};
+};
+
+struct PositiveChar {
+  PositiveChar() : d('a') {}
+  // CHECK-FIXES: PositiveChar()  {}
+  char d;
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use default member initializer for 'd'
+  // CHECK-FIXES: char d{'a'};
+};
+
+struct PositiveValueInt {
+  PositiveValueInt() : i() {}
+  // CHECK-FIXES: PositiveValueInt()  {}
+  const int i;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use default member initializer for 'i'
+  // CHECK-FIXES: const int i{};
+};
+
+struct PositiveInt {
+  PositiveInt() : j(1) {}
+  // CHECK-FIXES: PositiveInt()  {}
+  int j;
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use default member initializer for 'j'
+  // CHECK-FIXES: int j{1};
+};
+
+struct PositiveUnaryMinusInt {
+  PositiveUnaryMinusInt() : j(-1) {}
+  // CHECK-FIXES: PositiveUnaryMinusInt()  {}
+  int j;
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use default member initializer for 'j'
+  // CHECK-FIXES: int j{-1};
+};
+
+struct PositiveUnaryPlusInt {
+  PositiveUnaryPlusInt() : j(+1) {}
+  // CHECK-FIXES: PositiveUnaryPlusInt()  {}
+  int j;
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use default member initializer for 'j'
+  // CHECK-FIXES: int j{+1};
+};
+
+struct PositiveValueComplexInt {
+  PositiveValueComplexInt() : i() {}
+  // CHECK-FIXES: PositiveValueComplexInt()  {}
+  _Complex int i;
+  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: use default member initializer for 'i'
+  // CHECK-FIXES: _Complex int i{};
+};
+
+struct PositiveValueFloat {
+  PositiveValueFloat() : f() {}
+  // CHECK-FIXES: PositiveValueFloat()  {}
+  float f;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use default member initializer for 'f'
+  // CHECK-FIXES: float f{};
+};
+
+struct PositiveValueDouble {
+  PositiveValueDouble() : d() {}
+  // CHECK-FIXES: PositiveValueDouble()  {}
+  double d;
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use default member initializer for 'd'
+  // CHECK-FIXES: double d{};
+};
+
+struct PositiveDouble {
+  PositiveDouble() : f(2.5463e43) {}
+  // CHECK-FIXES: PositiveDouble()  {}
+  double f;
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use default member initializer for 'f'
+  // CHECK-FIXES: double f{2.5463e43};
+};
+
+struct PositiveValueComplexFloat {
+  PositiveValueComplexFloat() : f() {}
+  // CHECK-FIXES: PositiveValueComplexFloat()  {}
+  _Complex float f;
+  // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: use default member initializer for 'f'
+  // CHECK-FIXES: _Complex float f{};
+};
+
+struct PositiveValueComplexDouble {
+  PositiveValueComplexDouble() : f() {}
+  // CHECK-FIXES: PositiveValueComplexDouble()  {}
+  _Complex double f;
+  // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use default member initializer for 'f'
+  // CHECK-FIXES: _Complex double f{};
+};
+
+struct PositiveUnaryMinusDouble {
+  PositiveUnaryMinusDouble() : f(-2.5463e43) {}
+  // CHECK-FIXES: PositiveUnaryMinusDouble()  {}
+  double f;
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use default member initializer for 'f'
+  // CHECK-FIXES: double f{-2.5463e43};
+};
+
+struct PositiveUnaryPlusDouble {
+  PositiveUnaryPlusDouble() : f(+2.5463e43) {}
+  // CHECK-FIXES: PositiveUnaryPlusDouble()  {}
+  double f;
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use default member initializer for 'f'
+  // CHECK-FIXES: double f{+2.5463e43};
+};
+
+struct PositiveValueBool {
+  PositiveValueBool() : b() {}
+  // CHECK-FIXES: PositiveValueBool()  {}
+  bool b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use default member initializer for 'b'
+  // CHECK-FIXES: bool b{};
+};
+
+struct PositiveBool {
+  PositiveBool() : a(true) {}
+  // CHECK-FIXES: PositiveBool()  {}
+  bool a;
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use default member initializer for 'a'
+  // CHECK-FIXES: bool a{true};
+};
+
+struct PositiveValuePointer {
+  PositiveValuePointer() : p() {}
+  // CHECK-FIXES: PositiveValuePointer()  {}
+  int *p;
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use default member initializer for 'p'
+  // CHECK-FIXES: int *p{};
+};
+
+struct PositiveNullPointer {
+  PositiveNullPointer() : q(nullptr) {}
+  // CHECK-FIXES: PositiveNullPointer()  {}
+  int *q;
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use default member initializer for 'q'
+  // CHECK-FIXES: int *q{nullptr};
+};
+
+enum Enum { Foo, Bar };
+struct PositiveEnum {
+  PositiveEnum() : e(Foo) {}
+  // CHECK-FIXES: PositiveEnum()  {}
+  Enum e;
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use default member initializer for 'e'
+  // CHECK-FIXES: Enum e{Foo};
+};
+
+struct PositiveString {
+  PositiveString() : s("foo") {}
+  // CHECK-FIXES: PositiveString()  {}
+  const char *s;
+  // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: use default member initializer for 's'
+  // CHECK-FIXES: const char *s{"foo"};
+};
+
+template <typename T>
+struct NegativeTemplate {
+    NegativeTemplate() : t() {}
+    T t;
+};
+
+NegativeTemplate<int> nti;
+NegativeTemplate<double> ntd;
+
+struct NegativeDefaultMember {
+  NegativeDefaultMember() {}
+  int i = 2;
+};
+
+struct NegativeClass : S {
+  NegativeClass() : s() {}
+  S s;
+};
+
+struct NegativeBase : S {
+  NegativeBase() : S() {}
+};
+
+struct NegativeDefaultOtherMember{
+  NegativeDefaultOtherMember() : i(3) {}
+  int i = 4;
+};
+
+struct NegativeUnion {
+  NegativeUnion() : d(5.0) {}
+  union {
+    int i;
+    double d;
+  };
+};
+
+struct NegativeBitField
+{
+  NegativeBitField() : i(6) {}
+  int i : 5;
+};
+
+struct NegativeNotDefaultInt
+{
+  NegativeNotDefaultInt(int) : i(7) {}
+  int i;
+};
+
+struct NegativeDefaultArg
+{
+  NegativeDefaultArg(int i = 4) : i(i) {}
+  int i;
+};
+
+struct ExistingChar {
+  ExistingChar(short) : e1(), e2(), e3(), e4() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: member initializer for 'e1' is redundant [modernize-use-default-member-init]
+  // CHECK-MESSAGES: :[[@LINE-2]]:31: warning: member initializer for 'e2' is redundant
+  // CHECK-MESSAGES: :[[@LINE-3]]:37: warning: member initializer for 'e3' is redundant
+  // CHECK-FIXES: ExistingChar(short) :  e4() {}
+  ExistingChar(int) : e1(0), e2(0), e3(0), e4(0) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: member initializer for 'e1' is redundant
+  // CHECK-MESSAGES: :[[@LINE-2]]:30: warning: member initializer for 'e2' is redundant
+  // CHECK-MESSAGES: :[[@LINE-3]]:37: warning: member initializer for 'e3' is redundant
+  // CHECK-FIXES: ExistingChar(int) :  e4(0) {}
+  ExistingChar(long) : e1('\0'), e2('\0'), e3('\0'), e4('\0') {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: member initializer for 'e1' is redundant
+  // CHECK-MESSAGES: :[[@LINE-2]]:34: warning: member initializer for 'e2' is redundant
+  // CHECK-MESSAGES: :[[@LINE-3]]:44: warning: member initializer for 'e3' is redundant
+  // CHECK-FIXES: ExistingChar(long) :  e4('\0') {}
+  ExistingChar(char) : e1('a'), e2('a'), e3('a'), e4('a') {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:51: warning: member initializer for 'e4' is redundant
+  // CHECK-FIXES: ExistingChar(char) : e1('a'), e2('a'), e3('a') {}
+  char e1{};
+  char e2 = 0;
+  char e3 = '\0';
+  char e4 = 'a';
+};
+
+struct ExistingInt {
+  ExistingInt(short) : e1(), e2(), e3(), e4(), e5(), e6() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: member initializer for 'e1' is redundant [modernize-use-default-member-init]
+  // CHECK-MESSAGES: :[[@LINE-2]]:30: warning: member initializer for 'e2' is redundant
+  // CHECK-FIXES: ExistingInt(short) :  e3(), e4(), e5(), e6() {}
+  ExistingInt(int) : e1(0), e2(0), e3(0), e4(0), e5(0), e6(0) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: member initializer for 'e1' is redundant
+  // CHECK-MESSAGES: :[[@LINE-2]]:29: warning: member initializer for 'e2' is redundant
+  // CHECK-FIXES: ExistingInt(int) :  e3(0), e4(0), e5(0), e6(0) {}
+  ExistingInt(long) : e1(5), e2(5), e3(5), e4(5), e5(5), e6(5) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:37: warning: member initializer for 'e3' is redundant
+  // CHECK-MESSAGES: :[[@LINE-2]]:44: warning: member initializer for 'e4' is redundant
+  // CHECK-MESSAGES: :[[@LINE-3]]:58: warning: member initializer for 'e6' is redundant
+  // CHECK-FIXES: ExistingInt(long) : e1(5), e2(5),  e5(5) {}
+  ExistingInt(char) : e1(-5), e2(-5), e3(-5), e4(-5), e5(-5), e6(-5) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:55: warning: member initializer for 'e5' is redundant
+  // CHECK-FIXES: ExistingInt(char) : e1(-5), e2(-5), e3(-5), e4(-5),  e6(-5) {}
+  int e1{};
+  int e2 = 0;
+  int e3 = {5};
+  int e4 = 5;
+  int e5 = -5;
+  int e6 = +5;
+};
+
+struct ExistingDouble {
+  ExistingDouble(short) : e1(), e2(), e3(), e4(), e5() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: member initializer for 'e1' is redundant
+  // CHECK-MESSAGES: :[[@LINE-2]]:33: warning: member initializer for 'e2' is redundant
+  // CHECK-FIXES: ExistingDouble(short) :  e3(), e4(), e5() {}
+  ExistingDouble(int) : e1(0.0), e2(0.0), e3(0.0), e4(0.0), e5(0.0) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: member initializer for 'e1' is redundant
+  // CHECK-MESSAGES: :[[@LINE-2]]:34: warning: member initializer for 'e2' is redundant
+  // CHECK-FIXES: ExistingDouble(int) :  e3(0.0), e4(0.0), e5(0.0) {}
+  ExistingDouble(long) : e1(5.0), e2(5.0), e3(5.0), e4(5.0), e5(5.0) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:44: warning: member initializer for 'e3' is redundant
+  // CHECK-MESSAGES: :[[@LINE-2]]:62: warning: member initializer for 'e5' is redundant
+  // CHECK-FIXES: ExistingDouble(long) : e1(5.0), e2(5.0),  e4(5.0) {}
+  ExistingDouble(char) : e1(-5.0), e2(-5.0), e3(-5.0), e4(-5.0), e5(-5.0) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:56: warning: member initializer for 'e4' is redundant
+  // CHECK-FIXES: ExistingDouble(char) : e1(-5.0), e2(-5.0), e3(-5.0),  e5(-5.0) {}
+  double e1{};
+  double e2 = 0.0;
+  double e3 = 5.0;
+  double e4 = -5.0;
+  double e5 = +5.0;
+};
+
+struct ExistingBool {
+  ExistingBool(short) : e1(), e2(), e3() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: member initializer for 'e1' is redundant
+  // CHECK-MESSAGES: :[[@LINE-2]]:31: warning: member initializer for 'e2' is redundant
+  // CHECK-FIXES: ExistingBool(short) :  e3() {}
+  ExistingBool(int) : e1(false), e2(false), e3(false) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: member initializer for 'e1' is redundant
+  // CHECK-MESSAGES: :[[@LINE-2]]:34: warning: member initializer for 'e2' is redundant
+  // CHECK-FIXES: ExistingBool(int) :  e3(false) {}
+  ExistingBool(long) : e1(true), e2(true), e3(true) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:44: warning: member initializer for 'e3' is redundant
+  // CHECK-FIXES: ExistingBool(long) : e1(true), e2(true) {}
+  bool e1{};
+  bool e2 = false;
+  bool e3 = true;
+};
+
+struct ExistingEnum {
+  ExistingEnum(short) : e1(Foo), e2(Foo) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: member initializer for 'e1' is redundant
+  // CHECK-FIXES: ExistingEnum(short) :  e2(Foo) {}
+  ExistingEnum(int) : e1(Bar), e2(Bar) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:32: warning: member initializer for 'e2' is redundant
+  // CHECK-FIXES: ExistingEnum(int) : e1(Bar) {}
+  Enum e1 = Foo;
+  Enum e2{Bar};
+};
+
+struct ExistingPointer {
+  ExistingPointer(short) : e1(), e2(), e3(), e4() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:28: warning: member initializer for 'e1' is redundant
+  // CHECK-MESSAGES: :[[@LINE-2]]:34: warning: member initializer for 'e2' is redundant
+  // CHECK-MESSAGES: :[[@LINE-3]]:40: warning: member initializer for 'e3' is redundant
+  // CHECK-FIXES: ExistingPointer(short) :  e4() {}
+  ExistingPointer(int) : e1(0), e2(0), e3(0), e4(&e1) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: member initializer for 'e1' is redundant
+  // CHECK-MESSAGES: :[[@LINE-2]]:33: warning: member initializer for 'e2' is redundant
+  // CHECK-MESSAGES: :[[@LINE-3]]:40: warning: member initializer for 'e3' is redundant
+  // CHECK-FIXES: ExistingPointer(int) :  e4(&e1) {}
+  ExistingPointer(long) : e1(nullptr), e2(nullptr), e3(nullptr), e4(&e2) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: member initializer for 'e1' is redundant
+  // CHECK-MESSAGES: :[[@LINE-2]]:40: warning: member initializer for 'e2' is redundant
+  // CHECK-MESSAGES: :[[@LINE-3]]:53: warning: member initializer for 'e3' is redundant
+  // CHECK-FIXES: ExistingPointer(long) :  e4(&e2) {}
+  int *e1{};
+  int *e2 = 0;
+  int *e3 = nullptr;
+  int **e4 = &e1;
+};
+
+struct ExistingString {
+  ExistingString(short) : e1(), e2(), e3(), e4() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: member initializer for 'e1' is redundant [modernize-use-default-member-init]
+  // CHECK-MESSAGES: :[[@LINE-2]]:33: warning: member initializer for 'e2' is redundant
+  // CHECK-FIXES: ExistingString(short) :  e3(), e4() {}
+  ExistingString(int) : e1(0), e2(0), e3(0), e4(0) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: member initializer for 'e1' is redundant
+  // CHECK-MESSAGES: :[[@LINE-2]]:32: warning: member initializer for 'e2' is redundant
+  // CHECK-FIXES: ExistingString(int) :  e3(0), e4(0) {}
+  ExistingString(long) : e1(nullptr), e2(nullptr), e3(nullptr), e4(nullptr) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: member initializer for 'e1' is redundant
+  // CHECK-MESSAGES: :[[@LINE-2]]:39: warning: member initializer for 'e2' is redundant
+  // CHECK-FIXES: ExistingString(long) :  e3(nullptr), e4(nullptr) {}
+  ExistingString(char) : e1("foo"), e2("foo"), e3("foo"), e4("foo") {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:48: warning: member initializer for 'e3' is redundant
+  // CHECK-FIXES: ExistingString(char) : e1("foo"), e2("foo"),  e4("foo") {}
+  const char *e1{};
+  const char *e2 = nullptr;
+  const char *e3 = "foo";
+  const char *e4 = "bar";
+};
+
+template <typename T>
+struct NegativeTemplateExisting {
+  NegativeTemplateExisting(int) : t(0) {}
+  T t{};
+};
+
+NegativeTemplateExisting<int> ntei(0);
+NegativeTemplateExisting<double> nted(0);
+
+// This resulted in a warning by default.
+#define MACRO() \
+  struct MacroS { \
+    void *P; \
+    MacroS() : P(nullptr) {} \
+  };
+
+MACRO();
diff --git a/test/clang-tidy/modernize-use-emplace.cpp b/test/clang-tidy/modernize-use-emplace.cpp
new file mode 100644 (file)
index 0000000..c03d024
--- /dev/null
@@ -0,0 +1,607 @@
+// RUN: %check_clang_tidy %s modernize-use-emplace %t -- \
+// RUN:   -config="{CheckOptions: \
+// RUN:             [{key: modernize-use-emplace.ContainersWithPushBack, \
+// RUN:               value: '::std::vector; ::std::list; ::std::deque; llvm::LikeASmallVector'}, \
+// RUN:              {key: modernize-use-emplace.TupleTypes, \
+// RUN:               value: '::std::pair; std::tuple; ::test::Single'}, \
+// RUN:              {key: modernize-use-emplace.TupleMakeFunctions, \
+// RUN:               value: '::std::make_pair; ::std::make_tuple; ::test::MakeSingle'}] \
+// RUN:             }" -- -std=c++11
+
+namespace std {
+template <typename>
+class initializer_list
+{
+public:
+  initializer_list() noexcept {}
+};
+
+template <typename T>
+class vector {
+public:
+  vector() = default;
+  vector(initializer_list<T>) {}
+
+  void push_back(const T &) {}
+  void push_back(T &&) {}
+
+  template <typename... Args>
+  void emplace_back(Args &&... args){};
+  ~vector();
+};
+template <typename T>
+class list {
+public:
+  void push_back(const T &) {}
+  void push_back(T &&) {}
+
+  template <typename... Args>
+  void emplace_back(Args &&... args){};
+  ~list();
+};
+
+template <typename T>
+class deque {
+public:
+  void push_back(const T &) {}
+  void push_back(T &&) {}
+
+  template <typename... Args>
+  void emplace_back(Args &&... args){};
+  ~deque();
+};
+
+template <typename T> struct remove_reference { using type = T; };
+template <typename T> struct remove_reference<T &> { using type = T; };
+template <typename T> struct remove_reference<T &&> { using type = T; };
+
+template <typename T1, typename T2> class pair {
+public:
+  pair() = default;
+  pair(const pair &) = default;
+  pair(pair &&) = default;
+
+  pair(const T1 &, const T2 &) {}
+  pair(T1 &&, T2 &&) {}
+
+  template <typename U1, typename U2> pair(const pair<U1, U2> &){};
+  template <typename U1, typename U2> pair(pair<U1, U2> &&){};
+};
+
+template <typename T1, typename T2>
+pair<typename remove_reference<T1>::type, typename remove_reference<T2>::type>
+make_pair(T1 &&, T2 &&) {
+  return {};
+};
+
+template <typename... Ts> class tuple {
+public:
+  tuple() = default;
+  tuple(const tuple &) = default;
+  tuple(tuple &&) = default;
+
+  tuple(const Ts &...) {}
+  tuple(Ts &&...) {}
+
+  template <typename... Us> tuple(const tuple<Us...> &){};
+  template <typename... Us> tuple(tuple<Us...> &&) {}
+
+  template <typename U1, typename U2> tuple(const pair<U1, U2> &) {
+    static_assert(sizeof...(Ts) == 2, "Wrong tuple size");
+  };
+  template <typename U1, typename U2> tuple(pair<U1, U2> &&) {
+    static_assert(sizeof...(Ts) == 2, "Wrong tuple size");
+  };
+};
+
+template <typename... Ts>
+tuple<typename remove_reference<Ts>::type...> make_tuple(Ts &&...) {
+  return {};
+}
+
+template <typename T>
+class unique_ptr {
+public:
+  explicit unique_ptr(T *) {}
+  ~unique_ptr();
+};
+} // namespace std
+
+namespace llvm {
+template <typename T>
+class LikeASmallVector {
+public:
+  void push_back(const T &) {}
+  void push_back(T &&) {}
+
+  template <typename... Args>
+  void emplace_back(Args &&... args){};
+};
+
+} // llvm
+
+void testInts() {
+  std::vector<int> v;
+  v.push_back(42);
+  v.push_back(int(42));
+  v.push_back(int{42});
+  v.push_back(42.0);
+  int z;
+  v.push_back(z);
+}
+
+struct Something {
+  Something(int a, int b = 41) {}
+  Something() {}
+  void push_back(Something);
+  int getInt() { return 42; }
+};
+
+struct Convertable {
+  operator Something() { return Something{}; }
+};
+
+struct Zoz {
+  Zoz(Something, int = 42) {}
+};
+
+Zoz getZoz(Something s) { return Zoz(s); }
+
+void test_Something() {
+  std::vector<Something> v;
+
+  v.push_back(Something(1, 2));
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back instead of push_back [modernize-use-emplace]
+  // CHECK-FIXES: v.emplace_back(1, 2);
+
+  v.push_back(Something{1, 2});
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+  // CHECK-FIXES: v.emplace_back(1, 2);
+
+  v.push_back(Something());
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+  // CHECK-FIXES: v.emplace_back();
+
+  v.push_back(Something{});
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+  // CHECK-FIXES: v.emplace_back();
+
+  Something Different;
+  v.push_back(Something(Different.getInt(), 42));
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+  // CHECK-FIXES: v.emplace_back(Different.getInt(), 42);
+
+  v.push_back(Different.getInt());
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+  // CHECK-FIXES: v.emplace_back(Different.getInt());
+
+  v.push_back(42);
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+  // CHECK-FIXES: v.emplace_back(42);
+
+  Something temporary(42, 42);
+  temporary.push_back(temporary);
+  v.push_back(temporary);
+
+  v.push_back(Convertable());
+  v.push_back(Convertable{});
+  Convertable s;
+  v.push_back(s);
+}
+
+template <typename ElemType>
+void dependOnElem() {
+  std::vector<ElemType> v;
+  v.push_back(ElemType(42));
+}
+
+template <typename ContainerType>
+void dependOnContainer() {
+  ContainerType v;
+  v.push_back(Something(42));
+}
+
+void callDependent() {
+  dependOnElem<Something>();
+  dependOnContainer<std::vector<Something>>();
+}
+
+void test2() {
+  std::vector<Zoz> v;
+  v.push_back(Zoz(Something(21, 37)));
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+  // CHECK-FIXES: v.emplace_back(Something(21, 37));
+
+  v.push_back(Zoz(Something(21, 37), 42));
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+  // CHECK-FIXES: v.emplace_back(Something(21, 37), 42);
+
+  v.push_back(getZoz(Something(1, 2)));
+}
+
+struct GetPair {
+  std::pair<int, long> getPair();
+};
+void testPair() {
+  std::vector<std::pair<int, int>> v;
+  v.push_back(std::pair<int, int>(1, 2));
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+  // CHECK-FIXES: v.emplace_back(1, 2);
+
+  GetPair g;
+  v.push_back(g.getPair());
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+  // CHECK-FIXES: v.emplace_back(g.getPair());
+
+  std::vector<std::pair<Something, Zoz>> v2;
+  v2.push_back(std::pair<Something, Zoz>(Something(42, 42), Zoz(Something(21, 37))));
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: use emplace_back
+  // CHECK-FIXES: v2.emplace_back(Something(42, 42), Zoz(Something(21, 37)));
+}
+
+void testTuple() {
+  std::vector<std::tuple<bool, char, int>> v;
+  v.push_back(std::tuple<bool, char, int>(false, 'x', 1));
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+  // CHECK-FIXES: v.emplace_back(false, 'x', 1);
+
+  v.push_back(std::tuple<bool, char, int>{false, 'y', 2});
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+  // CHECK-FIXES: v.emplace_back(false, 'y', 2);
+
+  v.push_back({true, 'z', 3});
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+  // CHECK-FIXES: v.emplace_back(true, 'z', 3);
+
+  std::vector<std::tuple<int, bool>> x;
+  x.push_back(std::make_pair(1, false));
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+  // CHECK-FIXES: x.emplace_back(1, false);
+
+  x.push_back(std::make_pair(2LL, 1));
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+  // CHECK-FIXES: x.emplace_back(2LL, 1);
+}
+
+struct Base {
+  Base(int, int *, int = 42);
+};
+
+struct Derived : Base {
+  Derived(int *, Something) : Base(42, nullptr) {}
+};
+
+void testDerived() {
+  std::vector<Base> v;
+  v.push_back(Derived(nullptr, Something{}));
+}
+
+void testNewExpr() {
+  std::vector<Derived> v;
+  v.push_back(Derived(new int, Something{}));
+}
+
+void testSpaces() {
+  std::vector<Something> v;
+
+  // clang-format off
+
+  v.push_back(Something(1, //arg1
+                2 // arg2
+               ) // Something
+              );
+  // CHECK-MESSAGES: :[[@LINE-4]]:5: warning: use emplace_back
+  // CHECK-FIXES: v.emplace_back(1, //arg1
+  // CHECK-FIXES:                2 // arg2
+  // CHECK-FIXES:                  // Something
+  // CHECK-FIXES:                );
+
+  v.push_back(    Something   (1, 2)    );
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+  // CHECK-FIXES: v.emplace_back(1, 2   );
+
+  v.push_back(    Something   {1, 2}    );
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+  // CHECK-FIXES: v.emplace_back(1, 2   );
+
+  v.push_back(  Something {}    );
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+  // CHECK-FIXES: v.emplace_back(   );
+
+  v.push_back(
+             Something(1, 2)    );
+  // CHECK-MESSAGES: :[[@LINE-2]]:5: warning: use emplace_back
+  // CHECK-FIXES: v.emplace_back(1, 2   );
+
+  std::vector<Base> v2;
+  v2.push_back(
+    Base(42, nullptr));
+  // CHECK-MESSAGES: :[[@LINE-2]]:6: warning: use emplace_back
+  // CHECK-FIXES: v2.emplace_back(42, nullptr);
+
+  // clang-format on
+}
+
+void testPointers() {
+  std::vector<int *> v;
+  v.push_back(new int(5));
+
+  std::vector<std::unique_ptr<int>> v2;
+  v2.push_back(std::unique_ptr<int>(new int(42)));
+  // This call can't be replaced with emplace_back.
+  // If emplacement will fail (not enough memory to add to vector)
+  // we will have leak of int because unique_ptr won't be constructed
+  // (and destructed) as in push_back case.
+
+  auto *ptr = new int;
+  v2.push_back(std::unique_ptr<int>(ptr));
+  // Same here
+}
+
+void testMakePair() {
+  std::vector<std::pair<int, int>> v;
+  v.push_back(std::make_pair(1, 2));
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+  // CHECK-FIXES: v.emplace_back(1, 2);
+
+  v.push_back(std::make_pair(42LL, 13));
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+  // CHECK-FIXES: v.emplace_back(42LL, 13);
+
+  v.push_back(std::make_pair<char, char>(0, 3));
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+  // CHECK-FIXES: v.emplace_back(std::make_pair<char, char>(0, 3));
+  //
+  // Even though the call above could be turned into v.emplace_back(0, 3),
+  // we don't eliminate the make_pair call here, because of the explicit
+  // template parameters provided. make_pair's arguments can be convertible
+  // to its explicitly provided template parameter, but not to the pair's
+  // element type. The examples below illustrate the problem.
+  struct D {
+    D(...) {}
+    operator char() const { return 0; }
+  };
+  v.push_back(std::make_pair<D, int>(Something(), 2));
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+  // CHECK-FIXES: v.emplace_back(std::make_pair<D, int>(Something(), 2));
+
+  struct X {
+    X(std::pair<int, int>) {}
+  };
+  std::vector<X> x;
+  x.push_back(std::make_pair(1, 2));
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+  // CHECK-FIXES: x.emplace_back(std::make_pair(1, 2));
+  // make_pair cannot be removed here, as X is not constructible with two ints.
+
+  struct Y {
+    Y(std::pair<int, int>&&) {}
+  };
+  std::vector<Y> y;
+  y.push_back(std::make_pair(2, 3));
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+  // CHECK-FIXES: y.emplace_back(std::make_pair(2, 3));
+  // make_pair cannot be removed here, as Y is not constructible with two ints.
+}
+
+void testMakeTuple() {
+  std::vector<std::tuple<int, bool, char>> v;
+  v.push_back(std::make_tuple(1, true, 'v'));
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+  // CHECK-FIXES: v.emplace_back(1, true, 'v');
+
+  v.push_back(std::make_tuple(2ULL, 1, 0));
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+  // CHECK-FIXES: v.emplace_back(2ULL, 1, 0);
+
+  v.push_back(std::make_tuple<long long, int, int>(3LL, 1, 0));
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+  // CHECK-FIXES: v.emplace_back(std::make_tuple<long long, int, int>(3LL, 1, 0));
+  // make_tuple is not removed when there are explicit template
+  // arguments provided.
+}
+
+namespace test {
+template <typename T> struct Single {
+  Single() = default;
+  Single(const Single &) = default;
+  Single(Single &&) = default;
+
+  Single(const T &) {}
+  Single(T &&) {}
+
+  template <typename U> Single(const Single<U> &) {}
+  template <typename U> Single(Single<U> &&) {}
+
+  template <typename U> Single(const std::tuple<U> &) {}
+  template <typename U> Single(std::tuple<U> &&) {}
+};
+
+template <typename T>
+Single<typename std::remove_reference<T>::type> MakeSingle(T &&) {
+  return {};
+}
+} // namespace test
+
+void testOtherTuples() {
+  std::vector<test::Single<int>> v;
+  v.push_back(test::Single<int>(1));
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+  // CHECK-FIXES: v.emplace_back(1);
+
+  v.push_back({2});
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+  // CHECK-FIXES: v.emplace_back(2);
+
+  v.push_back(test::MakeSingle(3));
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+  // CHECK-FIXES: v.emplace_back(3);
+
+  v.push_back(test::MakeSingle<long long>(4));
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+  // CHECK-FIXES: v.emplace_back(test::MakeSingle<long long>(4));
+  // We don't remove make functions with explicit template parameters.
+
+  v.push_back(test::MakeSingle(5LL));
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+  // CHECK-FIXES: v.emplace_back(5LL);
+
+  v.push_back(std::make_tuple(6));
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+  // CHECK-FIXES: v.emplace_back(6);
+
+  v.push_back(std::make_tuple(7LL));
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+  // CHECK-FIXES: v.emplace_back(7LL);
+}
+
+void testOtherContainers() {
+  std::list<Something> l;
+  l.push_back(Something(42, 41));
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+  // CHECK-FIXES: l.emplace_back(42, 41);
+
+  std::deque<Something> d;
+  d.push_back(Something(42));
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+  // CHECK-FIXES: d.emplace_back(42);
+
+  llvm::LikeASmallVector<Something> ls;
+  ls.push_back(Something(42));
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: use emplace_back
+  // CHECK-FIXES: ls.emplace_back(42);
+}
+
+class IntWrapper {
+public:
+  IntWrapper(int x) : value(x) {}
+  IntWrapper operator+(const IntWrapper other) const {
+    return IntWrapper(value + other.value);
+  }
+
+private:
+  int value;
+};
+
+void testMultipleOpsInPushBack() {
+  std::vector<IntWrapper> v;
+  v.push_back(IntWrapper(42) + IntWrapper(27));
+}
+
+// Macro tests.
+#define PUSH_BACK_WHOLE(c, x) c.push_back(x)
+#define PUSH_BACK_NAME push_back
+#define PUSH_BACK_ARG(x) (x)
+#define SOME_OBJ Something(10)
+#define MILLION 3
+#define SOME_WEIRD_PUSH(v) v.push_back(Something(
+#define OPEN (
+#define CLOSE )
+void macroTest() {
+  std::vector<Something> v;
+  Something s;
+
+  PUSH_BACK_WHOLE(v, Something(5, 6));
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use emplace_back
+
+  v.PUSH_BACK_NAME(Something(5));
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+
+  v.push_back PUSH_BACK_ARG(Something(5, 6));
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+
+  v.push_back(SOME_OBJ);
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+
+  v.push_back(Something(MILLION));
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+  // CHECK-FIXES: v.emplace_back(MILLION);
+
+  // clang-format off
+  v.push_back(  Something OPEN 3 CLOSE  );
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+  // clang-format on
+  PUSH_BACK_WHOLE(s, Something(1));
+}
+
+struct A {
+  int value1, value2;
+};
+
+struct B {
+  B(A) {}
+};
+
+struct C {
+  int value1, value2, value3;
+};
+
+void testAggregation() {
+  // This should not be noticed or fixed; after the correction, the code won't
+  // compile.
+
+  std::vector<A> v;
+  v.push_back(A({1, 2}));
+
+  std::vector<B> vb;
+  vb.push_back(B({10, 42}));
+}
+
+struct Bitfield {
+  unsigned bitfield : 1;
+  unsigned notBitfield;
+};
+
+void testBitfields() {
+  std::vector<Something> v;
+  Bitfield b;
+  v.push_back(Something(42, b.bitfield));
+  v.push_back(Something(b.bitfield));
+
+  v.push_back(Something(42, b.notBitfield));
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+  // CHECK-FIXES: v.emplace_back(42, b.notBitfield);
+  int var;
+  v.push_back(Something(42, var));
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+  // CHECK-FIXES: v.emplace_back(42, var);
+}
+
+class PrivateCtor {
+  PrivateCtor(int z);
+
+public:
+  void doStuff() {
+    std::vector<PrivateCtor> v;
+    // This should not change it because emplace back doesn't have permission.
+    // Check currently doesn't support friend declarations because pretty much
+    // nobody would want to be friend with std::vector :(.
+    v.push_back(PrivateCtor(42));
+  }
+};
+
+struct WithDtor {
+  WithDtor(int) {}
+  ~WithDtor();
+};
+
+void testWithDtor() {
+  std::vector<WithDtor> v;
+
+  v.push_back(WithDtor(42));
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+  // CHECK-FIXES: v.emplace_back(42);
+}
+
+void testInitializerList() {
+  std::vector<std::vector<int>> v;
+  v.push_back(std::vector<int>({1}));
+  // Test against the bug reported in PR32896.
+
+  v.push_back({{2}});
+
+  using PairIntVector = std::pair<int, std::vector<int>>;
+  std::vector<PairIntVector> x;
+  x.push_back(PairIntVector(3, {4}));
+  x.push_back({5, {6}});
+}
diff --git a/test/clang-tidy/modernize-use-equals-default-copy.cpp b/test/clang-tidy/modernize-use-equals-default-copy.cpp
new file mode 100644 (file)
index 0000000..df21cc0
--- /dev/null
@@ -0,0 +1,497 @@
+// RUN: %check_clang_tidy %s modernize-use-equals-default %t -- -- -std=c++11 -fno-delayed-template-parsing -fexceptions
+
+// Out of line definition.
+struct OL {
+  OL(const OL &);
+  OL &operator=(const OL &);
+  int Field;
+};
+OL::OL(const OL &Other) : Field(Other.Field) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use '= default' to define a trivial copy constructor [modernize-use-equals-default]
+// CHECK-FIXES: OL::OL(const OL &Other)  = default;
+OL &OL::operator=(const OL &Other) {
+  Field = Other.Field;
+  return *this;
+}
+// CHECK-MESSAGES: :[[@LINE-4]]:9: warning: use '= default' to define a trivial copy-assignment operator [modernize-use-equals-default]
+// CHECK-FIXES: OL &OL::operator=(const OL &Other) = default;
+
+// Inline.
+struct IL {
+  IL(const IL &Other) : Field(Other.Field) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default'
+  // CHECK-FIXES: IL(const IL &Other)  = default;
+  IL &operator=(const IL &Other) {
+    Field = Other.Field;
+    return *this;
+  }
+  // CHECK-MESSAGES: :[[@LINE-4]]:7: warning: use '= default'
+  // CHECK-FIXES: IL &operator=(const IL &Other) = default;
+  int Field;
+};
+
+// Wrong type.
+struct WT {
+  WT(const IL &Other) {}
+  WT &operator=(const IL &);
+};
+WT &WT::operator=(const IL &Other) { return *this; }
+
+// Qualifiers.
+struct Qual {
+  Qual(const Qual &Other) : Field(Other.Field), Volatile(Other.Volatile),
+                            Mutable(Other.Mutable), Reference(Other.Reference),
+                            Const(Other.Const) {}
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use '= default'
+  // CHECK-FIXES: Qual(const Qual &Other)
+  // CHECK-FIXES:                          = default;
+
+  int Field;
+  volatile char Volatile;
+  mutable bool Mutable;
+  const OL &Reference; // This makes this class non-assignable.
+  const IL Const;      // This also makes this class non-assignable.
+  static int Static;
+};
+
+// Wrong init arguments.
+struct WI {
+  WI(const WI &Other) : Field1(Other.Field1), Field2(Other.Field1) {}
+  WI &operator=(const WI &);
+  int Field1, Field2;
+};
+WI &WI::operator=(const WI &Other) {
+  Field1 = Other.Field1;
+  Field2 = Other.Field1;
+  return *this;
+}
+
+// Missing field.
+struct MF {
+  MF(const MF &Other) : Field1(Other.Field1), Field2(Other.Field2) {}
+  MF &operator=(const MF &);
+  int Field1, Field2, Field3;
+};
+MF &MF::operator=(const MF &Other) {
+  Field1 = Other.Field1;
+  Field2 = Other.Field2;
+  return *this;
+}
+
+struct Comments {
+  Comments(const Comments &Other)
+      /* don't delete */ : /* this comment */ Field(Other.Field) {}
+  // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use '= default'
+  // CHECK-FIXES: /* don't delete */  = default;
+  int Field;
+};
+
+struct MoreComments {
+  MoreComments(const MoreComments &Other) /* this comment is OK */
+      : Field(Other.Field) {}
+  // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use '= default'
+  // CHECK-FIXES: MoreComments(const MoreComments &Other) /* this comment is OK */
+  // CHECK-FIXES-NEXT: = default;
+  int Field;
+};
+
+struct ColonInComment {
+  ColonInComment(const ColonInComment &Other) /* : */ : Field(Other.Field) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default'
+  // CHECK-FIXES: ColonInComment(const ColonInComment &Other) /* : */  = default;
+  int Field;
+};
+
+// No members or bases (in particular, no colon).
+struct Empty {
+  Empty(const Empty &Other) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default'
+  // CHECK-FIXES: Empty(const Empty &Other) = default;
+  Empty &operator=(const Empty &);
+};
+Empty &Empty::operator=(const Empty &Other) { return *this; }
+// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: use '= default'
+// CHECK-FIXES: Empty &Empty::operator=(const Empty &Other) = default;
+
+// Bit fields.
+struct BF {
+  BF() = default;
+  BF(const BF &Other) : Field1(Other.Field1), Field2(Other.Field2), Field3(Other.Field3),
+                        Field4(Other.Field4) {}
+  // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use '= default'
+  // CHECK-FIXES: BF(const BF &Other) {{$}}
+  // CHECK-FIXES:                     = default;
+  BF &operator=(const BF &);
+
+  unsigned Field1 : 3;
+  int : 7;
+  char Field2 : 6;
+  int : 0;
+  int Field3 : 24;
+  unsigned char Field4;
+};
+BF &BF::operator=(const BF &Other) {
+  Field1 = Other.Field1;
+  Field2 = Other.Field2;
+  Field3 = Other.Field3;
+  Field4 = Other.Field4;
+  return *this;
+}
+// CHECK-MESSAGES: :[[@LINE-7]]:9: warning: use '= default'
+// CHECK-FIXES: BF &BF::operator=(const BF &Other) = default;
+
+// Base classes.
+struct BC : IL, OL, BF {
+  BC(const BC &Other) : IL(Other), OL(Other), BF(Other) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default'
+  // CHECK-FIXES: BC(const BC &Other)  = default;
+  BC &operator=(const BC &Other);
+};
+BC &BC::operator=(const BC &Other) {
+  IL::operator=(Other);
+  OL::operator=(Other);
+  BF::operator=(Other);
+  return *this;
+}
+// CHECK-MESSAGES: :[[@LINE-6]]:9: warning: use '= default'
+// CHECK-FIXES: BC &BC::operator=(const BC &Other) = default;
+
+// Base classes with member.
+struct BCWM : IL, OL {
+  BCWM(const BCWM &Other) : IL(Other), OL(Other), Bf(Other.Bf) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default'
+  // CHECK-FIXES: BCWM(const BCWM &Other)  = default;
+  BCWM &operator=(const BCWM &);
+  BF Bf;
+};
+BCWM &BCWM::operator=(const BCWM &Other) {
+  IL::operator=(Other);
+  OL::operator=(Other);
+  Bf = Other.Bf;
+  return *this;
+}
+// CHECK-MESSAGES: :[[@LINE-6]]:13: warning: use '= default'
+// CHECK-FIXES: BCWM &BCWM::operator=(const BCWM &Other) = default;
+
+// Missing base class.
+struct MBC : IL, OL, BF {
+  MBC(const MBC &Other) : IL(Other), OL(Other) {}
+  MBC &operator=(const MBC &);
+};
+MBC &MBC::operator=(const MBC &Other) {
+  IL::operator=(Other);
+  OL::operator=(Other);
+  return *this;
+}
+
+// Base classes, incorrect parameter.
+struct BCIP : BCWM, BF {
+  BCIP(const BCIP &Other) : BCWM(Other), BF(Other.Bf) {}
+  BCIP &operator=(const BCIP &);
+};
+BCIP &BCIP::operator=(const BCIP &Other) {
+  BCWM::operator=(Other);
+  BF::operator=(Other.Bf);
+  return *this;
+}
+
+// Virtual base classes.
+struct VA : virtual OL {};
+struct VB : virtual OL {};
+struct VBC : VA, VB, virtual OL {
+  // OL is the first thing that is going to be initialized, despite the fact
+  // that it is the last in the list of bases, because it is virtual and there
+  // is a virtual OL at the beginning of VA (which is the same).
+  VBC(const VBC &Other) : OL(Other), VA(Other), VB(Other) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default'
+  // CHECK-FIXES: VBC(const VBC &Other)  = default;
+  VBC &operator=(const VBC &Other);
+};
+VBC &VBC::operator=(const VBC &Other) {
+  OL::operator=(Other);
+  VA::operator=(Other);
+  VB::operator=(Other);
+  return *this;
+}
+// CHECK-MESSAGES: :[[@LINE-6]]:11: warning: use '= default'
+// CHECK-FIXES: VBC &VBC::operator=(const VBC &Other) = default;
+
+// Indirect base.
+struct IB : VBC {
+  IB(const IB &Other) : OL(Other), VBC(Other) {}
+  IB &operator=(const IB &);
+};
+IB &IB::operator=(const IB &Other) {
+  OL::operator=(Other);
+  VBC::operator=(Other);
+  return *this;
+}
+
+// Class template.
+template <class T>
+struct Template {
+  Template() = default;
+  Template(const Template &Other) : Field(Other.Field) {}
+  Template &operator=(const Template &Other);
+  void foo(const T &t);
+  int Field;
+};
+template <class T>
+Template<T> &Template<T>::operator=(const Template<T> &Other) {
+  Field = Other.Field;
+  return *this;
+}
+Template<int> T1;
+
+// Dependent types.
+template <class T>
+struct DT1 {
+  DT1() = default;
+  DT1(const DT1 &Other) : Field(Other.Field) {}
+  DT1 &operator=(const DT1 &);
+  T Field;
+};
+template <class T>
+DT1<T> &DT1<T>::operator=(const DT1<T> &Other) {
+  Field = Other.Field;
+  return *this;
+}
+DT1<int> Dt1;
+
+template <class T>
+struct DT2 {
+  DT2() = default;
+  DT2(const DT2 &Other) : Field(Other.Field), Dependent(Other.Dependent) {}
+  DT2 &operator=(const DT2 &);
+  T Field;
+  typename T::TT Dependent;
+};
+template <class T>
+DT2<T> &DT2<T>::operator=(const DT2<T> &Other) {
+  Field = Other.Field;
+  Dependent = Other.Dependent;
+  return *this;
+}
+struct T {
+  typedef int TT;
+};
+DT2<T> Dt2;
+
+// Default arguments.
+struct DA {
+  DA(int Int);
+  DA(const DA &Other = DA(0)) : Field1(Other.Field1), Field2(Other.Field2) {}
+  DA &operator=(const DA &);
+  int Field1;
+  char Field2;
+};
+// Overloaded operator= cannot have a default argument.
+DA &DA::operator=(const DA &Other) {
+  Field1 = Other.Field1;
+  Field2 = Other.Field2;
+  return *this;
+}
+// CHECK-MESSAGES: :[[@LINE-5]]:9: warning: use '= default'
+// CHECK-FIXES: DA &DA::operator=(const DA &Other) = default;
+
+struct DA2 {
+  // Can be used as copy-constructor but cannot be explicitly defaulted.
+  DA2(const DA &Other, int Def = 0) {}
+};
+
+// Default initialization.
+struct DI {
+  DI(const DI &Other) : Field1(Other.Field1), Field2(Other.Field2) {}
+  int Field1;
+  int Field2 = 0;
+  int Fiedl3;
+};
+
+// Statement inside body.
+void foo();
+struct SIB {
+  SIB(const SIB &Other) : Field(Other.Field) { foo(); }
+  SIB &operator=(const SIB &);
+  int Field;
+};
+SIB &SIB::operator=(const SIB &Other) {
+  Field = Other.Field;
+  foo();
+  return *this;
+}
+
+// Comment inside body.
+struct CIB {
+  CIB(const CIB &Other) : Field(Other.Field) { /* Don't erase this */
+  }
+  // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use '= default'
+  CIB &operator=(const CIB &);
+  int Field;
+};
+CIB &CIB::operator=(const CIB &Other) {
+  Field = Other.Field;
+  // FIXME: don't erase this comment.
+  return *this;
+}
+// CHECK-MESSAGES: :[[@LINE-5]]:11: warning: use '= default'
+// CHECK-FIXES: CIB &CIB::operator=(const CIB &Other) = default;
+
+// Take non-const reference as argument.
+struct NCRef {
+  NCRef(NCRef &Other) : Field1(Other.Field1), Field2(Other.Field2) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default'
+  // CHECK-FIXES: NCRef(NCRef &Other)  = default;
+  NCRef &operator=(NCRef &);
+  int Field1, Field2;
+};
+NCRef &NCRef::operator=(NCRef &Other) {
+  Field1 = Other.Field1;
+  Field2 = Other.Field2;
+  return *this;
+}
+// CHECK-MESSAGES: :[[@LINE-5]]:15: warning: use '= default'
+// CHECK-FIXES: NCRef &NCRef::operator=(NCRef &Other) = default;
+
+// Already defaulted.
+struct IAD {
+  IAD(const IAD &Other) = default;
+  IAD &operator=(const IAD &Other) = default;
+};
+
+struct OAD {
+  OAD(const OAD &Other);
+  OAD &operator=(const OAD &);
+};
+OAD::OAD(const OAD &Other) = default;
+OAD &OAD::operator=(const OAD &Other) = default;
+
+// Deleted.
+struct ID {
+  ID(const ID &Other) = delete;
+  ID &operator=(const ID &Other) = delete;
+};
+
+// Non-reference parameter.
+struct NRef {
+  NRef &operator=(NRef Other);
+  int Field1;
+};
+NRef &NRef::operator=(NRef Other) {
+  Field1 = Other.Field1;
+  return *this;
+}
+
+// RValue reference parameter.
+struct RVR {
+  RVR(RVR &&Other) {}
+  RVR &operator=(RVR &&);
+};
+RVR &RVR::operator=(RVR &&Other) { return *this; }
+
+// Similar function.
+struct SF {
+  SF &foo(const SF &);
+  int Field1;
+};
+SF &SF::foo(const SF &Other) {
+  Field1 = Other.Field1;
+  return *this;
+}
+
+// No return.
+struct NR {
+  NR &operator=(const NR &);
+};
+NR &NR::operator=(const NR &Other) {}
+
+// Return misplaced.
+struct RM {
+  RM &operator=(const RM &);
+  int Field;
+};
+RM &RM::operator=(const RM &Other) {
+  return *this;
+  Field = Other.Field;
+}
+
+// Wrong return value.
+struct WRV {
+  WRV &operator=(WRV &);
+};
+WRV &WRV::operator=(WRV &Other) {
+  return Other;
+}
+
+// Wrong return type.
+struct WRT : IL {
+  IL &operator=(const WRT &);
+};
+IL &WRT::operator=(const WRT &Other) {
+  return *this;
+}
+
+// Try-catch.
+struct ITC {
+  ITC(const ITC &Other)
+  try : Field(Other.Field) {
+  } catch (...) {
+  }
+  ITC &operator=(const ITC &Other) try {
+    Field = Other.Field;
+  } catch (...) {
+  }
+  int Field;
+};
+
+struct OTC {
+  OTC(const OTC &);
+  OTC &operator=(const OTC &);
+  int Field;
+};
+OTC::OTC(const OTC &Other) try : Field(Other.Field) {
+} catch (...) {
+}
+OTC &OTC::operator=(const OTC &Other) try {
+  Field = Other.Field;
+} catch (...) {
+}
+
+// FIXME: the check is not able to detect exception specification.
+// noexcept(true).
+struct NET {
+  // This is the default.
+  //NET(const NET &Other) noexcept {}
+  NET &operator=(const NET &Other) noexcept;
+};
+//NET &NET::operator=(const NET &Other) noexcept { return *this; }
+
+// noexcept(false).
+struct NEF {
+  // This is the default.
+  //NEF(const NEF &Other) noexcept(false) {}
+  NEF &operator=(const NEF &Other) noexcept(false);
+};
+//NEF &NEF::operator=(const NEF &Other) noexcept(false) { return *this; }
+
+#define STRUCT_WITH_COPY_CONSTRUCT(_base, _type) \
+  struct _type {                                 \
+    _type(const _type &v) : value(v.value) {}    \
+    _base value;                                 \
+  };
+
+STRUCT_WITH_COPY_CONSTRUCT(unsigned char, Hex8CopyConstruct)
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use '= default' to define a trivial copy constructor
+// CHECK-MESSAGES: :[[@LINE-6]]:44: note:
+
+#define STRUCT_WITH_COPY_ASSIGN(_base, _type) \
+  struct _type {                              \
+    _type &operator=(const _type &rhs) {      \
+      value = rhs.value;                      \
+      return *this;                           \
+    }                                         \
+    _base value;                              \
+  };
+
+STRUCT_WITH_COPY_ASSIGN(unsigned char, Hex8CopyAssign)
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use '= default' to define a trivial copy-assignment operator
+// CHECK-MESSAGES: :[[@LINE-9]]:40: note:
diff --git a/test/clang-tidy/modernize-use-equals-default-delayed.cpp b/test/clang-tidy/modernize-use-equals-default-delayed.cpp
new file mode 100644 (file)
index 0000000..953d88a
--- /dev/null
@@ -0,0 +1,8 @@
+// RUN: clang-tidy %s -checks=-*,modernize-use-equals-default -- -std=c++11 -fdelayed-template-parsing -fexceptions | count 0
+// Note: this test expects no diagnostics, but FileCheck cannot handle that,
+// hence the use of | count 0.
+
+template <typename Ty>
+struct S {
+  S<Ty>& operator=(const S<Ty>&) { return *this; }
+};
diff --git a/test/clang-tidy/modernize-use-equals-default.cpp b/test/clang-tidy/modernize-use-equals-default.cpp
new file mode 100644 (file)
index 0000000..4892e58
--- /dev/null
@@ -0,0 +1,209 @@
+// RUN: %check_clang_tidy %s modernize-use-equals-default %t -- -- -std=c++11 -fno-delayed-template-parsing  -fexceptions
+
+// Out of line definition.
+class OL {
+public:
+  OL();
+  ~OL();
+};
+
+OL::OL() {}
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use '= default' to define a trivial default constructor [modernize-use-equals-default]
+// CHECK-FIXES: OL::OL() = default;
+OL::~OL() {}
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use '= default' to define a trivial destructor [modernize-use-equals-default]
+// CHECK-FIXES: OL::~OL() = default;
+
+// Inline definitions.
+class IL {
+public:
+  IL() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default'
+  // CHECK-FIXES: IL() = default;
+  ~IL() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default'
+  // CHECK-FIXES: ~IL() = default;
+};
+
+// Non-empty body.
+void f();
+class NE {
+public:
+  NE() { f(); }
+  ~NE() { f(); }
+};
+
+// Initializer or arguments.
+class IA {
+public:
+  // Constructor with initializer.
+  IA() : Field(5) {}
+  // Constructor with arguments.
+  IA(int Arg1, int Arg2) {}
+  int Field;
+};
+
+// Default member initializer
+class DMI {
+public:
+  DMI() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default'
+  // CHECK-FIXES: DMI() = default;
+  int Field = 5;
+};
+
+// Class member
+class CM {
+public:
+  CM() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default'
+  // CHECK-FIXES: CM() = default;
+  OL o;
+};
+
+// Private constructor/destructor.
+class Priv {
+  Priv() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default'
+  // CHECK-FIXES: Priv() = default;
+  ~Priv() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default'
+  // CHECK-FIXES: ~Priv() = default;
+};
+
+// struct.
+struct ST {
+  ST() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default'
+  // CHECK-FIXES: ST() = default;
+  ~ST() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default'
+  // CHECK-FIXES: ST() = default;
+};
+
+// Deleted constructor/destructor.
+class Del {
+public:
+  Del() = delete;
+  ~Del() = delete;
+};
+
+// Do not remove other keywords.
+class KW {
+public:
+  explicit KW() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use '= default'
+  // CHECK-FIXES: explicit KW() = default;
+  virtual ~KW() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: use '= default'
+  // CHECK-FIXES: virtual ~KW() = default;
+};
+
+// Nested class.
+struct N {
+  struct NN {
+    NN() {}
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use '= default'
+    // CHECK-FIXES: NN() = default;
+    ~NN() {}
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use '= default'
+    // CHECK-FIXES: ~NN() = default;
+  };
+  int Int;
+};
+
+// Class template.
+template <class T>
+class Temp {
+public:
+  Temp() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default'
+  // CHECK-FIXES: Temp() = default;
+  ~Temp() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default'
+  // CHECK-FIXES: ~Temp() = default;
+};
+
+// Class template out of line with explicit instantiation.
+template <class T>
+class TempODef {
+public:
+  TempODef();
+  ~TempODef();
+};
+
+template <class T>
+TempODef<T>::TempODef() {}
+// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: use '= default'
+// CHECK-FIXES: TempODef<T>::TempODef() = default;
+template <class T>
+TempODef<T>::~TempODef() {}
+// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: use '= default'
+// CHECK-FIXES: TempODef<T>::~TempODef() = default;
+
+template class TempODef<int>;
+template class TempODef<double>;
+
+// Non user-provided constructor/destructor.
+struct Imp {
+  int Int;
+};
+void g() {
+  Imp *PtrImp = new Imp();
+  PtrImp->~Imp();
+  delete PtrImp;
+}
+
+// Already using default.
+struct IDef {
+  IDef() = default;
+  ~IDef() = default;
+};
+struct ODef {
+  ODef();
+  ~ODef();
+};
+ODef::ODef() = default;
+ODef::~ODef() = default;
+
+// Delegating constructor and overriden destructor.
+struct DC : KW {
+  DC() : KW() {}
+  ~DC() override {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default'
+  // CHECK-FIXES: ~DC() override = default;
+};
+
+struct Comments {
+  Comments() {
+    // Don't erase comments inside the body.
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use '= default'
+  ~Comments() {
+    // Don't erase comments inside the body.
+  }
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use '= default'
+};
+
+// Try-catch.
+struct ITC {
+  ITC() try {} catch(...) {}
+  ~ITC() try {} catch(...) {}
+};
+
+struct OTC {
+  OTC();
+  ~OTC();
+};
+OTC::OTC() try {} catch(...) {}
+OTC::~OTC() try {} catch(...) {}
+
+#define STRUCT_WITH_DEFAULT(_base, _type) \
+  struct _type {                          \
+    _type() {}                            \
+    _base value;                          \
+  };
+
+STRUCT_WITH_DEFAULT(unsigned char, Hex8Default)
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use '= default' to define a trivial default constructor
+// CHECK-MESSAGES: :[[@LINE-6]]:13: note:
diff --git a/test/clang-tidy/modernize-use-equals-delete.cpp b/test/clang-tidy/modernize-use-equals-delete.cpp
new file mode 100644 (file)
index 0000000..6ab2ae4
--- /dev/null
@@ -0,0 +1,187 @@
+// RUN: %check_clang_tidy %s modernize-use-equals-delete %t
+
+struct PositivePrivate {
+private:
+  PositivePrivate();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= delete' to prohibit calling of a special member function [modernize-use-equals-delete]
+  // CHECK-FIXES: PositivePrivate() = delete;
+  PositivePrivate(const PositivePrivate &);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= delete' to prohibit calling of a special member function [modernize-use-equals-delete]
+  // CHECK-FIXES: PositivePrivate(const PositivePrivate &) = delete;
+  PositivePrivate &operator=(const PositivePrivate &);
+  // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: use '= delete' to prohibit calling of a special member function [modernize-use-equals-delete]
+  // CHECK-FIXES: PositivePrivate &operator=(const PositivePrivate &) = delete;
+  PositivePrivate(PositivePrivate &&);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= delete' to prohibit calling of a special member function [modernize-use-equals-delete]
+  // CHECK-FIXES: PositivePrivate(PositivePrivate &&) = delete;
+  PositivePrivate &operator=(PositivePrivate &&);
+  // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: use '= delete' to prohibit calling of a special member function [modernize-use-equals-delete]
+  // CHECK-FIXES: PositivePrivate &operator=(PositivePrivate &&) = delete;
+  ~PositivePrivate();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= delete' to prohibit calling of a special member function [modernize-use-equals-delete]
+  // CHECK-FIXES: ~PositivePrivate() = delete;
+};
+
+template<typename T>
+struct PositivePrivateTemplate {
+private:
+  PositivePrivateTemplate();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= delete' to prohibit calling of a special member function [modernize-use-equals-delete]
+  // CHECK-FIXES: PositivePrivateTemplate() = delete;
+  PositivePrivateTemplate(const PositivePrivateTemplate &);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= delete' to prohibit calling of a special member function [modernize-use-equals-delete]
+  // CHECK-FIXES: PositivePrivateTemplate(const PositivePrivateTemplate &) = delete;
+  PositivePrivateTemplate &operator=(const PositivePrivateTemplate &);
+  // CHECK-MESSAGES: :[[@LINE-1]]:28: warning: use '= delete' to prohibit calling of a special member function [modernize-use-equals-delete]
+  // CHECK-FIXES: PositivePrivateTemplate &operator=(const PositivePrivateTemplate &) = delete;
+  PositivePrivateTemplate(PositivePrivateTemplate &&);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= delete' to prohibit calling of a special member function [modernize-use-equals-delete]
+  // CHECK-FIXES: PositivePrivateTemplate(PositivePrivateTemplate &&) = delete;
+  PositivePrivateTemplate &operator=(PositivePrivateTemplate &&);
+  // CHECK-MESSAGES: :[[@LINE-1]]:28: warning: use '= delete' to prohibit calling of a special member function [modernize-use-equals-delete]
+  // CHECK-FIXES: PositivePrivateTemplate &operator=(PositivePrivateTemplate &&) = delete;
+  ~PositivePrivateTemplate();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= delete' to prohibit calling of a special member function [modernize-use-equals-delete]
+  // CHECK-FIXES: ~PositivePrivateTemplate() = delete;
+};
+
+template struct PositivePrivateTemplate<int>;
+template struct PositivePrivateTemplate<char>;
+
+struct NegativePublic {
+  NegativePublic(const NegativePublic &);
+};
+
+struct NegativeProtected {
+protected:
+  NegativeProtected(const NegativeProtected &);
+};
+
+struct PositiveInlineMember {
+  int foo() { return 0; }
+
+private:
+  PositiveInlineMember(const PositiveInlineMember &);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= delete' to prohibit calling of a special member function [modernize-use-equals-delete]
+  // CHECK-FIXES: PositiveInlineMember(const PositiveInlineMember &) = delete;
+};
+
+struct PositiveOutOfLineMember {
+  int foo();
+
+private:
+  PositiveOutOfLineMember(const PositiveOutOfLineMember &);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= delete' to prohibit calling of a special member function [modernize-use-equals-delete]
+  // CHECK-FIXES: PositiveOutOfLineMember(const PositiveOutOfLineMember &) = delete;
+};
+
+int PositiveOutOfLineMember::foo() { return 0; }
+
+struct PositiveAbstractMember {
+  virtual int foo() = 0;
+
+private:
+  PositiveAbstractMember(const PositiveAbstractMember &);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= delete' to prohibit calling of a special member function [modernize-use-equals-delete]
+  // CHECK-FIXES: PositiveAbstractMember(const PositiveAbstractMember &) = delete;
+};
+
+struct NegativeMemberNotImpl {
+  int foo();
+
+private:
+  NegativeMemberNotImpl(const NegativeMemberNotImpl &);
+};
+
+struct NegativeStaticMemberNotImpl {
+  static int foo();
+
+private:
+  NegativeStaticMemberNotImpl(const NegativeStaticMemberNotImpl &);
+};
+
+struct NegativeInline {
+private:
+  NegativeInline(const NegativeInline &) {}
+};
+
+struct NegativeOutOfLine {
+private:
+  NegativeOutOfLine(const NegativeOutOfLine &);
+};
+
+NegativeOutOfLine::NegativeOutOfLine(const NegativeOutOfLine &) {}
+
+struct NegativeConstructNotImpl {
+  NegativeConstructNotImpl();
+
+private:
+  NegativeConstructNotImpl(const NegativeConstructNotImpl &);
+};
+
+struct PositiveDefaultedConstruct {
+  PositiveDefaultedConstruct() = default;
+
+private:
+  PositiveDefaultedConstruct(const PositiveDefaultedConstruct &);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= delete' to prohibit calling of a special member function [modernize-use-equals-delete]
+  // CHECK-FIXES: PositiveDefaultedConstruct(const PositiveDefaultedConstruct &) = delete;
+};
+
+struct PositiveDeletedConstruct {
+  PositiveDeletedConstruct() = delete;
+
+private:
+  PositiveDeletedConstruct(const PositiveDeletedConstruct &);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= delete' to prohibit calling of a special member function [modernize-use-equals-delete]
+  // CHECK-FIXES: PositiveDeletedConstruct(const PositiveDeletedConstruct &) = delete;
+};
+
+struct NegativeDefaulted {
+private:
+  NegativeDefaulted(const NegativeDefaulted &) = default;
+};
+
+struct PrivateDeleted {
+private:
+  PrivateDeleted(const PrivateDeleted &) = delete;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: deleted member function should be public [modernize-use-equals-delete]
+};
+
+struct ProtectedDeleted {
+protected:
+  ProtectedDeleted(const ProtectedDeleted &) = delete;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: deleted member function should be public [modernize-use-equals-delete]
+};
+
+struct PublicDeleted {
+public:
+  PublicDeleted(const PublicDeleted &) = delete;
+};
+
+#define M1                                                         \
+  struct PrivateDeletedMacro {                                     \
+  private:                                                         \
+    PrivateDeletedMacro(const PrivateDeletedMacro &) = delete;     \
+  };                                                               \
+  struct ProtectedDeletedMacro {                                   \
+  protected:                                                       \
+    ProtectedDeletedMacro(const ProtectedDeletedMacro &) = delete; \
+  }
+
+M1;
+
+#define DISALLOW_COPY_AND_ASSIGN(name) \
+  name(const name &) = delete;         \
+  void operator=(const name &) = delete
+
+struct PrivateDeletedMacro2 {
+private:
+  DISALLOW_COPY_AND_ASSIGN(PrivateDeletedMacro2);
+};
+
+struct ProtectedDeletedMacro2 {
+protected:
+  DISALLOW_COPY_AND_ASSIGN(ProtectedDeletedMacro2);
+};
+
diff --git a/test/clang-tidy/modernize-use-noexcept-macro.cpp b/test/clang-tidy/modernize-use-noexcept-macro.cpp
new file mode 100644 (file)
index 0000000..3948b66
--- /dev/null
@@ -0,0 +1,36 @@
+// RUN: %check_clang_tidy %s modernize-use-noexcept %t -- \
+// RUN:   -config="{CheckOptions: [{key: modernize-use-noexcept.ReplacementString, value: 'NOEXCEPT'}]}" \
+// RUN:   -- -std=c++11 -fexceptions
+
+// Example definition of NOEXCEPT -- simplified test to see if noexcept is supported.
+#if (__has_feature(cxx_noexcept))
+#define NOEXCEPT noexcept
+#else
+#define NOEXCEPT throw()
+#endif
+
+void bar() throw() {}
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: dynamic exception specification 'throw()' is deprecated; consider using 'NOEXCEPT' instead [modernize-use-noexcept]
+// CHECK-FIXES: void bar() NOEXCEPT {}
+
+// Should not trigger a FixItHint, since macros only support noexcept, and this
+// case throws.
+class A {};
+class B {};
+void foobar() throw(A, B);
+// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: dynamic exception specification 'throw(A, B)' is deprecated; consider removing it instead [modernize-use-noexcept]
+
+// Should not trigger a replacement.
+void foo() noexcept(true);
+
+struct Z {
+  void operator delete(void *ptr) throw();
+  void operator delete[](void *ptr) throw(int);
+  ~Z() throw(int) {}
+};
+// CHECK-MESSAGES: :[[@LINE-4]]:35: warning: dynamic exception specification 'throw()' is deprecated; consider using 'NOEXCEPT' instead [modernize-use-noexcept]
+// CHECK-MESSAGES: :[[@LINE-4]]:37: warning: dynamic exception specification 'throw(int)' is deprecated; consider removing it instead [modernize-use-noexcept]
+// CHECK-MESSAGES: :[[@LINE-4]]:8: warning: dynamic exception specification 'throw(int)' is deprecated; consider removing it instead [modernize-use-noexcept]
+// CHECK-FIXES: void operator delete(void *ptr) NOEXCEPT;
+// CHECK-FIXES: void operator delete[](void *ptr) throw(int);
+// CHECK-FIXES: ~Z() throw(int) {}
diff --git a/test/clang-tidy/modernize-use-noexcept-opt.cpp b/test/clang-tidy/modernize-use-noexcept-opt.cpp
new file mode 100644 (file)
index 0000000..85a83c8
--- /dev/null
@@ -0,0 +1,88 @@
+// RUN: %check_clang_tidy %s modernize-use-noexcept %t -- \
+// RUN:   -config="{CheckOptions: [{key: modernize-use-noexcept.UseNoexceptFalse, value: 0}]}" \
+// RUN:   -- -std=c++11 -fexceptions
+
+class A {};
+class B {};
+
+void foo() throw();
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept]
+// CHECK-FIXES: void foo() noexcept;
+
+void bar() throw(...);
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: dynamic exception specification 'throw(...)' is deprecated; consider removing it instead [modernize-use-noexcept]
+// CHECK-FIXES: void bar() ;
+
+void k() throw(int(int));
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: dynamic exception specification 'throw(int(int))' is deprecated; consider removing it instead [modernize-use-noexcept]
+// CHECK-FIXES: void k() ;
+
+void foobar() throw(A, B)
+{}
+// CHECK-MESSAGES: :[[@LINE-2]]:15: warning: dynamic exception specification 'throw(A, B)' is deprecated; consider removing it instead [modernize-use-noexcept]
+// CHECK-FIXES: void foobar()
+
+void baz(int = (throw A(), 0)) throw(A, B) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:32: warning: dynamic exception specification 'throw(A, B)' is deprecated; consider removing it instead [modernize-use-noexcept]
+// CHECK-FIXES: void baz(int = (throw A(), 0)) {}
+
+void g(void (*fp)(void) throw());
+// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept]
+// CHECK-FIXES: void g(void (*fp)(void) noexcept);
+
+void f(void (*fp)(void) throw(int)) throw(char);
+// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: dynamic exception specification 'throw(int)' is deprecated; consider removing it instead [modernize-use-noexcept]
+// CHECK-MESSAGES: :[[@LINE-2]]:37: warning: dynamic exception specification 'throw(char)' is deprecated; consider removing it instead [modernize-use-noexcept]
+// CHECK-FIXES: void f(void (*fp)(void) ) ;
+
+#define THROW throw
+void h(void (*fp)(void) THROW(int)) THROW(char);
+// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: dynamic exception specification 'THROW(int)' is deprecated; consider removing it instead [modernize-use-noexcept]
+// CHECK-MESSAGES: :[[@LINE-2]]:37: warning: dynamic exception specification 'THROW(char)' is deprecated; consider removing it instead [modernize-use-noexcept]
+// CHECK-FIXES: void h(void (*fp)(void) ) ;
+
+void j() throw(int(int) throw(void(void) throw(int)));
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: dynamic exception specification 'throw(int(int) throw(void(void) throw(int)))' is deprecated; consider removing it instead [modernize-use-noexcept]
+// CHECK-FIXES: void j() ;
+
+class Y {
+  Y() throw() = default;
+};
+// CHECK-MESSAGES: :[[@LINE-2]]:7: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept]
+// CHECK-FIXES: Y() noexcept = default;
+
+struct Z {
+  void operator delete(void *ptr) throw();
+  void operator delete[](void *ptr) throw(int);
+  ~Z() throw(int) {}
+};
+// CHECK-MESSAGES: :[[@LINE-4]]:35: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept]
+// CHECK-MESSAGES: :[[@LINE-4]]:37: warning: dynamic exception specification 'throw(int)' is deprecated; consider using 'noexcept(false)' instead [modernize-use-noexcept]
+// CHECK-MESSAGES: :[[@LINE-4]]:8: warning: dynamic exception specification 'throw(int)' is deprecated; consider using 'noexcept(false)' instead [modernize-use-noexcept]
+// CHECK-FIXES: void operator delete(void *ptr) noexcept;
+// CHECK-FIXES: void operator delete[](void *ptr) noexcept(false);
+// CHECK-FIXES: ~Z() noexcept(false) {}
+
+struct S {
+  void f() throw();
+};
+void f(void (S::*)() throw());
+// CHECK-MESSAGES: :[[@LINE-3]]:12: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept]
+// CHECK-MESSAGES: :[[@LINE-2]]:22: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept]
+// CHECK-FIXES: void f() noexcept;
+// CHECK-FIXES: void f(void (S::*)() noexcept);
+
+typedef void (*fp)(void (*fp2)(int) throw());
+// CHECK-MESSAGES: :[[@LINE-1]]:37: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept]
+// CHECK-FIXES: typedef void (*fp)(void (*fp2)(int) noexcept);
+
+// Should not trigger a replacement.
+void titi() noexcept {}
+void toto() noexcept(true) {}
+
+// Should not trigger a replacement.
+void bad()
+#if !__has_feature(cxx_noexcept)
+    throw()
+#endif
+  ;
diff --git a/test/clang-tidy/modernize-use-noexcept.cpp b/test/clang-tidy/modernize-use-noexcept.cpp
new file mode 100644 (file)
index 0000000..58c764a
--- /dev/null
@@ -0,0 +1,104 @@
+// RUN: %check_clang_tidy %s modernize-use-noexcept %t -- \
+// RUN:   -- -std=c++11 -fexceptions
+
+class A {};
+class B {};
+
+void foo() throw();
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept]
+// CHECK-FIXES: void foo() noexcept;
+
+template <typename T>
+void foo() throw();
+void footest() { foo<int>(); foo<double>(); }
+// CHECK-MESSAGES: :[[@LINE-2]]:12: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept]
+// CHECK-FIXES: void foo() noexcept;
+
+void bar() throw(...);
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: dynamic exception specification 'throw(...)' is deprecated; consider using 'noexcept(false)' instead [modernize-use-noexcept]
+// CHECK-FIXES: void bar() noexcept(false);
+
+void k() throw(int(int));
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: dynamic exception specification 'throw(int(int))' is deprecated; consider using 'noexcept(false)' instead [modernize-use-noexcept]
+// CHECK-FIXES: void k() noexcept(false);
+
+void foobar() throw(A, B)
+{}
+// CHECK-MESSAGES: :[[@LINE-2]]:15: warning: dynamic exception specification 'throw(A, B)' is deprecated; consider using 'noexcept(false)' instead [modernize-use-noexcept]
+// CHECK-FIXES: void foobar() noexcept(false)
+
+void baz(int = (throw A(), 0)) throw(A, B) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:32: warning: dynamic exception specification 'throw(A, B)' is deprecated; consider using 'noexcept(false)' instead [modernize-use-noexcept]
+// CHECK-FIXES: void baz(int = (throw A(), 0)) noexcept(false) {}
+
+void g(void (*fp)(void) throw());
+// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept]
+// CHECK-FIXES: void g(void (*fp)(void) noexcept);
+
+void f(void (*fp)(void) throw(int)) throw(char);
+// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: dynamic exception specification 'throw(int)' is deprecated; consider using 'noexcept(false)' instead [modernize-use-noexcept]
+// CHECK-MESSAGES: :[[@LINE-2]]:37: warning: dynamic exception specification 'throw(char)' is deprecated; consider using 'noexcept(false)' instead [modernize-use-noexcept]
+// CHECK-FIXES: void f(void (*fp)(void) noexcept(false)) noexcept(false);
+
+#define THROW throw
+void h(void (*fp)(void) THROW(int)) THROW(char);
+// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: dynamic exception specification 'THROW(int)' is deprecated; consider using 'noexcept(false)' instead [modernize-use-noexcept]
+// CHECK-MESSAGES: :[[@LINE-2]]:37: warning: dynamic exception specification 'THROW(char)' is deprecated; consider using 'noexcept(false)' instead [modernize-use-noexcept]
+// CHECK-FIXES: void h(void (*fp)(void) noexcept(false)) noexcept(false);
+
+void j() throw(int(int) throw(void(void) throw(int)));
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: dynamic exception specification 'throw(int(int) throw(void(void) throw(int)))' is deprecated; consider using 'noexcept(false)' instead [modernize-use-noexcept]
+// CHECK-FIXES: void j() noexcept(false);
+
+class Y {
+  Y() throw() = default;
+};
+// CHECK-MESSAGES: :[[@LINE-2]]:7: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept]
+// CHECK-FIXES: Y() noexcept = default;
+
+struct Z {
+  void operator delete(void *ptr) throw();
+  void operator delete[](void *ptr) throw(int);
+  ~Z() throw(int) {}
+};
+// CHECK-MESSAGES: :[[@LINE-4]]:35: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept]
+// CHECK-MESSAGES: :[[@LINE-4]]:37: warning: dynamic exception specification 'throw(int)' is deprecated; consider using 'noexcept(false)' instead [modernize-use-noexcept]
+// CHECK-MESSAGES: :[[@LINE-4]]:8: warning: dynamic exception specification 'throw(int)' is deprecated; consider using 'noexcept(false)' instead [modernize-use-noexcept]
+// CHECK-FIXES: void operator delete(void *ptr) noexcept;
+// CHECK-FIXES: void operator delete[](void *ptr) noexcept(false);
+// CHECK-FIXES: ~Z() noexcept(false) {}
+
+struct S {
+  void f() throw();
+};
+void f(void (S::*)() throw());
+// CHECK-MESSAGES: :[[@LINE-3]]:12: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept]
+// CHECK-MESSAGES: :[[@LINE-2]]:22: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept]
+// CHECK-FIXES: void f() noexcept;
+// CHECK-FIXES: void f(void (S::*)() noexcept);
+
+template <typename T>
+struct ST {
+  void foo() throw();
+};
+template <typename T>
+void ft(void (ST<T>::*)() throw());
+// CHECK-MESSAGES: :[[@LINE-4]]:14: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept]
+// CHECK-MESSAGES: :[[@LINE-2]]:27: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept]
+// CHECK-FIXES: void foo() noexcept;
+// CHECK-FIXES: void ft(void (ST<T>::*)() noexcept);
+
+typedef void (*fp)(void (*fp2)(int) throw());
+// CHECK-MESSAGES: :[[@LINE-1]]:37: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept]
+// CHECK-FIXES: typedef void (*fp)(void (*fp2)(int) noexcept);
+
+// Should not trigger a replacement.
+void titi() noexcept {}
+void toto() noexcept(true) {}
+
+// Should not trigger a replacement.
+void bad()
+#if !__has_feature(cxx_noexcept)
+    throw()
+#endif
+  ;
diff --git a/test/clang-tidy/modernize-use-nullptr-basic.cpp b/test/clang-tidy/modernize-use-nullptr-basic.cpp
new file mode 100644 (file)
index 0000000..7a953a7
--- /dev/null
@@ -0,0 +1,361 @@
+// RUN: %check_clang_tidy %s modernize-use-nullptr %t -- -- \
+// RUN:   -std=c++98 -Wno-non-literal-null-conversion
+//
+// Some parts of the test (e.g. assignment of `const int` to `int *`) fail in
+// C++11, so we need to run the test in C++98 mode.
+
+const unsigned int g_null = 0;
+#define NULL 0
+
+void test_assignment() {
+  int *p1 = 0;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use nullptr [modernize-use-nullptr]
+  // CHECK-FIXES: int *p1 = nullptr;
+  p1 = 0;
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use nullptr
+  // CHECK-FIXES: p1 = nullptr;
+
+  int *p2 = NULL;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use nullptr
+  // CHECK-FIXES: int *p2 = nullptr;
+
+  p2 = p1;
+  // CHECK-FIXES: p2 = p1;
+
+  const int null = 0;
+  int *p3 = null;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use nullptr
+  // CHECK-FIXES: int *p3 = nullptr;
+
+  p3 = NULL;
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use nullptr
+  // CHECK-FIXES: p3 = nullptr;
+
+  int *p4 = p3;
+  // CHECK-FIXES: int *p4 = p3;
+
+  p4 = null;
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use nullptr
+  // CHECK-FIXES: p4 = nullptr;
+
+  int i1 = 0;
+
+  int i2 = NULL;
+
+  int i3 = null;
+
+  int *p5, *p6, *p7;
+  p5 = p6 = p7 = NULL;
+  // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: use nullptr
+  // CHECK-FIXES: p5 = p6 = p7 = nullptr;
+}
+
+struct Foo {
+  Foo(int *p = NULL) : m_p1(p) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: use nullptr
+  // CHECK-FIXES: Foo(int *p = nullptr) : m_p1(p) {}
+
+  void bar(int *p = 0) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: use nullptr
+  // CHECK-FIXES: void bar(int *p = nullptr) {}
+
+  void baz(int i = 0) {}
+
+  int *m_p1;
+  static int *m_p2;
+};
+
+int *Foo::m_p2 = NULL;
+// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: use nullptr
+// CHECK-FIXES: int *Foo::m_p2 = nullptr;
+
+template <typename T>
+struct Bar {
+  Bar(T *p) : m_p(p) {
+    m_p = static_cast<T*>(NULL);
+    // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: use nullptr
+    // CHECK-FIXES: m_p = static_cast<T*>(nullptr);
+
+    m_p = static_cast<T*>(reinterpret_cast<int*>((void*)NULL));
+    // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: use nullptr
+    // CHECK-FIXES: m_p = static_cast<T*>(nullptr);
+
+    m_p = static_cast<T*>(p ? p : static_cast<void*>(g_null));
+    // CHECK-MESSAGES: :[[@LINE-1]]:54: warning: use nullptr
+    // CHECK-FIXES: m_p = static_cast<T*>(p ? p : static_cast<void*>(nullptr));
+
+    T *p2 = static_cast<T*>(reinterpret_cast<int*>((void*)NULL));
+    // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: use nullptr
+    // CHECK-FIXES: T *p2 = static_cast<T*>(nullptr);
+
+    m_p = NULL;
+    // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: use nullptr
+    // CHECK-FIXES: m_p = nullptr;
+
+    int i = static_cast<int>(0.f);
+    T *i2 = static_cast<int>(0.f);
+    // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use nullptr
+    // CHECK-FIXES: T *i2 = nullptr;
+  }
+
+  T *m_p;
+};
+
+struct Baz {
+  Baz() : i(0) {}
+  int i;
+};
+
+void test_cxx_cases() {
+  Foo f(g_null);
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use nullptr
+  // CHECK-FIXES: Foo f(nullptr);
+
+  f.bar(NULL);
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use nullptr
+  // CHECK-FIXES: f.bar(nullptr);
+
+  f.baz(g_null);
+
+  f.m_p1 = 0;
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use nullptr
+  // CHECK-FIXES: f.m_p1 = nullptr;
+
+  Bar<int> b(g_null);
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: use nullptr
+  // CHECK-FIXES: Bar<int> b(nullptr);
+
+  Baz b2;
+  int Baz::*memptr(0);
+  // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: use nullptr
+  // CHECK-FIXES: int Baz::*memptr(nullptr);
+
+  memptr = 0;
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use nullptr
+  // CHECK-FIXES: memptr = nullptr;
+}
+
+void test_function_default_param1(void *p = 0);
+// CHECK-MESSAGES: :[[@LINE-1]]:45: warning: use nullptr
+// CHECK-FIXES: void test_function_default_param1(void *p = nullptr);
+
+void test_function_default_param2(void *p = NULL);
+// CHECK-MESSAGES: :[[@LINE-1]]:45: warning: use nullptr
+// CHECK-FIXES: void test_function_default_param2(void *p = nullptr);
+
+void test_function_default_param3(void *p = g_null);
+// CHECK-MESSAGES: :[[@LINE-1]]:45: warning: use nullptr
+// CHECK-FIXES: void test_function_default_param3(void *p = nullptr);
+
+void test_function(int *p) {}
+
+void test_function_no_ptr_param(int i) {}
+
+void test_function_call() {
+  test_function(0);
+  // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: use nullptr
+  // CHECK-FIXES: test_function(nullptr);
+
+  test_function(NULL);
+  // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: use nullptr
+  // CHECK-FIXES: test_function(nullptr);
+
+  test_function(g_null);
+  // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: use nullptr
+  // CHECK-FIXES: test_function(nullptr);
+
+  test_function_no_ptr_param(0);
+}
+
+char *test_function_return1() {
+  return 0;
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use nullptr
+  // CHECK-FIXES: return nullptr;
+}
+
+void *test_function_return2() {
+  return NULL;
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use nullptr
+  // CHECK-FIXES: return nullptr;
+}
+
+long *test_function_return3() {
+  return g_null;
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use nullptr
+  // CHECK-FIXES: return nullptr;
+}
+
+int test_function_return4() {
+  return 0;
+}
+
+int test_function_return5() {
+  return NULL;
+}
+
+int test_function_return6() {
+  return g_null;
+}
+
+int *test_function_return_cast1() {
+  return(int)0;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use nullptr
+  // CHECK-FIXES: return nullptr;
+}
+
+int *test_function_return_cast2() {
+#define RET return
+  RET(int)0;
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: use nullptr
+  // CHECK-FIXES: RET nullptr;
+#undef RET
+}
+
+// Test parentheses expressions resulting in a nullptr.
+int *test_parentheses_expression1() {
+  return(0);
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use nullptr
+  // CHECK-FIXES: return(nullptr);
+}
+
+int *test_parentheses_expression2() {
+  return(int(0.f));
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use nullptr
+  // CHECK-FIXES: return(nullptr);
+}
+
+int *test_nested_parentheses_expression() {
+  return((((0))));
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use nullptr
+  // CHECK-FIXES: return((((nullptr))));
+}
+
+void *test_parentheses_explicit_cast() {
+  return(static_cast<void*>(0));
+  // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: use nullptr
+  // CHECK-FIXES: return(static_cast<void*>(nullptr));
+}
+
+void *test_parentheses_explicit_cast_sequence1() {
+  return(static_cast<void*>(static_cast<int*>((void*)NULL)));
+  // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: use nullptr
+  // CHECK-FIXES: return(static_cast<void*>(nullptr));
+}
+
+void *test_parentheses_explicit_cast_sequence2() {
+  return(static_cast<void*>(reinterpret_cast<int*>((float*)int(0.f))));
+  // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: use nullptr
+  // CHECK-FIXES: return(static_cast<void*>(nullptr));
+}
+
+// Test explicit cast expressions resulting in nullptr.
+struct Bam {
+  Bam(int *a) {}
+  Bam(float *a) {}
+  Bam operator=(int *a) { return Bam(a); }
+  Bam operator=(float *a) { return Bam(a); }
+};
+
+void ambiguous_function(int *a) {}
+void ambiguous_function(float *a) {}
+void const_ambiguous_function(const int *p) {}
+void const_ambiguous_function(const float *p) {}
+
+void test_explicit_cast_ambiguous1() {
+  ambiguous_function((int*)0);
+  // CHECK-MESSAGES: :[[@LINE-1]]:28: warning: use nullptr
+  // CHECK-FIXES: ambiguous_function((int*)nullptr);
+}
+
+void test_explicit_cast_ambiguous2() {
+  ambiguous_function((int*)(0));
+  // CHECK-MESSAGES: :[[@LINE-1]]:28: warning: use nullptr
+  // CHECK-FIXES: ambiguous_function((int*)nullptr);
+}
+
+void test_explicit_cast_ambiguous3() {
+  ambiguous_function(static_cast<int*>(reinterpret_cast<int*>((float*)0)));
+  // CHECK-MESSAGES: :[[@LINE-1]]:40: warning: use nullptr
+  // CHECK-FIXES: ambiguous_function(static_cast<int*>(nullptr));
+}
+
+Bam test_explicit_cast_ambiguous4() {
+  return(((int*)(0)));
+  // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: use nullptr
+  // CHECK-FIXES: return(((int*)nullptr));
+}
+
+void test_explicit_cast_ambiguous5() {
+  // Test for ambiguous overloaded constructors.
+  Bam k((int*)(0));
+  // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: use nullptr
+  // CHECK-FIXES: Bam k((int*)nullptr);
+
+  // Test for ambiguous overloaded operators.
+  k = (int*)0;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use nullptr
+  // CHECK-FIXES: k = (int*)nullptr;
+}
+
+void test_const_pointers_abiguous() {
+  const_ambiguous_function((int*)0);
+  // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: use nullptr
+  // CHECK-FIXES: const_ambiguous_function((int*)nullptr);
+}
+
+// Test where the implicit cast to null is surrounded by another implict cast
+// with possible explict casts in-between.
+void test_const_pointers() {
+  const int *const_p1 = 0;
+  // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: use nullptr
+  // CHECK-FIXES: const int *const_p1 = nullptr;
+  const int *const_p2 = NULL;
+  // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: use nullptr
+  // CHECK-FIXES: const int *const_p2 = nullptr;
+  const int *const_p3 = (int)0;
+  // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: use nullptr
+  // CHECK-FIXES: const int *const_p3 = nullptr;
+  const int *const_p4 = (int)0.0f;
+  // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: use nullptr
+  // CHECK-FIXES: const int *const_p4 = nullptr;
+  const int *const_p5 = (int*)0;
+  // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: use nullptr
+  // CHECK-FIXES: const int *const_p5 = (int*)nullptr;
+  int *t;
+  const int *const_p6 = static_cast<int*>(t ? t : static_cast<int*>(0));
+  // CHECK-MESSAGES: :[[@LINE-1]]:69: warning: use nullptr
+  // CHECK-FIXES: const int *const_p6 = static_cast<int*>(t ? t : static_cast<int*>(nullptr));
+}
+
+void test_nested_implicit_cast_expr() {
+  int func0(void*, void*);
+  int func1(int, void*, void*);
+
+  (double)func1(0, 0, 0);
+  // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: use nullptr
+  // CHECK-MESSAGES: :[[@LINE-2]]:23: warning: use nullptr
+  // CHECK-FIXES: (double)func1(0, nullptr, nullptr);
+  (double)func1(func0(0, 0), 0, 0);
+  // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: use nullptr
+  // CHECK-MESSAGES: :[[@LINE-2]]:26: warning: use nullptr
+  // CHECK-MESSAGES: :[[@LINE-3]]:30: warning: use nullptr
+  // CHECK-MESSAGES: :[[@LINE-4]]:33: warning: use nullptr
+  // CHECK-FIXES: (double)func1(func0(nullptr, nullptr), nullptr, nullptr);
+}
+
+// FIXME: currently, the check doesn't work as it should with templates.
+template<typename T>
+class A {
+ public:
+  A(T *p = NULL) {}
+
+  void f() {
+    Ptr = NULL;
+  }
+  T *Ptr;
+};
+
+template<typename T>
+T *f2(T *a = NULL) {
+  return a ? a : NULL;
+}
diff --git a/test/clang-tidy/modernize-use-nullptr.c b/test/clang-tidy/modernize-use-nullptr.c
new file mode 100644 (file)
index 0000000..c2ccbbd
--- /dev/null
@@ -0,0 +1,10 @@
+// RUN: clang-tidy %s -checks=-*,modernize-use-nullptr -- | count 0
+
+// Note: this test expects no diagnostics, but FileCheck cannot handle that,
+// hence the use of | count 0.
+
+#define NULL 0
+void f(void) {
+  char *str = NULL; // ok
+  (void)str;
+}
diff --git a/test/clang-tidy/modernize-use-nullptr.cpp b/test/clang-tidy/modernize-use-nullptr.cpp
new file mode 100644 (file)
index 0000000..878c114
--- /dev/null
@@ -0,0 +1,305 @@
+// RUN: %check_clang_tidy %s modernize-use-nullptr %t -- \
+// RUN:   -config="{CheckOptions: [{key: modernize-use-nullptr.NullMacros, value: 'MY_NULL,NULL'}]}" \
+// RUN:   -- -std=c++11
+
+#define NULL 0
+
+namespace std {
+
+typedef decltype(nullptr) nullptr_t;
+
+} // namespace std
+
+// Just to make sure make_null() could have side effects.
+void external();
+
+std::nullptr_t make_null() {
+  external();
+  return nullptr;
+}
+
+void func() {
+  void *CallTest = make_null();
+
+  int var = 1;
+  void *CommaTest = (var+=2, make_null());
+
+  int *CastTest = static_cast<int*>(make_null());
+}
+
+void dummy(int*) {}
+void side_effect() {}
+
+#define MACRO_EXPANSION_HAS_NULL \
+  void foo() { \
+    dummy(0); \
+    dummy(NULL); \
+    side_effect(); \
+  }
+
+MACRO_EXPANSION_HAS_NULL;
+#undef MACRO_EXPANSION_HAS_NULL
+
+
+void test_macro_expansion1() {
+#define MACRO_EXPANSION_HAS_NULL \
+  dummy(NULL); \
+  side_effect();
+
+  MACRO_EXPANSION_HAS_NULL;
+
+#undef MACRO_EXPANSION_HAS_NULL
+}
+
+// Test macro expansion with cast sequence, PR15572.
+void test_macro_expansion2() {
+#define MACRO_EXPANSION_HAS_NULL \
+  dummy((int*)0); \
+  side_effect();
+
+  MACRO_EXPANSION_HAS_NULL;
+
+#undef MACRO_EXPANSION_HAS_NULL
+}
+
+void test_macro_expansion3() {
+#define MACRO_EXPANSION_HAS_NULL \
+  dummy(NULL); \
+  side_effect();
+
+#define OUTER_MACRO \
+  MACRO_EXPANSION_HAS_NULL; \
+  side_effect();
+
+  OUTER_MACRO;
+
+#undef OUTER_MACRO
+#undef MACRO_EXPANSION_HAS_NULL
+}
+
+void test_macro_expansion4() {
+#define MY_NULL NULL
+  int *p = MY_NULL;
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use nullptr [modernize-use-nullptr]
+  // CHECK-FIXES: int *p = nullptr;
+#undef MY_NULL
+}
+
+#define IS_EQ(x, y) if (x != y) return;
+void test_macro_args() {
+  int i = 0;
+  int *Ptr;
+
+  IS_EQ(static_cast<int*>(0), Ptr);
+  // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: use nullptr
+  // CHECK-FIXES: IS_EQ(static_cast<int*>(nullptr), Ptr);
+
+  IS_EQ(0, Ptr);    // literal
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use nullptr
+  // CHECK-FIXES: IS_EQ(nullptr, Ptr);
+
+  IS_EQ(NULL, Ptr); // macro
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use nullptr
+  // CHECK-FIXES: IS_EQ(nullptr, Ptr);
+
+  // These are ok since the null literal is not spelled within a macro.
+#define myassert(x) if (!(x)) return;
+  myassert(0 == Ptr);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use nullptr
+  // CHECK-FIXES: myassert(nullptr == Ptr);
+
+  myassert(NULL == Ptr);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use nullptr
+  // CHECK-FIXES: myassert(nullptr == Ptr);
+
+  // These are bad as the null literal is buried in a macro.
+#define BLAH(X) myassert(0 == (X));
+#define BLAH2(X) myassert(NULL == (X));
+  BLAH(Ptr);
+  BLAH2(Ptr);
+
+  // Same as above but testing extra macro expansion.
+#define EXPECT_NULL(X) IS_EQ(0, X);
+#define EXPECT_NULL2(X) IS_EQ(NULL, X);
+  EXPECT_NULL(Ptr);
+  EXPECT_NULL2(Ptr);
+
+  // Almost the same as above but now null literal is not in a macro so ok
+  // to transform.
+#define EQUALS_PTR(X) IS_EQ(X, Ptr);
+  EQUALS_PTR(0);
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: use nullptr
+  // CHECK-FIXES: EQUALS_PTR(nullptr);
+  EQUALS_PTR(NULL);
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: use nullptr
+  // CHECK-FIXES: EQUALS_PTR(nullptr);
+
+  // Same as above but testing extra macro expansion.
+#define EQUALS_PTR_I(X) EQUALS_PTR(X)
+  EQUALS_PTR_I(0);
+  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: use nullptr
+  // CHECK-FIXES: EQUALS_PTR_I(nullptr);
+  EQUALS_PTR_I(NULL);
+  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: use nullptr
+  // CHECK-FIXES: EQUALS_PTR_I(nullptr);
+
+  // Ok since null literal not within macro. However, now testing macro
+  // used as arg to another macro.
+#define decorate(EXPR) side_effect(); EXPR;
+  decorate(IS_EQ(NULL, Ptr));
+  // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: use nullptr
+  // CHECK-FIXES: decorate(IS_EQ(nullptr, Ptr));
+  decorate(IS_EQ(0, Ptr));
+  // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: use nullptr
+  // CHECK-FIXES: decorate(IS_EQ(nullptr, Ptr));
+
+  // This macro causes a NullToPointer cast to happen where 0 is assigned to z
+  // but the 0 literal cannot be replaced because it is also used as an
+  // integer in the comparison.
+#define INT_AND_PTR_USE(X) do { int *z = X; if (X == 4) break; } while(false)
+  INT_AND_PTR_USE(0);
+
+  // Both uses of X in this case result in NullToPointer casts so replacement
+  // is possible.
+#define PTR_AND_PTR_USE(X) do { int *z = X; if (X != z) break; } while(false)
+  PTR_AND_PTR_USE(0);
+  // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use nullptr
+  // CHECK-FIXES: PTR_AND_PTR_USE(nullptr);
+  PTR_AND_PTR_USE(NULL);
+  // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use nullptr
+  // CHECK-FIXES: PTR_AND_PTR_USE(nullptr);
+
+#define OPTIONAL_CODE(...) __VA_ARGS__
+#define NOT_NULL dummy(0)
+#define CALL(X) X
+  OPTIONAL_CODE(NOT_NULL);
+  CALL(NOT_NULL);
+
+#define ENTRY(X) {X}
+  struct A {
+    int *Ptr;
+  } a[2] = {ENTRY(0), {0}};
+  // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use nullptr
+  // CHECK-MESSAGES: :[[@LINE-2]]:24: warning: use nullptr
+  // CHECK-FIXES: a[2] = {ENTRY(nullptr), {nullptr}};
+#undef ENTRY
+
+#define assert1(expr) (expr) ? 0 : 1
+#define assert2 assert1
+  int *p;
+  assert2(p == 0);
+  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: use nullptr
+  // CHECK-FIXES: assert2(p == nullptr);
+  assert2(p == NULL);
+  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: use nullptr
+  // CHECK-FIXES: assert2(p == nullptr);
+#undef assert2
+#undef assert1
+
+#define ASSERT_EQ(a, b) a == b
+#define ASSERT_NULL(x) ASSERT_EQ(static_cast<void *>(NULL), x)
+  int *pp;
+  ASSERT_NULL(pp);
+  ASSERT_NULL(NULL);
+  // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: use nullptr
+  // CHECK-FIXES: ASSERT_NULL(nullptr);
+#undef ASSERT_NULL
+#undef ASSERT_EQ
+}
+
+// One of the ancestor of the cast is a NestedNameSpecifierLoc.
+class NoDef;
+char function(NoDef *p);
+#define F(x) (sizeof(function(x)) == 1)
+template<class T, T t>
+class C {};
+C<bool, F(0)> c;
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: use nullptr
+// CHECK-FIXES: C<bool, F(nullptr)> c;
+#undef F
+
+// Test default argument expression.
+struct D {
+  explicit D(void *t, int *c = NULL) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:32: warning: use nullptr
+  // CHECK-FIXES: explicit D(void *t, int *c = nullptr) {}
+};
+
+void test_default_argument() {
+  D(nullptr);
+}
+
+// Test on two neighbour CXXDefaultArgExprs nodes.
+typedef unsigned long long uint64;
+struct ZZ {
+  explicit ZZ(uint64, const uint64* = NULL) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:39: warning: use nullptr
+// CHECK-FIXES: explicit ZZ(uint64, const uint64* = nullptr) {}
+  operator bool()  { return true; }
+};
+
+uint64 Hash(uint64 seed = 0) { return 0; }
+
+void f() {
+  bool a;
+  a = ZZ(Hash());
+}
+
+// Test on ignoring substituted template types.
+template<typename T>
+class TemplateClass {
+ public:
+  explicit TemplateClass(int a, T default_value = 0) {}
+
+  void h(T *default_value = 0) {}
+
+  void f(int* p = 0) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use nullptr
+// CHECK-FIXES: void f(int* p = nullptr) {}
+};
+
+void IgnoreSubstTemplateType() {
+  TemplateClass<int*> a(1);
+}
+
+// Test on casting nullptr.
+struct G {
+  explicit G(bool, const char * = NULL) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:35: warning: use nullptr
+  // CHECK-FIXES: explicit G(bool, const char * = nullptr) {}
+};
+bool g(const char*);
+void test_cast_nullptr() {
+  G(g(nullptr));
+  G(g((nullptr)));
+  G(g(static_cast<char*>(nullptr)));
+  G(g(static_cast<const char*>(nullptr)));
+}
+
+// Test on recognizing multiple NULLs.
+class H {
+public:
+  H(bool);
+};
+
+#define T(expression) H(expression);
+bool h(int *, int *, int * = nullptr);
+void test_multiple_nulls() {
+  T(h(NULL, NULL));
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use nullptr
+// CHECK-MESSAGES: :[[@LINE-2]]:13: warning: use nullptr
+// CHECK-FIXES: T(h(nullptr, nullptr));
+  T(h(NULL, nullptr));
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use nullptr
+// CHECK-FIXES: T(h(nullptr, nullptr));
+  T(h(nullptr, NULL));
+// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: use nullptr
+// CHECK-FIXES: T(h(nullptr, nullptr));
+  T(h(nullptr, nullptr));
+  T(h(NULL, NULL, NULL));
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use nullptr
+// CHECK-MESSAGES: :[[@LINE-2]]:13: warning: use nullptr
+// CHECK-MESSAGES: :[[@LINE-3]]:19: warning: use nullptr
+// CHECK-FIXES: T(h(nullptr, nullptr, nullptr));
+}
+#undef T
diff --git a/test/clang-tidy/modernize-use-override-cxx98.cpp b/test/clang-tidy/modernize-use-override-cxx98.cpp
new file mode 100644 (file)
index 0000000..bc162d6
--- /dev/null
@@ -0,0 +1,19 @@
+// RUN: %check_clang_tidy %s modernize-use-override %t -- -- -std=c++98
+
+struct Base {
+  virtual ~Base() {}
+  virtual void a();
+  virtual void b();
+};
+
+struct SimpleCases : public Base {
+public:
+  virtual ~SimpleCases();
+  // CHECK-FIXES: {{^}}  virtual ~SimpleCases();
+
+  void a();
+  // CHECK-FIXES: {{^}}  void a();
+
+  virtual void b();
+  // CHECK-FIXES: {{^}}  virtual void b();
+};
diff --git a/test/clang-tidy/modernize-use-override-ms.cpp b/test/clang-tidy/modernize-use-override-ms.cpp
new file mode 100644 (file)
index 0000000..0cee917
--- /dev/null
@@ -0,0 +1,25 @@
+// RUN: %check_clang_tidy %s modernize-use-override %t -- -- -fms-extensions -std=c++11
+
+// This test is designed to test ms-extension __declspec(dllexport) attributes.
+#define EXPORT __declspec(dllexport)
+
+class Base {
+  virtual EXPORT void a();
+};
+
+class EXPORT InheritedBase {
+  virtual void a();
+};
+
+class Derived : public Base {
+  virtual EXPORT void a();
+  // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: prefer using 'override' or (rarely) 'final' instead of 'virtual' [modernize-use-override]
+  // CHECK-FIXES: {{^}}  EXPORT void a() override;
+};
+
+class EXPORT InheritedDerived : public InheritedBase {
+  virtual void a();
+  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer using 'override' or (rarely) 'final' instead of 'virtual' [modernize-use-override]
+  // CHECK-FIXES: {{^}}  void a() override;
+};
+
diff --git a/test/clang-tidy/modernize-use-override.cpp b/test/clang-tidy/modernize-use-override.cpp
new file mode 100644 (file)
index 0000000..24aafe5
--- /dev/null
@@ -0,0 +1,318 @@
+// RUN: %check_clang_tidy %s modernize-use-override %t -- -- -std=c++11 -fexceptions
+
+#define ABSTRACT = 0
+
+#define OVERRIDE override
+#define VIRTUAL virtual
+#define NOT_VIRTUAL
+#define NOT_OVERRIDE
+
+#define MUST_USE_RESULT __attribute__((warn_unused_result))
+#define UNUSED __attribute__((unused))
+
+struct MUST_USE_RESULT MustUseResultObject {};
+
+struct IntPair {
+  int First, Second;
+};
+
+struct Base {
+  virtual ~Base() {}
+  virtual void a();
+  virtual void b();
+  virtual void c();
+  virtual void d();
+  virtual void d2();
+  virtual void e() = 0;
+  virtual void f() = 0;
+  virtual void f2() const = 0;
+  virtual void g() = 0;
+
+  virtual void j() const;
+  virtual MustUseResultObject k();
+  virtual bool l() MUST_USE_RESULT UNUSED;
+  virtual bool n() MUST_USE_RESULT UNUSED;
+
+  virtual void m();
+  virtual void m2();
+  virtual void o() __attribute__((unused));
+
+  virtual void r() &;
+  virtual void rr() &&;
+
+  virtual void cv() const volatile;
+  virtual void cv2() const volatile;
+
+  virtual void ne() noexcept(false);
+  virtual void t() throw();
+
+  virtual void il(IntPair);
+};
+
+struct SimpleCases : public Base {
+public:
+  virtual ~SimpleCases();
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: prefer using 'override' or (rarely) 'final' instead of 'virtual' [modernize-use-override]
+  // CHECK-FIXES: {{^}}  ~SimpleCases() override;
+
+  void a();
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: annotate this
+  // CHECK-FIXES: {{^}}  void a() override;
+
+  void b() override;
+  // CHECK-MESSAGES-NOT: warning:
+  // CHECK-FIXES: {{^}}  void b() override;
+
+  virtual void c();
+  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer using
+  // CHECK-FIXES: {{^}}  void c() override;
+
+  virtual void d() override;
+  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 'virtual' is redundant since the function is already declared 'override'
+  // CHECK-FIXES: {{^}}  void d() override;
+
+  virtual void d2() final;
+  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 'virtual' is redundant since the function is already declared 'final'
+  // CHECK-FIXES: {{^}}  void d2() final;
+
+  virtual void e() = 0;
+  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer using
+  // CHECK-FIXES: {{^}}  void e() override = 0;
+
+  virtual void f()=0;
+  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer using
+  // CHECK-FIXES: {{^}}  void f() override =0;
+
+  virtual void f2() const=0;
+  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer using
+  // CHECK-FIXES: {{^}}  void f2() const override =0;
+
+  virtual void g() ABSTRACT;
+  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer using
+  // CHECK-FIXES: {{^}}  void g() override ABSTRACT;
+
+  virtual void j() const;
+  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer using
+  // CHECK-FIXES: {{^}}  void j() const override;
+
+  virtual MustUseResultObject k();  // Has an implicit attribute.
+  // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: prefer using
+  // CHECK-FIXES: {{^}}  MustUseResultObject k() override;
+
+  virtual bool l() MUST_USE_RESULT UNUSED;
+  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer using
+  // CHECK-FIXES: {{^}}  bool l() override MUST_USE_RESULT UNUSED;
+
+  virtual bool n() UNUSED MUST_USE_RESULT;
+  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer using
+  // CHECK-FIXES: {{^}}  bool n() override UNUSED MUST_USE_RESULT;
+
+  void m() override final;
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: 'override' is redundant since the function is already declared 'final'
+  // CHECK-FIXES: {{^}}  void m() final;
+
+  virtual void m2() override final;
+  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 'virtual' and 'override' are redundant since the function is already declared 'final'
+  // CHECK-FIXES: {{^}}  void m2() final;
+
+  virtual void o() __attribute__((unused));
+  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer using
+  // CHECK-FIXES: {{^}}  void o() override __attribute__((unused));
+
+  virtual void ne() noexcept(false);
+  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer using
+  // CHECK-FIXES: {{^}}  void ne() noexcept(false) override;
+
+  virtual void t() throw();
+  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer using
+  // CHECK-FIXES: {{^}}  void t() throw() override;
+};
+
+// CHECK-MESSAGES-NOT: warning:
+
+void SimpleCases::c() {}
+// CHECK-FIXES: {{^}}void SimpleCases::c() {}
+
+SimpleCases::~SimpleCases() {}
+// CHECK-FIXES: {{^}}SimpleCases::~SimpleCases() {}
+
+struct DefaultedDestructor : public Base {
+  DefaultedDestructor() {}
+  virtual ~DefaultedDestructor() = default;
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: prefer using
+  // CHECK-FIXES: {{^}}  ~DefaultedDestructor() override = default;
+};
+
+struct FinalSpecified : public Base {
+public:
+  virtual ~FinalSpecified() final;
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: 'virtual' is redundant since the function is already declared 'final'
+  // CHECK-FIXES: {{^}}  ~FinalSpecified() final;
+
+  void b() final;
+  // CHECK-MESSAGES-NOT: warning:
+  // CHECK-FIXES: {{^}}  void b() final;
+
+  virtual void d() final;
+  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 'virtual' is redundant
+  // CHECK-FIXES: {{^}}  void d() final;
+
+  virtual void e() final = 0;
+  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 'virtual' is redundant
+  // CHECK-FIXES: {{^}}  void e() final = 0;
+
+  virtual void j() const final;
+  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 'virtual' is redundant
+  // CHECK-FIXES: {{^}}  void j() const final;
+
+  virtual bool l() final MUST_USE_RESULT UNUSED;
+  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 'virtual' is redundant
+  // CHECK-FIXES: {{^}}  bool l() final MUST_USE_RESULT UNUSED;
+};
+
+struct InlineDefinitions : public Base {
+public:
+  virtual ~InlineDefinitions() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: prefer using
+  // CHECK-FIXES: {{^}}  ~InlineDefinitions() override {}
+
+  void a() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: annotate this
+  // CHECK-FIXES: {{^}}  void a() override {}
+
+  void b() override {}
+  // CHECK-MESSAGES-NOT: warning:
+  // CHECK-FIXES: {{^}}  void b() override {}
+
+  virtual void c()
+  {}
+  // CHECK-MESSAGES: :[[@LINE-2]]:16: warning: prefer using
+  // CHECK-FIXES: {{^}}  void c() override
+
+  virtual void d() override {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 'virtual' is redundant
+  // CHECK-FIXES: {{^}}  void d() override {}
+
+  virtual void j() const
+  {}
+  // CHECK-MESSAGES: :[[@LINE-2]]:16: warning: prefer using
+  // CHECK-FIXES: {{^}}  void j() const override
+
+  virtual MustUseResultObject k() {}  // Has an implicit attribute.
+  // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: prefer using
+  // CHECK-FIXES: {{^}}  MustUseResultObject k() override {}
+
+  virtual bool l() MUST_USE_RESULT UNUSED {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer using
+  // CHECK-FIXES: {{^}}  bool l() override MUST_USE_RESULT UNUSED {}
+
+  virtual void r() &
+  {}
+  // CHECK-MESSAGES: :[[@LINE-2]]:16: warning: prefer using
+  // CHECK-FIXES: {{^}}  void r() & override
+
+  virtual void rr() &&
+  {}
+  // CHECK-MESSAGES: :[[@LINE-2]]:16: warning: prefer using
+  // CHECK-FIXES: {{^}}  void rr() && override
+
+  virtual void cv() const volatile
+  {}
+  // CHECK-MESSAGES: :[[@LINE-2]]:16: warning: prefer using
+  // CHECK-FIXES: {{^}}  void cv() const volatile override
+
+  virtual void cv2() const volatile // some comment
+  {}
+  // CHECK-MESSAGES: :[[@LINE-2]]:16: warning: prefer using
+  // CHECK-FIXES: {{^}}  void cv2() const volatile override // some comment
+};
+
+struct DefaultArguments : public Base {
+  // Tests for default arguments (with initializer lists).
+  // Make sure the override fix is placed after the argument list.
+  void il(IntPair p = {1, (2 + (3))}) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: annotate this
+  // CHECK-FIXES: {{^}}  void il(IntPair p = {1, (2 + (3))}) override {}
+};
+
+struct Macros : public Base {
+  // Tests for 'virtual' and 'override' being defined through macros. Basically
+  // give up for now.
+  NOT_VIRTUAL void a() NOT_OVERRIDE;
+  // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: annotate this
+  // CHECK-FIXES: {{^}}  NOT_VIRTUAL void a() override NOT_OVERRIDE;
+
+  VIRTUAL void b() NOT_OVERRIDE;
+  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer using
+  // CHECK-FIXES: {{^}}  VIRTUAL void b() override NOT_OVERRIDE;
+
+  NOT_VIRTUAL void c() OVERRIDE;
+  // CHECK-MESSAGES-NOT: warning:
+  // CHECK-FIXES: {{^}}  NOT_VIRTUAL void c() OVERRIDE;
+
+  VIRTUAL void d() OVERRIDE;
+  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 'virtual' is redundant
+  // CHECK-FIXES: {{^}}  VIRTUAL void d() OVERRIDE;
+
+#define FUNC(return_type, name) return_type name()
+  FUNC(void, e);
+  // CHECK-FIXES: {{^}}  FUNC(void, e);
+
+#define F virtual void f();
+  F
+  // CHECK-FIXES: {{^}}  F
+
+  VIRTUAL void g() OVERRIDE final;
+  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 'virtual' and 'override' are redundant
+  // CHECK-FIXES: {{^}}  VIRTUAL void g() final;
+};
+
+// Tests for templates.
+template <typename T> struct TemplateBase {
+  virtual void f(T t);
+};
+
+template <typename T> struct DerivedFromTemplate : public TemplateBase<T> {
+  virtual void f(T t);
+  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer using
+  // CHECK-FIXES: {{^}}  void f(T t) override;
+};
+void f() { DerivedFromTemplate<int>().f(2); }
+
+template <class C>
+struct UnusedMemberInstantiation : public C {
+  virtual ~UnusedMemberInstantiation() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: prefer using
+  // CHECK-FIXES: {{^}}  ~UnusedMemberInstantiation() override {}
+};
+struct IntantiateWithoutUse : public UnusedMemberInstantiation<Base> {};
+
+struct Base2 {
+  virtual ~Base2() {}
+  virtual void a();
+};
+
+// The OverrideAttr isn't propagated to specializations in all cases. Make sure
+// we don't add "override" a second time.
+template <int I>
+struct MembersOfSpecializations : public Base2 {
+  void a() override;
+  // CHECK-MESSAGES-NOT: warning:
+  // CHECK-FIXES: {{^}}  void a() override;
+};
+template <> void MembersOfSpecializations<3>::a() {}
+void ff() { MembersOfSpecializations<3>().a(); };
+
+// In case try statement is used as a method body,
+// make sure that override fix is placed before try keyword.
+struct TryStmtAsBody : public Base {
+  void a() try
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: annotate this
+  // CHECK-FIXES: {{^}}  void a() override try
+  { b(); } catch(...) { c(); }
+
+  virtual void d() try
+  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer using
+  // CHECK-FIXES: {{^}}  void d() override try
+  { e(); } catch(...) { f(); }
+};
diff --git a/test/clang-tidy/modernize-use-transparent-functors.cpp b/test/clang-tidy/modernize-use-transparent-functors.cpp
new file mode 100644 (file)
index 0000000..b700f54
--- /dev/null
@@ -0,0 +1,107 @@
+// RUN: %check_clang_tidy %s modernize-use-transparent-functors %t -- -- -std=c++14
+
+namespace std {
+template<class T>
+struct remove_reference;
+
+template <class T>
+constexpr T &&forward(typename std::remove_reference<T>::type &t);
+
+template <class T>
+constexpr T &&forward(typename std::remove_reference<T>::type &&t);
+
+template <typename T = void>
+struct plus {
+  constexpr T operator()(const T &Lhs, const T &Rhs) const;
+};
+
+template <>
+struct plus<void> {
+  template <typename T, typename U>
+  constexpr auto operator()(T &&Lhs, U &&Rhs) const ->
+    decltype(forward<T>(Lhs) + forward<U>(Rhs));
+};
+
+template <typename T = void>
+struct less {
+  constexpr bool operator()(const T &Lhs, const T &Rhs) const;
+};
+
+template <>
+struct less<void> {
+  template <typename T, typename U>
+  constexpr bool operator()(T &&Lhs, U &&Rhs) const;
+};
+
+template <typename T = void>
+struct logical_not {
+  constexpr bool operator()(const T &Arg) const;
+};
+
+template <>
+struct logical_not<void> {
+  template <typename T>
+  constexpr bool operator()(T &&Arg) const;
+};
+
+template <typename T>
+class allocator;
+
+template <
+    class Key,
+    class Compare = std::less<>,
+    class Allocator = std::allocator<Key>>
+class set {};
+
+template <
+    class Key,
+    class Compare = std::less<Key>,
+    class Allocator = std::allocator<Key>>
+class set2 {};
+
+template <class InputIt, class UnaryPredicate>
+InputIt find_if(InputIt first, InputIt last,
+                UnaryPredicate p);
+
+template <class RandomIt, class Compare>
+void sort(RandomIt first, RandomIt last, Compare comp);
+
+class iterator {};
+class string {};
+}
+
+int main() {
+  using std::set;
+  using std::less;
+  std::set<int, std::less<int>> s;
+  // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: prefer transparent functors 'less<>' [modernize-use-transparent-functors]
+  // CHECK-FIXES: {{^}}  std::set<int, std::less<>> s;{{$}}
+  set<int, std::less<int>> s2;
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: prefer transparent functors
+  // CHECK-FIXES: {{^}}  set<int, std::less<>> s2;{{$}}
+  set<int, less<int>> s3;
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: prefer transparent functors
+  // CHECK-FIXES: {{^}}  set<int, less<>> s3;{{$}}
+  std::set<int, std::less<>> s4;
+  std::set<char *, std::less<std::string>> s5;
+  std::set<set<int, less<int>>, std::less<>> s6;
+  // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: prefer transparent functors
+  // CHECK-FIXES: {{^}}  std::set<set<int, less<>>, std::less<>> s6;{{$}}
+  std::iterator begin, end;
+  sort(begin, end, std::less<int>());
+  // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: prefer transparent functors
+  std::sort(begin, end, std::less<>());
+  find_if(begin, end, std::logical_not<bool>());
+  // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: prefer transparent functors
+  std::find_if(begin, end, std::logical_not<>());
+  using my_set = std::set<int, std::less<int>>;
+  // CHECK-MESSAGES: :[[@LINE-1]]:32: warning: prefer transparent functors
+  // CHECK-FIXES: {{^}}  using my_set = std::set<int, std::less<>>;{{$}}
+  using my_set2 = std::set<char*, std::less<std::string>>;
+  using my_less = std::less<std::string>;
+  find_if(begin, end, my_less());
+  // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: prefer transparent functors
+  std::set2<int> control;
+}
+
+
diff --git a/test/clang-tidy/modernize-use-using-macros.cpp b/test/clang-tidy/modernize-use-using-macros.cpp
new file mode 100644 (file)
index 0000000..5d88d1d
--- /dev/null
@@ -0,0 +1,23 @@
+// RUN: %check_clang_tidy %s modernize-use-using %t -- \
+// RUN:   -config="{CheckOptions: [{key: modernize-use-using.IgnoreMacros, value: 0}]}" \
+// RUN:   -- -std=c++11
+
+#define CODE typedef int INT
+
+CODE;
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
+// CHECK-FIXES: #define CODE typedef int INT
+// CHECK-FIXES: CODE;
+
+struct Foo;
+#define Bar Baz
+typedef Foo Bar;
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
+// CHECK-FIXES: #define Bar Baz
+// CHECK-FIXES: using Baz = Foo;
+
+#define TYPEDEF typedef
+TYPEDEF Foo Bak;
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
+// CHECK-FIXES: #define TYPEDEF typedef
+// CHECK-FIXES: TYPEDEF Foo Bak;
diff --git a/test/clang-tidy/modernize-use-using.cpp b/test/clang-tidy/modernize-use-using.cpp
new file mode 100644 (file)
index 0000000..e8f4958
--- /dev/null
@@ -0,0 +1,164 @@
+// RUN: %check_clang_tidy %s modernize-use-using %t
+
+typedef int Type;
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef' [modernize-use-using]
+// CHECK-FIXES: using Type = int;
+
+typedef long LL;
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
+// CHECK-FIXES: using LL = long;
+
+typedef int Bla;
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
+// CHECK-FIXES: using Bla = int;
+
+typedef Bla Bla2;
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
+// CHECK-FIXES: using Bla2 = Bla;
+
+typedef void (*type)(int, int);
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
+// CHECK-FIXES: using type = void (*)(int, int);
+
+typedef void (*type2)();
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
+// CHECK-FIXES: using type2 = void (*)();
+
+class Class {
+  typedef long long Type;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use 'using' instead of 'typedef'
+  // CHECK-FIXES: using Type = long long;
+};
+
+typedef void (Class::*MyPtrType)(Bla) const;
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
+// CHECK-FIXES: using MyPtrType = void (Class::*)(Bla)[[ATTR:( __attribute__\(\(thiscall\)\))?]] const;
+
+class Iterable {
+public:
+  class Iterator {};
+};
+
+template <typename T>
+class Test {
+  typedef typename T::iterator Iter;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use 'using' instead of 'typedef'
+  // CHECK-FIXES: using Iter = typename T::iterator;
+};
+
+using balba = long long;
+
+union A {};
+
+typedef void (A::*PtrType)(int, int) const;
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
+// CHECK-FIXES: using PtrType = void (A::*)(int, int)[[ATTR]] const;
+
+typedef Class some_class;
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
+// CHECK-FIXES: using some_class = Class;
+
+typedef Class Cclass;
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
+// CHECK-FIXES: using Cclass = Class;
+
+typedef Cclass cclass2;
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
+// CHECK-FIXES: using cclass2 = Cclass;
+
+class cclass {};
+
+typedef void (cclass::*MyPtrType3)(Bla);
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
+// CHECK-FIXES: using MyPtrType3 = void (cclass::*)(Bla)[[ATTR]];
+
+using my_class = int;
+
+typedef Test<my_class *> another;
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
+// CHECK-FIXES: using another = Test<my_class *>;
+
+typedef int* PInt;
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
+// CHECK-FIXES: using PInt = int *;
+
+typedef int bla1, bla2, bla3;
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
+// CHECK-FIXES: typedef int bla1, bla2, bla3;
+
+#define CODE typedef int INT
+
+CODE;
+// CHECK-FIXES: #define CODE typedef int INT
+// CHECK-FIXES: CODE;
+
+struct Foo;
+#define Bar Baz
+typedef Foo Bar;
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
+// CHECK-FIXES: #define Bar Baz
+// CHECK-FIXES: using Baz = Foo;
+
+#define TYPEDEF typedef
+TYPEDEF Foo Bak;
+// CHECK-FIXES: #define TYPEDEF typedef
+// CHECK-FIXES: TYPEDEF Foo Bak;
+
+#define FOO Foo
+typedef FOO Bam;
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
+// CHECK-FIXES: #define FOO Foo
+// CHECK-FIXES: using Bam = Foo;
+
+typedef struct Foo Bap;
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
+// CHECK-FIXES: using Bap = struct Foo;
+
+struct Foo typedef Bap2;
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
+// CHECK-FIXES: using Bap2 = struct Foo;
+
+Foo typedef Bap3;
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
+// CHECK-FIXES: using Bap3 = Foo;
+
+typedef struct Unknown Baq;
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
+// CHECK-FIXES: using Baq = struct Unknown;
+
+struct Unknown2 typedef Baw;
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
+// CHECK-FIXES: using Baw = struct Unknown2;
+
+int typedef Bax;
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
+// CHECK-FIXES: using Bax = int;
+
+typedef struct Q1 { int a; } S1;
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
+// CHECK-FIXES: typedef struct Q1 { int a; } S1;
+typedef struct { int b; } S2;
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
+// CHECK-FIXES: typedef struct { int b; } S2;
+struct Q2 { int c; } typedef S3;
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
+// CHECK-FIXES: struct Q2 { int c; } typedef S3;
+struct { int d; } typedef S4;
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
+// CHECK-FIXES: struct { int d; } typedef S4;
+
+namespace my_space {
+  class my_cclass {};
+  typedef my_cclass FuncType;
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use 'using' instead of 'typedef'
+// CHECK-FIXES: using FuncType = my_cclass;
+}
+
+#define lol 4
+typedef unsigned Map[lol];
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
+// CHECK-FIXES: typedef unsigned Map[lol];
+
+typedef void (*fun_type)();
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
+// CHECK-FIXES: using fun_type = void (*)();
diff --git a/test/clang-tidy/mpi-buffer-deref.cpp b/test/clang-tidy/mpi-buffer-deref.cpp
new file mode 100644 (file)
index 0000000..67304d2
--- /dev/null
@@ -0,0 +1,50 @@
+// RUN: %check_clang_tidy %s mpi-buffer-deref %t -- -- -I %S/Inputs/mpi-type-mismatch
+
+#include "mpimock.h"
+
+void negativeTests() {
+  char *buf;
+  MPI_Send(&buf, 1, MPI_CHAR, 0, 0, MPI_COMM_WORLD);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer is insufficiently dereferenced: pointer->pointer [mpi-buffer-deref]
+
+  unsigned **buf2;
+  MPI_Send(buf2, 1, MPI_UNSIGNED, 0, 0, MPI_COMM_WORLD);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer is insufficiently dereferenced: pointer->pointer
+
+  short buf3[1][1];
+  MPI_Send(buf3, 1, MPI_SHORT, 0, 0, MPI_COMM_WORLD);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer is insufficiently dereferenced: array->array
+
+  long double _Complex *buf4[1];
+  MPI_Send(buf4, 1, MPI_C_LONG_DOUBLE_COMPLEX, 0, 0, MPI_COMM_WORLD);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer is insufficiently dereferenced: pointer->array
+
+  std::complex<float> *buf5[1][1];
+  MPI_Send(&buf5, 1, MPI_CXX_FLOAT_COMPLEX, 0, 0, MPI_COMM_WORLD);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer is insufficiently dereferenced: pointer->array->array->pointer
+}
+
+void positiveTests() {
+  char buf;
+  MPI_Send(&buf, 1, MPI_CHAR, 0, 0, MPI_COMM_WORLD);
+
+  unsigned *buf2;
+  MPI_Send(buf2, 1, MPI_UNSIGNED, 0, 0, MPI_COMM_WORLD);
+
+  short buf3[1][1];
+  MPI_Send(buf3[0], 1, MPI_SHORT, 0, 0, MPI_COMM_WORLD);
+
+  long double _Complex *buf4[1];
+  MPI_Send(*buf4, 1, MPI_C_LONG_DOUBLE_COMPLEX, 0, 0, MPI_COMM_WORLD);
+
+  long double _Complex buf5[1];
+  MPI_Send(buf5, 1, MPI_C_LONG_DOUBLE_COMPLEX, 0, 0, MPI_COMM_WORLD);
+
+  std::complex<float> *buf6[1][1];
+  MPI_Send(*buf6[0], 1, MPI_CXX_FLOAT_COMPLEX, 0, 0, MPI_COMM_WORLD);
+
+  // Referencing an array with '&' is valid, as this also points to the
+  // beginning of the array.
+  long double _Complex buf7[1];
+  MPI_Send(&buf7, 1, MPI_C_LONG_DOUBLE_COMPLEX, 0, 0, MPI_COMM_WORLD);
+}
diff --git a/test/clang-tidy/mpi-type-mismatch.cpp b/test/clang-tidy/mpi-type-mismatch.cpp
new file mode 100644 (file)
index 0000000..5e230b8
--- /dev/null
@@ -0,0 +1,255 @@
+// RUN: %check_clang_tidy %s mpi-type-mismatch %t -- -- -I %S/Inputs/mpi-type-mismatch
+
+#include "mpimock.h"
+
+void charNegativeTest() {
+  int buf;
+  MPI_Send(&buf, 1, MPI_CHAR, 0, 0, MPI_COMM_WORLD);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'int' does not match the MPI datatype 'MPI_CHAR'
+
+  short buf2;
+  MPI_Send(&buf2, 1, MPI_CHAR, 0, 0, MPI_COMM_WORLD);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'short' does not match the MPI datatype 'MPI_CHAR'
+
+  long buf3;
+  MPI_Send(&buf3, 1, MPI_CHAR, 0, 0, MPI_COMM_WORLD);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'long' does not match the MPI datatype 'MPI_CHAR'
+
+  int8_t buf4;
+  MPI_Send(&buf4, 1, MPI_CHAR, 0, 0, MPI_COMM_WORLD);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'int8_t' does not match the MPI datatype 'MPI_CHAR'
+
+  uint16_t buf5;
+  MPI_Send(&buf5, 1, MPI_CHAR, 0, 0, MPI_COMM_WORLD);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'uint16_t' does not match the MPI datatype 'MPI_CHAR'
+
+  long double _Complex buf6;
+  MPI_Send(&buf6, 1, MPI_CHAR, 0, 0, MPI_COMM_WORLD);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'long double _Complex' does not match the MPI datatype 'MPI_CHAR'
+
+  std::complex<float> buf7;
+  MPI_Send(&buf7, 1, MPI_CHAR, 0, 0, MPI_COMM_WORLD);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'complex<float>' does not match the MPI datatype 'MPI_CHAR'
+}
+
+void intNegativeTest() {
+  unsigned char buf;
+  MPI_Send(&buf, 1, MPI_INT, 0, 0, MPI_COMM_WORLD);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'unsigned char' does not match the MPI datatype 'MPI_INT'
+
+  unsigned buf2;
+  MPI_Send(&buf2, 1, MPI_INT, 0, 0, MPI_COMM_WORLD);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'unsigned int' does not match the MPI datatype 'MPI_INT'
+
+  short buf3;
+  MPI_Send(&buf3, 1, MPI_INT, 0, 0, MPI_COMM_WORLD);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'short' does not match the MPI datatype 'MPI_INT'
+
+  long buf4;
+  MPI_Send(&buf4, 1, MPI_INT, 0, 0, MPI_COMM_WORLD);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'long' does not match the MPI datatype 'MPI_INT'
+
+  int8_t buf5;
+  MPI_Send(&buf5, 1, MPI_INT, 0, 0, MPI_COMM_WORLD);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'int8_t' does not match the MPI datatype 'MPI_INT'
+
+  uint16_t buf6;
+  MPI_Send(&buf6, 1, MPI_INT, 0, 0, MPI_COMM_WORLD);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'uint16_t' does not match the MPI datatype 'MPI_INT'
+
+  long double _Complex buf7;
+  MPI_Send(&buf7, 1, MPI_INT, 0, 0, MPI_COMM_WORLD);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'long double _Complex' does not match the MPI datatype 'MPI_INT'
+
+  std::complex<float> buf8;
+  MPI_Send(&buf8, 1, MPI_INT, 0, 0, MPI_COMM_WORLD);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'complex<float>' does not match the MPI datatype 'MPI_INT'
+}
+
+void longNegativeTest() {
+  char buf;
+  MPI_Send(&buf, 1, MPI_LONG, 0, 0, MPI_COMM_WORLD);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'char' does not match the MPI datatype 'MPI_LONG'
+
+  unsigned buf2;
+  MPI_Send(&buf2, 1, MPI_LONG, 0, 0, MPI_COMM_WORLD);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'unsigned int' does not match the MPI datatype 'MPI_LONG'
+
+  unsigned short buf3;
+  MPI_Send(&buf3, 1, MPI_LONG, 0, 0, MPI_COMM_WORLD);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'unsigned short' does not match the MPI datatype 'MPI_LONG'
+
+  unsigned long buf4;
+  MPI_Send(&buf4, 1, MPI_LONG, 0, 0, MPI_COMM_WORLD);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'unsigned long' does not match the MPI datatype 'MPI_LONG'
+
+  int8_t buf5;
+  MPI_Send(&buf5, 1, MPI_LONG, 0, 0, MPI_COMM_WORLD);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'int8_t' does not match the MPI datatype 'MPI_LONG'
+
+  uint16_t buf6;
+  MPI_Send(&buf6, 1, MPI_LONG, 0, 0, MPI_COMM_WORLD);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'uint16_t' does not match the MPI datatype 'MPI_LONG'
+
+  long double _Complex buf7;
+  MPI_Send(&buf7, 1, MPI_LONG, 0, 0, MPI_COMM_WORLD);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'long double _Complex' does not match the MPI datatype 'MPI_LONG'
+
+  std::complex<float> buf8;
+  MPI_Send(&buf8, 1, MPI_LONG, 0, 0, MPI_COMM_WORLD);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'complex<float>' does not match the MPI datatype 'MPI_LONG'
+}
+
+void int8_tNegativeTest() {
+  char buf;
+  MPI_Send(&buf, 1, MPI_INT8_T, 0, 0, MPI_COMM_WORLD);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'char' does not match the MPI datatype 'MPI_INT8_T'
+
+  unsigned buf2;
+  MPI_Send(&buf2, 1, MPI_INT8_T, 0, 0, MPI_COMM_WORLD);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'unsigned int' does not match the MPI datatype 'MPI_INT8_T'
+
+  short buf3;
+  MPI_Send(&buf3, 1, MPI_INT8_T, 0, 0, MPI_COMM_WORLD);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'short' does not match the MPI datatype 'MPI_INT8_T'
+
+  unsigned long buf4;
+  MPI_Send(&buf4, 1, MPI_INT8_T, 0, 0, MPI_COMM_WORLD);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'unsigned long' does not match the MPI datatype 'MPI_INT8_T'
+
+  uint8_t buf5;
+  MPI_Send(&buf5, 1, MPI_INT8_T, 0, 0, MPI_COMM_WORLD);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'uint8_t' does not match the MPI datatype 'MPI_INT8_T'
+
+  uint16_t buf6;
+  MPI_Send(&buf6, 1, MPI_INT8_T, 0, 0, MPI_COMM_WORLD);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'uint16_t' does not match the MPI datatype 'MPI_INT8_T'
+
+  long double _Complex buf7;
+  MPI_Send(&buf7, 1, MPI_INT8_T, 0, 0, MPI_COMM_WORLD);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'long double _Complex' does not match the MPI datatype 'MPI_INT8_T'
+
+  std::complex<float> buf8;
+  MPI_Send(&buf8, 1, MPI_INT8_T, 0, 0, MPI_COMM_WORLD);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'complex<float>' does not match the MPI datatype 'MPI_INT8_T'
+}
+
+void complex_c_long_double_complexNegativeTest() {
+  char buf;
+  MPI_Send(&buf, 1, MPI_C_LONG_DOUBLE_COMPLEX, 0, 0, MPI_COMM_WORLD);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'char' does not match the MPI datatype 'MPI_C_LONG_DOUBLE_COMPLEX'
+
+  unsigned buf2;
+  MPI_Send(&buf2, 1, MPI_C_LONG_DOUBLE_COMPLEX, 0, 0, MPI_COMM_WORLD);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'unsigned int' does not match the MPI datatype 'MPI_C_LONG_DOUBLE_COMPLEX'
+
+  short buf3;
+  MPI_Send(&buf3, 1, MPI_C_LONG_DOUBLE_COMPLEX, 0, 0, MPI_COMM_WORLD);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'short' does not match the MPI datatype 'MPI_C_LONG_DOUBLE_COMPLEX'
+
+  unsigned long buf4;
+  MPI_Send(&buf4, 1, MPI_C_LONG_DOUBLE_COMPLEX, 0, 0, MPI_COMM_WORLD);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'unsigned long' does not match the MPI datatype 'MPI_C_LONG_DOUBLE_COMPLEX'
+
+  uint8_t buf5;
+  MPI_Send(&buf5, 1, MPI_C_LONG_DOUBLE_COMPLEX, 0, 0, MPI_COMM_WORLD);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'uint8_t' does not match the MPI datatype 'MPI_C_LONG_DOUBLE_COMPLEX'
+
+  uint16_t buf6;
+  MPI_Send(&buf6, 1, MPI_C_LONG_DOUBLE_COMPLEX, 0, 0, MPI_COMM_WORLD);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'uint16_t' does not match the MPI datatype 'MPI_C_LONG_DOUBLE_COMPLEX'
+
+  double _Complex buf7;
+  MPI_Send(&buf7, 1, MPI_C_LONG_DOUBLE_COMPLEX, 0, 0, MPI_COMM_WORLD);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'double _Complex' does not match the MPI datatype 'MPI_C_LONG_DOUBLE_COMPLEX'
+
+  std::complex<float> buf8;
+  MPI_Send(&buf8, 1, MPI_C_LONG_DOUBLE_COMPLEX, 0, 0, MPI_COMM_WORLD);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'complex<float>' does not match the MPI datatype 'MPI_C_LONG_DOUBLE_COMPLEX'
+}
+
+void complex_cxx_float_complexNegativeTest() {
+  char buf;
+  MPI_Send(&buf, 1, MPI_CXX_FLOAT_COMPLEX, 0, 0, MPI_COMM_WORLD);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'char' does not match the MPI datatype 'MPI_CXX_FLOAT_COMPLEX'
+
+  unsigned buf2;
+  MPI_Send(&buf2, 1, MPI_CXX_FLOAT_COMPLEX, 0, 0, MPI_COMM_WORLD);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'unsigned int' does not match the MPI datatype 'MPI_CXX_FLOAT_COMPLEX'
+
+  short buf3;
+  MPI_Send(&buf3, 1, MPI_CXX_FLOAT_COMPLEX, 0, 0, MPI_COMM_WORLD);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'short' does not match the MPI datatype 'MPI_CXX_FLOAT_COMPLEX'
+
+  unsigned long buf4;
+  MPI_Send(&buf4, 1, MPI_CXX_FLOAT_COMPLEX, 0, 0, MPI_COMM_WORLD);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'unsigned long' does not match the MPI datatype 'MPI_CXX_FLOAT_COMPLEX'
+
+  uint8_t buf5;
+  MPI_Send(&buf5, 1, MPI_CXX_FLOAT_COMPLEX, 0, 0, MPI_COMM_WORLD);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'uint8_t' does not match the MPI datatype 'MPI_CXX_FLOAT_COMPLEX'
+
+  uint16_t buf6;
+  MPI_Send(&buf6, 1, MPI_CXX_FLOAT_COMPLEX, 0, 0, MPI_COMM_WORLD);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'uint16_t' does not match the MPI datatype 'MPI_CXX_FLOAT_COMPLEX'
+
+  double _Complex buf7;
+  MPI_Send(&buf7, 1, MPI_CXX_FLOAT_COMPLEX, 0, 0, MPI_COMM_WORLD);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'double _Complex' does not match the MPI datatype 'MPI_CXX_FLOAT_COMPLEX'
+
+  std::complex<double> buf8;
+  MPI_Send(&buf8, 1, MPI_CXX_FLOAT_COMPLEX, 0, 0, MPI_COMM_WORLD);
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: buffer type 'complex<double>' does not match the MPI datatype 'MPI_CXX_FLOAT_COMPLEX'
+}
+
+void skippedTypesTests() {
+  // typedefs, user defined MPI and nullptr types are skipped
+  typedef char CHAR;
+  CHAR buf;
+  MPI_Send(&buf, 1, MPI_CXX_FLOAT_COMPLEX, 0, 0, MPI_COMM_WORLD);
+
+  typedef unsigned UNSIGNED;
+  UNSIGNED buf2;
+  MPI_Send(&buf2, 1, MPI_CXX_FLOAT_COMPLEX, 0, 0, MPI_COMM_WORLD);
+
+#define _MPI_LONG MPI_LONG
+  int buf3;
+  MPI_Send(&buf3, 1, _MPI_LONG, 0, 0, MPI_COMM_WORLD);
+
+#define _MPI_CXX_FLOAT_COMPLEX MPI_CXX_FLOAT_COMPLEX
+  short buf4;
+  MPI_Send(&buf4, 1, _MPI_CXX_FLOAT_COMPLEX, 0, 0, MPI_COMM_WORLD);
+
+  MPI_Send(NULL, 1, MPI_LONG, 0, 0, MPI_COMM_WORLD);
+}
+
+void positiveTests() {
+  char buf;
+  MPI_Send(&buf, 1, MPI_CHAR, 0, 0, MPI_COMM_WORLD);
+
+  int buf2;
+  MPI_Send(&buf2, 1, MPI_INT, 0, 0, MPI_COMM_WORLD);
+
+  long buf3;
+  MPI_Send(&buf3, 1, MPI_LONG, 0, 0, MPI_COMM_WORLD);
+
+  int8_t buf4;
+  MPI_Send(&buf4, 1, MPI_INT8_T, 0, 0, MPI_COMM_WORLD);
+
+  long double _Complex buf5;
+  MPI_Send(&buf5, 1, MPI_C_LONG_DOUBLE_COMPLEX, 0, 0, MPI_COMM_WORLD);
+
+  std::complex<float> buf6;
+  MPI_Send(&buf6, 1, MPI_CXX_FLOAT_COMPLEX, 0, 0, MPI_COMM_WORLD);
+
+  uint8_t buf7;
+  MPI_Send(&buf7, 1, MPI_UINT8_T, 0, 0, MPI_COMM_WORLD);
+
+  uint16_t buf8;
+  MPI_Send(&buf8, 1, MPI_UINT16_T, 0, 0, MPI_COMM_WORLD);
+
+  // On some systems like PPC or ARM, 'char' is unsigned by default which is why
+  // distinct signedness for the buffer and MPI type is tolerated.
+  unsigned char buf9;
+  MPI_Send(&buf9, 1, MPI_CHAR, 0, 0, MPI_COMM_WORLD);
+}
diff --git a/test/clang-tidy/nolint.cpp b/test/clang-tidy/nolint.cpp
new file mode 100644 (file)
index 0000000..7e828bf
--- /dev/null
@@ -0,0 +1,38 @@
+// RUN: %check_clang_tidy %s google-explicit-constructor,clang-diagnostic-unused-variable,clang-analyzer-core.UndefinedBinaryOperatorResult %t -- -extra-arg=-Wunused-variable -- -I%S/Inputs/nolint
+
+#include "trigger_warning.h"
+void I(int& Out) {
+  int In;
+  A1(In, Out);
+}
+// CHECK-MESSAGES-NOT: trigger_warning.h:{{.*}} warning
+// CHECK-MESSAGES-NOT: :[[@LINE-4]]:{{.*}} note
+
+class A { A(int i); };
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: single-argument constructors must be marked explicit
+
+class B { B(int i); }; // NOLINT
+
+class C { C(int i); }; // NOLINT(we-dont-care-about-categories-yet)
+
+void f() {
+  int i;
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: unused variable 'i' [clang-diagnostic-unused-variable]
+  int j; // NOLINT
+}
+
+#define MACRO(X) class X { X(int i); };
+MACRO(D)
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: single-argument constructors must be marked explicit
+MACRO(E) // NOLINT
+
+#define MACRO_NOARG class F { F(int i); };
+MACRO_NOARG // NOLINT
+
+#define MACRO_NOLINT class G { G(int i); }; // NOLINT
+MACRO_NOLINT
+
+#define DOUBLE_MACRO MACRO(H) // NOLINT
+DOUBLE_MACRO
+
+// CHECK-MESSAGES: Suppressed 8 warnings (8 NOLINT)
diff --git a/test/clang-tidy/nolintnextline.cpp b/test/clang-tidy/nolintnextline.cpp
new file mode 100644 (file)
index 0000000..d18f335
--- /dev/null
@@ -0,0 +1,33 @@
+class A { A(int i); };
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: single-argument constructors must be marked explicit
+
+// NOLINTNEXTLINE
+class B { B(int i); };
+
+// NOLINTNEXTLINE(we-dont-care-about-categories-yet)
+class C { C(int i); };
+
+
+// NOLINTNEXTLINE
+
+class D { D(int i); };
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: single-argument constructors must be marked explicit
+
+// NOLINTNEXTLINE
+//
+class E { E(int i); };
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: single-argument constructors must be marked explicit
+
+#define MACRO(X) class X { X(int i); };
+MACRO(F)
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: single-argument constructors must be marked explicit
+// NOLINTNEXTLINE
+MACRO(G)
+
+#define MACRO_NOARG class H { H(int i); };
+// NOLINTNEXTLINE
+MACRO_NOARG
+
+// CHECK-MESSAGES: Suppressed 4 warnings (4 NOLINT)
+
+// RUN: %check_clang_tidy %s google-explicit-constructor %t --
diff --git a/test/clang-tidy/overlapping.cpp b/test/clang-tidy/overlapping.cpp
new file mode 100644 (file)
index 0000000..f970a13
--- /dev/null
@@ -0,0 +1,10 @@
+// RUN: clang-tidy -checks=-*,llvm-include-order -header-filter=.* %s \
+// RUN:   -- -isystem %S/Inputs/Headers -I %S/Inputs/overlapping | \
+// RUN:   not grep "note: this fix will not be applied because it overlaps with another fix"
+
+#include <s.h>
+#include "o.h"
+
+// Test that clang-tidy takes into account in which file we are doing the
+// replacements to determine if they overlap or not. In the file "o.h" there is
+// a similar error at the same file offset, but they do not overlap.
diff --git a/test/clang-tidy/performance-faster-string-find.cpp b/test/clang-tidy/performance-faster-string-find.cpp
new file mode 100644 (file)
index 0000000..b36e323
--- /dev/null
@@ -0,0 +1,110 @@
+// RUN: %check_clang_tidy %s performance-faster-string-find %t -- \
+// RUN:   -config="{CheckOptions: \
+// RUN:             [{key: performance-faster-string-find.StringLikeClasses, \
+// RUN:               value: 'std::basic_string; ::llvm::StringRef;'}]}" --
+
+namespace std {
+template <typename Char>
+struct basic_string {
+  int find(const Char *, int = 0) const;
+  int find(const Char *, int, int) const;
+  int rfind(const Char *) const;
+  int find_first_of(const Char *) const;
+  int find_first_not_of(const Char *) const;
+  int find_last_of(const Char *) const;
+  int find_last_not_of(const Char *) const;
+};
+
+typedef basic_string<char> string;
+typedef basic_string<wchar_t> wstring;
+}  // namespace std
+
+namespace llvm {
+struct StringRef {
+  int find(const char *) const;
+};
+}  // namespace llvm
+
+struct NotStringRef {
+  int find(const char *);
+};
+
+void StringFind() {
+  std::string Str;
+
+  Str.find("a");
+  // CHECK-MESSAGES: [[@LINE-1]]:12: warning: 'find' called with a string literal consisting of a single character; consider using the more effective overload accepting a character [performance-faster-string-find]
+  // CHECK-FIXES: Str.find('a');
+
+  // Works with the pos argument.
+  Str.find("a", 1);
+  // CHECK-MESSAGES: [[@LINE-1]]:12: warning: 'find' called with a string literal
+  // CHECK-FIXES: Str.find('a', 1);
+
+  // Doens't work with strings smaller or larger than 1 char.
+  Str.find("");
+  Str.find("ab");
+
+  // Doesn't do anything with the 3 argument overload.
+  Str.find("a", 1, 1);
+
+  // Other methods that can also be replaced
+  Str.rfind("a");
+  // CHECK-MESSAGES: [[@LINE-1]]:13: warning: 'rfind' called with a string literal
+  // CHECK-FIXES: Str.rfind('a');
+  Str.find_first_of("a");
+  // CHECK-MESSAGES: [[@LINE-1]]:21: warning: 'find_first_of' called with a string
+  // CHECK-FIXES: Str.find_first_of('a');
+  Str.find_first_not_of("a");
+  // CHECK-MESSAGES: [[@LINE-1]]:25: warning: 'find_first_not_of' called with a
+  // CHECK-FIXES: Str.find_first_not_of('a');
+  Str.find_last_of("a");
+  // CHECK-MESSAGES: [[@LINE-1]]:20: warning: 'find_last_of' called with a string
+  // CHECK-FIXES: Str.find_last_of('a');
+  Str.find_last_not_of("a");
+  // CHECK-MESSAGES: [[@LINE-1]]:24: warning: 'find_last_not_of' called with a
+  // CHECK-FIXES: Str.find_last_not_of('a');
+
+  // std::wstring should work.
+  std::wstring WStr;
+  WStr.find(L"n");
+  // CHECK-MESSAGES: [[@LINE-1]]:13: warning: 'find' called with a string literal
+  // CHECK-FIXES: Str.find(L'n');
+  // Even with unicode that fits in one wide char.
+  WStr.find(L"\x3A9");
+  // CHECK-MESSAGES: [[@LINE-1]]:13: warning: 'find' called with a string literal
+  // CHECK-FIXES: Str.find(L'\x3A9');
+
+  // Also with other types, but only if it was specified in the options.
+  llvm::StringRef sr;
+  sr.find("x");
+  // CHECK-MESSAGES: [[@LINE-1]]:11: warning: 'find' called with a string literal
+  // CHECK-FIXES: sr.find('x');
+  NotStringRef nsr;
+  nsr.find("x");
+}
+
+
+template <typename T>
+int FindTemplateDependant(T value) {
+  return value.find("A");
+}
+template <typename T>
+int FindTemplateNotDependant(T pos) {
+  return std::string().find("A", pos);
+  // CHECK-MESSAGES: [[@LINE-1]]:29: warning: 'find' called with a string literal
+  // CHECK-FIXES: return std::string().find('A', pos);
+}
+
+int FindStr() {
+  return FindTemplateDependant(std::string()) + FindTemplateNotDependant(1);
+}
+
+#define STR_MACRO(str) str.find("A")
+#define POS_MACRO(pos) std::string().find("A",pos)
+
+int Macros() {
+  return STR_MACRO(std::string()) + POS_MACRO(1);
+  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: 'find' called with a string literal
+  // CHECK-MESSAGES: [[@LINE-2]]:37: warning: 'find' called with a string literal
+}
diff --git a/test/clang-tidy/performance-for-range-copy-warn-on-all-auto-copies.cpp b/test/clang-tidy/performance-for-range-copy-warn-on-all-auto-copies.cpp
new file mode 100644 (file)
index 0000000..75655a6
--- /dev/null
@@ -0,0 +1,39 @@
+// RUN: %check_clang_tidy %s performance-for-range-copy %t -config="{CheckOptions: [{key: "performance-for-range-copy.WarnOnAllAutoCopies", value: 1}]}" -- -std=c++11
+
+template <typename T>
+struct Iterator {
+  void operator++() {}
+  const T& operator*() {
+    static T* TT = new T();
+    return *TT;
+  }
+  bool operator!=(const Iterator &) { return false; }
+};
+template <typename T>
+struct View {
+  T begin() { return T(); }
+  T begin() const { return T(); }
+  T end() { return T(); }
+  T end() const { return T(); }
+};
+
+struct S {
+  S();
+  S(const S &);
+  ~S();
+  S &operator=(const S &);
+};
+
+void NegativeLoopVariableNotAuto() {
+  for (S S1 : View<Iterator<S>>()) {
+    S* S2 = &S1;
+  }
+}
+
+void PositiveTriggeredForAutoLoopVariable() {
+  for (auto S1 : View<Iterator<S>>()) {
+    // CHECK-MESSAGES: [[@LINE-1]]:13: warning: the loop variable's type is not a reference type; this creates a copy in each iteration; consider making this a reference [performance-for-range-copy]
+    // CHECK-FIXES: for (const auto& S1 : View<Iterator<S>>()) {
+    S* S2 = &S1;
+  }
+}
diff --git a/test/clang-tidy/performance-for-range-copy.cpp b/test/clang-tidy/performance-for-range-copy.cpp
new file mode 100644 (file)
index 0000000..88ec45b
--- /dev/null
@@ -0,0 +1,248 @@
+// RUN: %check_clang_tidy %s performance-for-range-copy %t -- -- -std=c++11 -fno-delayed-template-parsing
+
+namespace std {
+
+template <typename _Tp>
+struct remove_reference { typedef _Tp type; };
+
+template <typename _Tp>
+constexpr typename std::remove_reference<_Tp>::type &&move(_Tp &&__t) {
+  return static_cast<typename std::remove_reference<_Tp>::type &&>(__t);
+}
+
+} // std
+
+template <typename T>
+struct Iterator {
+  void operator++() {}
+  const T& operator*() {
+    static T* TT = new T();
+    return *TT;
+  }
+  bool operator!=(const Iterator &) { return false; }
+  typedef const T& const_reference;
+};
+template <typename T>
+struct View {
+  T begin() { return T(); }
+  T begin() const { return T(); }
+  T end() { return T(); }
+  T end() const { return T(); }
+  typedef typename T::const_reference const_reference;
+};
+
+struct ConstructorConvertible {
+};
+
+struct S {
+  S();
+  S(const S &);
+  S(const ConstructorConvertible&) {}
+  ~S();
+  S &operator=(const S &);
+};
+
+struct Convertible {
+  operator S() const {
+    return S();
+  }
+};
+
+void negativeConstReference() {
+  for (const S &S1 : View<Iterator<S>>()) {
+  }
+}
+
+void negativeUserDefinedConversion() {
+  Convertible C[0];
+  for (const S &S1 : C) {
+  }
+}
+
+void negativeImplicitConstructorConversion() {
+  ConstructorConvertible C[0];
+  for (const S &S1 : C) {
+  }
+}
+
+template <typename T>
+void uninstantiated() {
+  for (const S S1 : View<Iterator<S>>()) {}
+  // CHECK-MESSAGES: [[@LINE-1]]:16: warning: the loop variable's type is not a reference type; this creates a copy in each iteration; consider making this a reference [performance-for-range-copy]
+  // CHECK-FIXES: {{^}}  for (const S& S1 : View<Iterator<S>>()) {}
+
+  // Don't warn on dependent types.
+  for (const T t1 : View<Iterator<T>>()) {
+  }
+}
+
+template <typename T>
+void instantiated() {
+  for (const S S2 : View<Iterator<S>>()) {}
+  // CHECK-MESSAGES: [[@LINE-1]]:16: warning: the loop variable's type is {{.*}}
+  // CHECK-FIXES: {{^}}  for (const S& S2 : View<Iterator<S>>()) {}
+
+  for (const T T2 : View<Iterator<T>>()) {}
+  // CHECK-MESSAGES: [[@LINE-1]]:16: warning: the loop variable's type is {{.*}}
+  // CHECK-FIXES: {{^}}  for (const T& T2 : View<Iterator<T>>()) {}
+}
+
+template <typename T>
+void instantiatedNegativeTypedefConstReference() {
+  for (typename T::const_reference T2 : T()) {
+    S S1 = T2;
+  }
+}
+
+void f() {
+  instantiated<int>();
+  instantiated<S>();
+  instantiatedNegativeTypedefConstReference<View<Iterator<S>>>();
+}
+
+struct Mutable {
+  Mutable() {}
+  Mutable(const Mutable &) = default;
+  Mutable(const Mutable &, const Mutable &) {}
+  void setBool(bool B) {}
+  bool constMethod() const {
+    return true;
+  }
+  Mutable& operator[](int I) {
+    return *this;
+  }
+  bool operator==(const Mutable &Other) const {
+    return true;
+  }
+  ~Mutable() {}
+};
+
+Mutable& operator<<(Mutable &Out, bool B) {
+  Out.setBool(B);
+  return Out;
+}
+
+bool operator!=(const Mutable& M1, const Mutable& M2) {
+  return false;
+}
+
+void use(const Mutable &M);
+void use(int I);
+void useTwice(const Mutable &M1, const Mutable &M2);
+void useByValue(Mutable M);
+void useByConstValue(const Mutable M);
+void mutate(Mutable *M);
+void mutate(Mutable &M);
+void onceConstOnceMutated(const Mutable &M1, Mutable &M2);
+
+void negativeVariableIsMutated() {
+  for (auto M : View<Iterator<Mutable>>()) {
+    mutate(M);
+  }
+  for (auto M : View<Iterator<Mutable>>()) {
+    mutate(&M);
+  }
+  for (auto M : View<Iterator<Mutable>>()) {
+    M.setBool(true);
+  }
+}
+
+void negativeOnceConstOnceMutated() {
+  for (auto M : View<Iterator<Mutable>>()) {
+    onceConstOnceMutated(M, M);
+  }
+}
+
+void negativeVarIsMoved() {
+  for (auto M : View<Iterator<Mutable>>()) {
+    auto Moved = std::move(M);
+  }
+}
+
+void negativeNonConstOperatorIsInvoked() {
+  for (auto NonConstOperatorInvokee : View<Iterator<Mutable>>()) {
+    auto& N = NonConstOperatorInvokee[0];
+  }
+}
+
+void negativeNonConstNonMemberOperatorInvoked() {
+  for (auto NonConstOperatorInvokee : View<Iterator<Mutable>>()) {
+    NonConstOperatorInvokee << true;
+  }
+}
+
+void negativeConstCheapToCopy() {
+  for (const int I : View<Iterator<int>>()) {
+  }
+}
+
+void negativeConstCheapToCopyTypedef() {
+  typedef const int ConstInt;
+  for (ConstInt C  : View<Iterator<ConstInt>>()) {
+  }
+}
+
+void negativeCheapToCopy() {
+  for (int I : View<Iterator<int>>()) {
+    use(I);
+  }
+}
+
+void negativeCheapToCopyTypedef() {
+  typedef int Int;
+  for (Int I : View<Iterator<Int>>()) {
+    use(I);
+  }
+}
+
+void positiveOnlyConstMethodInvoked() {
+  for (auto M : View<Iterator<Mutable>>()) {
+    // CHECK-MESSAGES: [[@LINE-1]]:13: warning: loop variable is copied but only used as const reference; consider making it a const reference [performance-for-range-copy]
+    // CHECK-FIXES: for (const auto& M : View<Iterator<Mutable>>()) {
+    M.constMethod();
+  }
+}
+
+void positiveOnlyUsedAsConstArguments() {
+  for (auto UsedAsConst : View<Iterator<Mutable>>()) {
+    // CHECK-MESSAGES: [[@LINE-1]]:13: warning: loop variable is copied but only used as const reference; consider making it a const reference [performance-for-range-copy]
+    // CHECK-FIXES: for (const auto& UsedAsConst : View<Iterator<Mutable>>()) {
+    use(UsedAsConst);
+    useTwice(UsedAsConst, UsedAsConst);
+    useByValue(UsedAsConst);
+    useByConstValue(UsedAsConst);
+  }
+}
+
+void positiveOnlyUsedInCopyConstructor() {
+  for (auto A : View<Iterator<Mutable>>()) {
+    // CHECK-MESSAGES: [[@LINE-1]]:13: warning: loop variable is copied but only used as const reference; consider making it a const reference [performance-for-range-copy]
+    // CHECK-FIXES: for (const auto& A : View<Iterator<Mutable>>()) {
+    Mutable Copy = A;
+    Mutable Copy2(A);
+  }
+}
+
+void positiveTwoConstConstructorArgs() {
+  for (auto A : View<Iterator<Mutable>>()) {
+    // CHECK-MESSAGES: [[@LINE-1]]:13: warning: loop variable is copied but only used as const reference; consider making it a const reference [performance-for-range-copy]
+    // CHECK-FIXES: for (const auto& A : View<Iterator<Mutable>>()) {
+    Mutable Copy(A, A);
+  }
+}
+
+void PositiveConstMemberOperatorInvoked() {
+  for (auto ConstOperatorInvokee : View<Iterator<Mutable>>()) {
+    // CHECK-MESSAGES: [[@LINE-1]]:13: warning: loop variable is copied but only used as const reference; consider making it a const reference [performance-for-range-copy]
+    // CHECK-FIXES: for (const auto& ConstOperatorInvokee : View<Iterator<Mutable>>()) {
+    bool result = ConstOperatorInvokee == Mutable();
+  }
+}
+
+void PositiveConstNonMemberOperatorInvoked() {
+  for (auto ConstOperatorInvokee : View<Iterator<Mutable>>()) {
+    // CHECK-MESSAGES: [[@LINE-1]]:13: warning: loop variable is copied but only used as const reference; consider making it a const reference [performance-for-range-copy]
+    // CHECK-FIXES: for (const auto& ConstOperatorInvokee : View<Iterator<Mutable>>()) {
+    bool result = ConstOperatorInvokee != Mutable();
+  }
+}
diff --git a/test/clang-tidy/performance-implicit-cast-in-loop.cpp b/test/clang-tidy/performance-implicit-cast-in-loop.cpp
new file mode 100644 (file)
index 0000000..788debe
--- /dev/null
@@ -0,0 +1,161 @@
+// RUN: %check_clang_tidy %s performance-implicit-cast-in-loop %t
+
+// ---------- Classes used in the tests ----------
+
+// Iterator returning by value.
+template <typename T>
+struct Iterator {
+  void operator++();
+  T operator*();
+  bool operator!=(const Iterator& other);
+};
+
+// Iterator returning by reference.
+template <typename T>
+struct RefIterator {
+  void operator++();
+  T& operator*();
+  bool operator!=(const RefIterator& other);
+};
+
+// The template argument is an iterator type, and a view is an object you can
+// run a for loop on.
+template <typename T>
+struct View {
+  T begin();
+  T end();
+};
+
+// With this class, the implicit cast is a call to the (implicit) constructor of
+// the class.
+template <typename T>
+class ImplicitWrapper {
+ public:
+  // Implicit!
+  ImplicitWrapper(const T& t);
+};
+
+// With this class, the implicit cast is a call to the conversion operators of
+// SimpleClass and ComplexClass.
+template <typename T>
+class OperatorWrapper {
+ public:
+  explicit OperatorWrapper(const T& t);
+};
+
+struct SimpleClass {
+  int foo;
+  operator OperatorWrapper<SimpleClass>();
+};
+
+// The materialize expression is not the same when the class has a destructor,
+// so we make sure we cover that case too.
+class ComplexClass {
+ public:
+  ComplexClass();
+  ~ComplexClass();
+  operator OperatorWrapper<ComplexClass>();
+};
+
+typedef View<Iterator<SimpleClass>> SimpleView;
+typedef View<RefIterator<SimpleClass>> SimpleRefView;
+typedef View<Iterator<ComplexClass>> ComplexView;
+typedef View<RefIterator<ComplexClass>> ComplexRefView;
+
+// ---------- The test themselves ----------
+// For each test we do, in the same order, const ref, non const ref, const
+// value, non const value.
+
+void SimpleClassIterator() {
+  for (const SimpleClass& foo : SimpleView()) {}
+  // This line does not compile because a temporary cannot be assigned to a non
+  // const reference.
+  // for (SimpleClass& foo : SimpleView()) {}
+  for (const SimpleClass foo : SimpleView()) {}
+  for (SimpleClass foo : SimpleView()) {}
+}
+
+void SimpleClassRefIterator() {
+  for (const SimpleClass& foo : SimpleRefView()) {}
+  for (SimpleClass& foo : SimpleRefView()) {}
+  for (const SimpleClass foo : SimpleRefView()) {}
+  for (SimpleClass foo : SimpleRefView()) {}
+}
+
+void ComplexClassIterator() {
+  for (const ComplexClass& foo : ComplexView()) {}
+  // for (ComplexClass& foo : ComplexView()) {}
+  for (const ComplexClass foo : ComplexView()) {}
+  for (ComplexClass foo : ComplexView()) {}
+}
+
+void ComplexClassRefIterator() {
+  for (const ComplexClass& foo : ComplexRefView()) {}
+  for (ComplexClass& foo : ComplexRefView()) {}
+  for (const ComplexClass foo : ComplexRefView()) {}
+  for (ComplexClass foo : ComplexRefView()) {}
+}
+
+void ImplicitSimpleClassIterator() {
+  for (const ImplicitWrapper<SimpleClass>& foo : SimpleView()) {}
+  // CHECK-MESSAGES: [[@LINE-1]]:{{[0-9]*}}: warning: the type of the loop variable 'foo' is different from the one returned by the iterator and generates an implicit cast; you can either change the type to the correct one ('const SimpleClass &' but 'const auto&' is always a valid option) or remove the reference to make it explicit that you are creating a new value [performance-implicit-cast-in-loop]
+  // for (ImplicitWrapper<SimpleClass>& foo : SimpleView()) {}
+  for (const ImplicitWrapper<SimpleClass> foo : SimpleView()) {}
+  for (ImplicitWrapper<SimpleClass>foo : SimpleView()) {}
+}
+
+void ImplicitSimpleClassRefIterator() {
+  for (const ImplicitWrapper<SimpleClass>& foo : SimpleRefView()) {}
+  // CHECK-MESSAGES: [[@LINE-1]]:{{[0-9]*}}: warning: the type of the{{.*'const SimpleClass &'.*}}
+  // for (ImplicitWrapper<SimpleClass>& foo : SimpleRefView()) {}
+  for (const ImplicitWrapper<SimpleClass> foo : SimpleRefView()) {}
+  for (ImplicitWrapper<SimpleClass>foo : SimpleRefView()) {}
+}
+
+void ImplicitComplexClassIterator() {
+  for (const ImplicitWrapper<ComplexClass>& foo : ComplexView()) {}
+  // CHECK-MESSAGES: [[@LINE-1]]:{{[0-9]*}}: warning: the type of the{{.*'const ComplexClass &'.*}}
+  // for (ImplicitWrapper<ComplexClass>& foo : ComplexView()) {}
+  for (const ImplicitWrapper<ComplexClass> foo : ComplexView()) {}
+  for (ImplicitWrapper<ComplexClass>foo : ComplexView()) {}
+}
+
+void ImplicitComplexClassRefIterator() {
+  for (const ImplicitWrapper<ComplexClass>& foo : ComplexRefView()) {}
+  // CHECK-MESSAGES: [[@LINE-1]]:{{[0-9]*}}: warning: the type of the{{.*'const ComplexClass &'.*}}
+  // for (ImplicitWrapper<ComplexClass>& foo : ComplexRefView()) {}
+  for (const ImplicitWrapper<ComplexClass> foo : ComplexRefView()) {}
+  for (ImplicitWrapper<ComplexClass>foo : ComplexRefView()) {}
+}
+
+void OperatorSimpleClassIterator() {
+  for (const OperatorWrapper<SimpleClass>& foo : SimpleView()) {}
+  // CHECK-MESSAGES: [[@LINE-1]]:{{[0-9]*}}: warning: the type of the{{.*'const SimpleClass &'.*}}
+  // for (OperatorWrapper<SimpleClass>& foo : SimpleView()) {}
+  for (const OperatorWrapper<SimpleClass> foo : SimpleView()) {}
+  for (OperatorWrapper<SimpleClass>foo : SimpleView()) {}
+}
+
+void OperatorSimpleClassRefIterator() {
+  for (const OperatorWrapper<SimpleClass>& foo : SimpleRefView()) {}
+  // CHECK-MESSAGES: [[@LINE-1]]:{{[0-9]*}}: warning: the type of the{{.*'const SimpleClass &'.*}}
+  // for (OperatorWrapper<SimpleClass>& foo : SimpleRefView()) {}
+  for (const OperatorWrapper<SimpleClass> foo : SimpleRefView()) {}
+  for (OperatorWrapper<SimpleClass>foo : SimpleRefView()) {}
+}
+
+void OperatorComplexClassIterator() {
+  for (const OperatorWrapper<ComplexClass>& foo : ComplexView()) {}
+  // CHECK-MESSAGES: [[@LINE-1]]:{{[0-9]*}}: warning: the type of the{{.*'const ComplexClass &'.*}}
+  // for (OperatorWrapper<ComplexClass>& foo : ComplexView()) {}
+  for (const OperatorWrapper<ComplexClass> foo : ComplexView()) {}
+  for (OperatorWrapper<ComplexClass>foo : ComplexView()) {}
+}
+
+void OperatorComplexClassRefIterator() {
+  for (const OperatorWrapper<ComplexClass>& foo : ComplexRefView()) {}
+  // CHECK-MESSAGES: [[@LINE-1]]:{{[0-9]*}}: warning: the type of the{{.*'const ComplexClass &'.*}}
+  // for (OperatorWrapper<ComplexClass>& foo : ComplexRefView()) {}
+  for (const OperatorWrapper<ComplexClass> foo : ComplexRefView()) {}
+  for (OperatorWrapper<ComplexClass>foo : ComplexRefView()) {}
+}
diff --git a/test/clang-tidy/performance-inefficient-string-concatenation.cpp b/test/clang-tidy/performance-inefficient-string-concatenation.cpp
new file mode 100644 (file)
index 0000000..f4b293a
--- /dev/null
@@ -0,0 +1,44 @@
+// RUN: %check_clang_tidy %s performance-inefficient-string-concatenation %t
+
+namespace std {
+template <typename T>
+class basic_string {
+public:
+  basic_string() {}
+  ~basic_string() {}
+  basic_string<T> *operator+=(const basic_string<T> &) {}
+  friend basic_string<T> operator+(const basic_string<T> &, const basic_string<T> &) {}
+};
+typedef basic_string<char> string;
+typedef basic_string<wchar_t> wstring;
+}
+
+void f(std::string) {}
+std::string g(std::string) {}
+
+int main() {
+  std::string mystr1, mystr2;
+  std::wstring mywstr1, mywstr2;
+
+  for (int i = 0; i < 10; ++i) {
+    f(mystr1 + mystr2 + mystr1);
+    // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: string concatenation results in allocation of unnecessary temporary strings; consider using 'operator+=' or 'string::append()' instead
+    mystr1 = mystr1 + mystr2;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: string concatenation
+    mystr1 = mystr2 + mystr2 + mystr2;
+    // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: string concatenation
+    mystr1 = mystr2 + mystr1;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: string concatenation
+    mywstr1 = mywstr2 + mywstr1;
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: string concatenation
+    mywstr1 = mywstr2 + mywstr2 + mywstr2;
+    // CHECK-MESSAGES: :[[@LINE-1]]:33: warning: string concatenation
+
+    mywstr1 = mywstr2 + mywstr2;
+    mystr1 = mystr2 + mystr2;
+    mystr1 += mystr2;
+    f(mystr2 + mystr1);
+    mystr1 = g(mystr1);
+  }
+  return 0;
+}
diff --git a/test/clang-tidy/performance-inefficient-vector-operation.cpp b/test/clang-tidy/performance-inefficient-vector-operation.cpp
new file mode 100644 (file)
index 0000000..a04f66e
--- /dev/null
@@ -0,0 +1,277 @@
+// RUN: %check_clang_tidy %s performance-inefficient-vector-operation %t -- -format-style=llvm -- --std=c++11
+
+namespace std {
+
+typedef int size_t;
+
+template<class E> class initializer_list {
+public:
+  using value_type = E;
+  using reference = E&;
+  using const_reference = const E&;
+  using size_type = size_t;
+  using iterator = const E*;
+  using const_iterator = const E*;
+  initializer_list();
+  size_t size() const; // number of elements
+  const E* begin() const; // first element
+  const E* end() const; // one past the last element
+};
+
+// initializer list range access
+template<class E> const E* begin(initializer_list<E> il);
+template<class E> const E* end(initializer_list<E> il);
+
+template <class T>
+class vector {
+ public:
+  typedef T* iterator;
+  typedef const T* const_iterator;
+  typedef T& reference;
+  typedef const T& const_reference;
+  typedef size_t size_type;
+
+  explicit vector();
+  explicit vector(size_type n);
+
+  void push_back(const T& val);
+
+  template <class... Args> void emplace_back(Args &&... args);
+
+  void reserve(size_t n);
+  void resize(size_t n);
+
+  size_t size();
+  const_reference operator[] (size_type) const;
+  reference operator[] (size_type);
+
+  const_iterator begin() const;
+  const_iterator end() const;
+};
+} // namespace std
+
+class Foo {
+ public:
+  explicit Foo(int);
+};
+
+class Bar {
+ public:
+  Bar(int);
+};
+
+int Op(int);
+
+void f(std::vector<int>& t) {
+  {
+    std::vector<int> v0;
+    // CHECK-FIXES: v0.reserve(10);
+    for (int i = 0; i < 10; ++i)
+      v0.push_back(i);
+      // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called inside a loop; consider pre-allocating the vector capacity before the loop
+  }
+  {
+    std::vector<int> v1;
+    // CHECK-FIXES: v1.reserve(10);
+    for (int i = 0; i < 10; i++)
+      v1.push_back(i);
+      // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called
+  }
+  {
+    std::vector<int> v2;
+    // CHECK-FIXES: v2.reserve(10);
+    for (int i = 0; i < 10; ++i)
+      v2.push_back(0);
+      // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called
+  }
+  {
+    std::vector<int> v3;
+    // CHECK-FIXES: v3.reserve(5);
+    for (int i = 0; i < 5; ++i) {
+      v3.push_back(i);
+      // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called
+    }
+    // CHECK-FIXES-NOT: v3.reserve(10);
+    for (int i = 0; i < 10; ++i) {
+      // No fix for this loop as we encounter the prior loops.
+      v3.push_back(i);
+    }
+  }
+  {
+    std::vector<int> v4;
+    std::vector<int> v5;
+    v5.reserve(3);
+    // CHECK-FIXES: v4.reserve(10);
+    for (int i = 0; i < 10; ++i)
+      v4.push_back(i);
+      // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called
+  }
+  {
+    std::vector<int> v6;
+    // CHECK-FIXES: v6.reserve(t.size());
+    for (std::size_t i = 0; i < t.size(); ++i) {
+      v6.push_back(t[i]);
+      // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called
+    }
+  }
+  {
+    std::vector<int> v7;
+    // CHECK-FIXES: v7.reserve(t.size() - 1);
+    for (std::size_t i = 0; i < t.size() - 1; ++i) {
+      v7.push_back(t[i]);
+    } // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called
+  }
+  {
+    std::vector<int> v8;
+    // CHECK-FIXES: v8.reserve(t.size());
+    for (const auto &e : t) {
+      v8.push_back(e);
+      // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called
+    }
+  }
+  {
+    std::vector<int> v9;
+    // CHECK-FIXES: v9.reserve(t.size());
+    for (const auto &e : t) {
+      v9.push_back(Op(e));
+      // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called
+    }
+  }
+  {
+    std::vector<Foo> v10;
+    // CHECK-FIXES: v10.reserve(t.size());
+    for (const auto &e : t) {
+      v10.push_back(Foo(e));
+      // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called
+    }
+  }
+  {
+    std::vector<Bar> v11;
+    // CHECK-FIXES: v11.reserve(t.size());
+    for (const auto &e : t) {
+      v11.push_back(e);
+      // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'push_back' is called
+    }
+  }
+  {
+    std::vector<Foo> v12;
+    // CHECK-FIXES: v12.reserve(t.size());
+    for (const auto &e : t) {
+      v12.emplace_back(e);
+      // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: 'emplace_back' is called
+    }
+  }
+
+  // ---- Non-fixed Cases ----
+  {
+    std::vector<int> z0;
+    z0.reserve(20);
+    // CHECK-FIXES-NOT: z0.reserve(10);
+    // There is a "reserve" call already.
+    for (int i = 0; i < 10; ++i) {
+      z0.push_back(i);
+    }
+  }
+  {
+    std::vector<int> z1;
+    z1.reserve(5);
+    // CHECK-FIXES-NOT: z1.reserve(10);
+    // There is a "reserve" call already.
+    for (int i = 0; i < 10; ++i) {
+      z1.push_back(i);
+    }
+  }
+  {
+    std::vector<int> z2;
+    z2.resize(5);
+    // CHECK-FIXES-NOT: z2.reserve(10);
+    // There is a ref usage of v before the loop.
+    for (int i = 0; i < 10; ++i) {
+      z2.push_back(i);
+    }
+  }
+  {
+    std::vector<int> z3;
+    z3.push_back(0);
+    // CHECK-FIXES-NOT: z3.reserve(10);
+    // There is a ref usage of v before the loop.
+    for (int i = 0; i < 10; ++i) {
+      z3.push_back(i);
+    }
+  }
+  {
+    std::vector<int> z4;
+    f(z4);
+    // CHECK-FIXES-NOT: z4.reserve(10);
+    // There is a ref usage of z4 before the loop.
+    for (int i = 0; i < 10; ++i) {
+      z4.push_back(i);
+    }
+  }
+  {
+    std::vector<int> z5(20);
+    // CHECK-FIXES-NOT: z5.reserve(10);
+    // z5 is not constructed with default constructor.
+    for (int i = 0; i < 10; ++i) {
+      z5.push_back(i);
+    }
+  }
+  {
+    std::vector<int> z6;
+    // CHECK-FIXES-NOT: z6.reserve(10);
+    // For-loop is not started with 0.
+    for (int i = 1; i < 10; ++i) {
+      z6.push_back(i);
+    }
+  }
+  {
+    std::vector<int> z7;
+    // CHECK-FIXES-NOT: z7.reserve(t.size());
+    // z7 isn't referenced in for-loop body.
+    for (std::size_t i = 0; i < t.size(); ++i) {
+      t.push_back(i);
+    }
+  }
+  {
+    std::vector<int> z8;
+    int k;
+    // CHECK-FIXES-NOT: z8.reserve(10);
+    // For-loop isn't a fixable loop.
+    for (std::size_t i = 0; k < 10; ++i) {
+      z8.push_back(t[i]);
+    }
+  }
+  {
+    std::vector<int> z9;
+    // CHECK-FIXES-NOT: z9.reserve(i + 1);
+    // The loop end expression refers to the loop variable i.
+    for (int i = 0; i < i + 1; i++)
+      z9.push_back(i);
+  }
+  {
+    std::vector<int> z10;
+    int k;
+    // CHECK-FIXES-NOT: z10.reserve(10);
+    // For-loop isn't a fixable loop.
+    for (std::size_t i = 0; i < 10; ++k) {
+      z10.push_back(t[i]);
+    }
+  }
+  {
+    std::vector<int> z11;
+    // initializer_list should not trigger the check.
+    for (int e : {1, 2, 3, 4, 5}) {
+      z11.push_back(e);
+    }
+  }
+  {
+    std::vector<int> z12;
+    std::vector<int>* z13 = &t;
+    // We only support detecting the range init expression which references
+    // container directly.
+    // Complex range init expressions like `*z13` is not supported.
+    for (const auto &e : *z13) {
+      z12.push_back(e);
+    }
+  }
+}
diff --git a/test/clang-tidy/performance-type-promotion-in-math-fn.cpp b/test/clang-tidy/performance-type-promotion-in-math-fn.cpp
new file mode 100644 (file)
index 0000000..b2da7cc
--- /dev/null
@@ -0,0 +1,316 @@
+// RUN: %check_clang_tidy %s performance-type-promotion-in-math-fn %t
+
+// CHECK-FIXES: #include <cmath>
+
+double acos(double);
+double acosh(double);
+double asin(double);
+double asinh(double);
+double atan2(double, double);
+double atan(double);
+double atanh(double);
+double cbrt(double);
+double ceil(double);
+double copysign(double, double);
+double cos(double);
+double cosh(double);
+double erfc(double);
+double erf(double);
+double exp2(double);
+double exp(double);
+double expm1(double);
+double fabs(double);
+double fdim(double, double);
+double floor(double);
+double fma(double, double, double);
+double fmax(double, double);
+double fmin(double, double);
+double fmod(double, double);
+double frexp(double, int *);
+double hypot(double, double);
+double ilogb(double);
+double ldexp(double, double);
+double lgamma(double);
+long long llrint(double);
+double log10(double);
+double log1p(double);
+double log2(double);
+double logb(double);
+double log(double);
+long lrint(double);
+double modf(double);
+double nearbyint(double);
+double nextafter(double, double);
+double nexttoward(double, long double);
+double pow(double, double);
+double remainder(double, double);
+double remquo(double, double, int *);
+double rint(double);
+double round(double);
+double scalbln(double, long);
+double scalbn(double, int);
+double sin(double);
+double sinh(double);
+double sqrt(double);
+double tan(double);
+double tanh(double);
+double tgamma(double);
+double trunc(double);
+long long llround(double);
+long lround(double);
+
+void check_all_fns() {
+  float a, b, c;
+  int i;
+  long l;
+  int *int_ptr;
+
+  acos(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'acos' promotes float to double [performance-type-promotion-in-math-fn]
+  // CHECK-FIXES: {{^}}  std::acos(a);{{$}}
+  acosh(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'acosh'
+  // CHECK-FIXES: {{^}}  std::acosh(a);{{$}}
+  asin(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'asin'
+  // CHECK-FIXES: {{^}}  std::asin(a);{{$}}
+  asinh(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'asinh'
+  // CHECK-FIXES: {{^}}  std::asinh(a);{{$}}
+  atan2(a, b);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'atan2'
+  // CHECK-FIXES: {{^}}  std::atan2(a, b);{{$}}
+  atan(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'atan'
+  // CHECK-FIXES: {{^}}  std::atan(a);{{$}}
+  atanh(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'atanh'
+  // CHECK-FIXES: {{^}}  std::atanh(a);{{$}}
+  cbrt(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'cbrt'
+  // CHECK-FIXES: {{^}}  std::cbrt(a);{{$}}
+  ceil(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'ceil'
+  // CHECK-FIXES: {{^}}  std::ceil(a);{{$}}
+  copysign(a, b);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'copysign'
+  // CHECK-FIXES: {{^}}  std::copysign(a, b);{{$}}
+  cos(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'cos'
+  // CHECK-FIXES: {{^}}  std::cos(a);{{$}}
+  cosh(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'cosh'
+  // CHECK-FIXES: {{^}}  std::cosh(a);{{$}}
+  erf(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'erf'
+  // CHECK-FIXES: {{^}}  std::erf(a);{{$}}
+  erfc(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'erfc'
+  // CHECK-FIXES: {{^}}  std::erfc(a);{{$}}
+  exp2(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'exp2'
+  // CHECK-FIXES: {{^}}  std::exp2(a);{{$}}
+  exp(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'exp'
+  // CHECK-FIXES: {{^}}  std::exp(a);{{$}}
+  expm1(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'expm1'
+  // CHECK-FIXES: {{^}}  std::expm1(a);{{$}}
+  fabs(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'fabs'
+  // CHECK-FIXES: {{^}}  std::fabs(a);{{$}}
+  fdim(a, b);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'fdim'
+  // CHECK-FIXES: {{^}}  std::fdim(a, b);{{$}}
+  floor(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'floor'
+  // CHECK-FIXES: {{^}}  std::floor(a);{{$}}
+  fma(a, b, c);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'fma'
+  // CHECK-FIXES: {{^}}  std::fma(a, b, c);{{$}}
+  fmax(a, b);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'fmax'
+  // CHECK-FIXES: {{^}}  std::fmax(a, b);{{$}}
+  fmin(a, b);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'fmin'
+  // CHECK-FIXES: {{^}}  std::fmin(a, b);{{$}}
+  fmod(a, b);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'fmod'
+  // CHECK-FIXES: {{^}}  std::fmod(a, b);{{$}}
+  frexp(a, int_ptr);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'frexp'
+  // CHECK-FIXES: {{^}}  std::frexp(a, int_ptr);{{$}}
+  hypot(a, b);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'hypot'
+  // CHECK-FIXES: {{^}}  std::hypot(a, b);{{$}}
+  ilogb(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'ilogb'
+  // CHECK-FIXES: {{^}}  std::ilogb(a);{{$}}
+  ldexp(a, b);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'ldexp'
+  // CHECK-FIXES: {{^}}  std::ldexp(a, b);{{$}}
+  lgamma(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'lgamma'
+  // CHECK-FIXES: {{^}}  std::lgamma(a);{{$}}
+  llrint(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'llrint'
+  // CHECK-FIXES: {{^}}  std::llrint(a);{{$}}
+  llround(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'llround'
+  // CHECK-FIXES: {{^}}  std::llround(a);{{$}}
+  log10(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'log10'
+  // CHECK-FIXES: {{^}}  std::log10(a);{{$}}
+  log1p(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'log1p'
+  // CHECK-FIXES: {{^}}  std::log1p(a);{{$}}
+  log2(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'log2'
+  // CHECK-FIXES: {{^}}  std::log2(a);{{$}}
+  log(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'log'
+  // CHECK-FIXES: {{^}}  std::log(a);{{$}}
+  logb(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'logb'
+  // CHECK-FIXES: {{^}}  std::logb(a);{{$}}
+  lrint(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'lrint'
+  // CHECK-FIXES: {{^}}  std::lrint(a);{{$}}
+  lround(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'lround'
+  // CHECK-FIXES: {{^}}  std::lround(a);{{$}}
+  nearbyint(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'nearbyint'
+  // CHECK-FIXES: {{^}}  std::nearbyint(a);{{$}}
+  nextafter(a, b);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'nextafter'
+  // CHECK-FIXES: {{^}}  std::nextafter(a, b);{{$}}
+  nexttoward(a, b);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'nexttoward'
+  // CHECK-FIXES: {{^}}  std::nexttoward(a, b);{{$}}
+  pow(a, b);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'pow'
+  // CHECK-FIXES: {{^}}  std::pow(a, b);{{$}}
+  remainder(a, b);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'remainder'
+  // CHECK-FIXES: {{^}}  std::remainder(a, b);{{$}}
+  remquo(a, b, int_ptr);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'remquo'
+  // CHECK-FIXES: {{^}}  std::remquo(a, b, int_ptr);{{$}}
+  rint(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'rint'
+  // CHECK-FIXES: {{^}}  std::rint(a);{{$}}
+  round(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'round'
+  // CHECK-FIXES: {{^}}  std::round(a);{{$}}
+  scalbln(a, l);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'scalbln'
+  // CHECK-FIXES: {{^}}  std::scalbln(a, l);{{$}}
+  scalbn(a, i);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'scalbn'
+  // CHECK-FIXES: {{^}}  std::scalbn(a, i);{{$}}
+  sin(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'sin'
+  // CHECK-FIXES: {{^}}  std::sin(a);{{$}}
+  sinh(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'sinh'
+  // CHECK-FIXES: {{^}}  std::sinh(a);{{$}}
+  sqrt(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'sqrt'
+  // CHECK-FIXES: {{^}}  std::sqrt(a);{{$}}
+  tan(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'tan'
+  // CHECK-FIXES: {{^}}  std::tan(a);{{$}}
+  tanh(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'tanh'
+  // CHECK-FIXES: {{^}}  std::tanh(a);{{$}}
+  tgamma(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'tgamma'
+  // CHECK-FIXES: {{^}}  std::tgamma(a);{{$}}
+  trunc(a);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'trunc'
+  // CHECK-FIXES: {{^}}  std::trunc(a);{{$}}
+}
+
+// nexttoward/nexttowardf are weird -- the second param is always long double.
+// So we warn if the first arg is a float, regardless of what the second arg is.
+void check_nexttoward() {
+  nexttoward(0.f, 0);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'nexttoward'
+  // CHECK-FIXES: {{^}}  std::nexttoward(0.f, 0);{{$}}
+  nexttoward(0.f, 0l);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'nexttoward'
+  // CHECK-FIXES: {{^}}  std::nexttoward(0.f, 0l);{{$}}
+  nexttoward(0.f, 0.f);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'nexttoward'
+  // CHECK-FIXES: {{^}}  std::nexttoward(0.f, 0.f);{{$}}
+  nexttoward(0.f, 0.);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'nexttoward'
+  // CHECK-FIXES: {{^}}  std::nexttoward(0.f, 0.);{{$}}
+
+  // No warnings for these.
+  nexttoward(0., 0);
+  nexttoward(0., 0.f);
+  nexttoward(0., 0.);
+}
+
+// The second parameter to scalbn and scalbnf is an int, so we don't care what
+// type you pass as that argument; we warn iff the first argument is a float.
+void check_scalbn() {
+  scalbn(0.f, 0);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'scalbn'
+  // CHECK-FIXES: {{^}}  std::scalbn(0.f, 0);{{$}}
+  scalbn(0.f, static_cast<char>(0));
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'scalbn'
+  // CHECK-FIXES: {{^}}  std::scalbn(0.f, static_cast<char>(0));{{$}}
+
+  // No warnings for these.
+  scalbn(0., 0);
+  scalbn(0., static_cast<char>(0));
+}
+
+// scalbln/scalblnf are like scalbn/scalbnf except their second arg is a long.
+// Again, doesn't matter what we pass for the second arg; we warn iff the first
+// arg is a float.
+void check_scalbln() {
+  scalbln(0.f, 0);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'scalbln'
+  // CHECK-FIXES: {{^}}  std::scalbln(0.f, 0);{{$}}
+  scalbln(0.f, 0l);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: call to 'scalbln'
+  // CHECK-FIXES: {{^}}  std::scalbln(0.f, 0l);{{$}}
+
+  // No warnings for these.
+  scalbln(0., 0);
+  scalbln(0., 0l);
+}
+
+float cosf(float);
+double foo(double);         // not a math.h function
+float cos(float);           // not a math.h function (wrong signature)
+double cos(double, double); // not a math.h function (wrong signature)
+
+namespace std {
+void cos(float);
+} // namespace std
+
+void check_no_warnings() {
+  foo(0.); // no warning because not a math.h function.
+
+  sin(0);        // no warning because arg is an int
+  cos(0.);       // no warning because arg is a double
+  std::cos(0.f); // no warning because not ::cos.
+  cosf(0.f);     // no warning; we expect this to take a float
+  cos(0.f);      // does not match the expected signature of ::cos
+  cos(0.f, 0.f); // does not match the expected signature of ::cos
+
+  // No warnings because all args are not floats.
+  remainder(0., 0.f);
+  remainder(0.f, 0.);
+  remainder(0, 0.f);
+  remainder(0.f, 0);
+  fma(0.f, 0.f, 0);
+  fma(0.f, 0.f, 0.);
+  fma(0.f, 0., 0.f);
+  fma(0., 0.f, 0.f);
+}
diff --git a/test/clang-tidy/performance-unnecessary-copy-initialization.cpp b/test/clang-tidy/performance-unnecessary-copy-initialization.cpp
new file mode 100644 (file)
index 0000000..50dcfd8
--- /dev/null
@@ -0,0 +1,389 @@
+// RUN: %check_clang_tidy %s performance-unnecessary-copy-initialization %t
+
+struct ExpensiveToCopyType {
+  ExpensiveToCopyType();
+  virtual ~ExpensiveToCopyType();
+  const ExpensiveToCopyType &reference() const;
+  void nonConstMethod();
+  bool constMethod() const;
+};
+
+struct TrivialToCopyType {
+  const TrivialToCopyType &reference() const;
+};
+
+struct WeirdCopyCtorType {
+  WeirdCopyCtorType();
+  WeirdCopyCtorType(const WeirdCopyCtorType &w, bool oh_yes = true);
+
+  void nonConstMethod();
+  bool constMethod() const;
+};
+
+ExpensiveToCopyType global_expensive_to_copy_type;
+
+const ExpensiveToCopyType &ExpensiveTypeReference();
+const TrivialToCopyType &TrivialTypeReference();
+
+void mutate(ExpensiveToCopyType &);
+void mutate(ExpensiveToCopyType *);
+void useAsConstPointer(const ExpensiveToCopyType *);
+void useAsConstReference(const ExpensiveToCopyType &);
+void useByValue(ExpensiveToCopyType);
+
+void PositiveFunctionCall() {
+  const auto AutoAssigned = ExpensiveTypeReference();
+  // CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 'AutoAssigned' is copy-constructed from a const reference; consider making it a const reference [performance-unnecessary-copy-initialization]
+  // CHECK-FIXES: const auto& AutoAssigned = ExpensiveTypeReference();
+  const auto AutoCopyConstructed(ExpensiveTypeReference());
+  // CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 'AutoCopyConstructed'
+  // CHECK-FIXES: const auto& AutoCopyConstructed(ExpensiveTypeReference());
+  const ExpensiveToCopyType VarAssigned = ExpensiveTypeReference();
+  // CHECK-MESSAGES: [[@LINE-1]]:29: warning: the const qualified variable 'VarAssigned'
+  // CHECK-FIXES:   const ExpensiveToCopyType& VarAssigned = ExpensiveTypeReference();
+  const ExpensiveToCopyType VarCopyConstructed(ExpensiveTypeReference());
+  // CHECK-MESSAGES: [[@LINE-1]]:29: warning: the const qualified variable 'VarCopyConstructed'
+  // CHECK-FIXES: const ExpensiveToCopyType& VarCopyConstructed(ExpensiveTypeReference());
+}
+
+void PositiveMethodCallConstReferenceParam(const ExpensiveToCopyType &Obj) {
+  const auto AutoAssigned = Obj.reference();
+  // CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 'AutoAssigned'
+  // CHECK-FIXES: const auto& AutoAssigned = Obj.reference();
+  const auto AutoCopyConstructed(Obj.reference());
+  // CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 'AutoCopyConstructed'
+  // CHECK-FIXES: const auto& AutoCopyConstructed(Obj.reference());
+  const ExpensiveToCopyType VarAssigned = Obj.reference();
+  // CHECK-MESSAGES: [[@LINE-1]]:29: warning: the const qualified variable 'VarAssigned'
+  // CHECK-FIXES: const ExpensiveToCopyType& VarAssigned = Obj.reference();
+  const ExpensiveToCopyType VarCopyConstructed(Obj.reference());
+  // CHECK-MESSAGES: [[@LINE-1]]:29: warning: the const qualified variable 'VarCopyConstructed'
+  // CHECK-FIXES: const ExpensiveToCopyType& VarCopyConstructed(Obj.reference());
+}
+
+void PositiveMethodCallConstParam(const ExpensiveToCopyType Obj) {
+  const auto AutoAssigned = Obj.reference();
+  // CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 'AutoAssigned'
+  // CHECK-FIXES: const auto& AutoAssigned = Obj.reference();
+  const auto AutoCopyConstructed(Obj.reference());
+  // CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 'AutoCopyConstructed'
+  // CHECK-FIXES: const auto& AutoCopyConstructed(Obj.reference());
+  const ExpensiveToCopyType VarAssigned = Obj.reference();
+  // CHECK-MESSAGES: [[@LINE-1]]:29: warning: the const qualified variable 'VarAssigned'
+  // CHECK-FIXES: const ExpensiveToCopyType& VarAssigned = Obj.reference();
+  const ExpensiveToCopyType VarCopyConstructed(Obj.reference());
+  // CHECK-MESSAGES: [[@LINE-1]]:29: warning: the const qualified variable 'VarCopyConstructed'
+  // CHECK-FIXES: const ExpensiveToCopyType& VarCopyConstructed(Obj.reference());
+}
+
+void PositiveMethodCallConstPointerParam(const ExpensiveToCopyType *const Obj) {
+  const auto AutoAssigned = Obj->reference();
+  // CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 'AutoAssigned'
+  // CHECK-FIXES: const auto& AutoAssigned = Obj->reference();
+  const auto AutoCopyConstructed(Obj->reference());
+  // CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 'AutoCopyConstructed'
+  // CHECK-FIXES: const auto& AutoCopyConstructed(Obj->reference());
+  const ExpensiveToCopyType VarAssigned = Obj->reference();
+  // CHECK-MESSAGES: [[@LINE-1]]:29: warning: the const qualified variable 'VarAssigned'
+  // CHECK-FIXES: const ExpensiveToCopyType& VarAssigned = Obj->reference();
+  const ExpensiveToCopyType VarCopyConstructed(Obj->reference());
+  // CHECK-MESSAGES: [[@LINE-1]]:29: warning: the const qualified variable 'VarCopyConstructed'
+  // CHECK-FIXES: const ExpensiveToCopyType& VarCopyConstructed(Obj->reference());
+}
+
+void PositiveLocalConstValue() {
+  const ExpensiveToCopyType Obj;
+  const auto UnnecessaryCopy = Obj.reference();
+  // CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 'UnnecessaryCopy'
+  // CHECK-FIXES: const auto& UnnecessaryCopy = Obj.reference();
+}
+
+void PositiveLocalConstRef() {
+  const ExpensiveToCopyType Obj;
+  const ExpensiveToCopyType &ConstReference = Obj.reference();
+  const auto UnnecessaryCopy = ConstReference.reference();
+  // CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 'UnnecessaryCopy'
+  // CHECK-FIXES: const auto& UnnecessaryCopy = ConstReference.reference();
+}
+
+void PositiveLocalConstPointer() {
+  const ExpensiveToCopyType Obj;
+  const ExpensiveToCopyType *const ConstPointer = &Obj;
+  const auto UnnecessaryCopy = ConstPointer->reference();
+  // CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 'UnnecessaryCopy'
+  // CHECK-FIXES: const auto& UnnecessaryCopy = ConstPointer->reference();
+}
+
+void NegativeFunctionCallTrivialType() {
+  const auto AutoAssigned = TrivialTypeReference();
+  const auto AutoCopyConstructed(TrivialTypeReference());
+  const TrivialToCopyType VarAssigned = TrivialTypeReference();
+  const TrivialToCopyType VarCopyConstructed(TrivialTypeReference());
+}
+
+void NegativeStaticLocalVar(const ExpensiveToCopyType &Obj) {
+  static const auto StaticVar = Obj.reference();
+}
+
+void PositiveFunctionCallExpensiveTypeNonConstVariable() {
+  auto AutoAssigned = ExpensiveTypeReference();
+  // CHECK-MESSAGES: [[@LINE-1]]:8: warning: the variable 'AutoAssigned' is copy-constructed from a const reference but is only used as const reference; consider making it a const reference [performance-unnecessary-copy-initialization]
+  // CHECK-FIXES: const auto& AutoAssigned = ExpensiveTypeReference();
+  auto AutoCopyConstructed(ExpensiveTypeReference());
+  // CHECK-MESSAGES: [[@LINE-1]]:8: warning: the variable 'AutoCopyConstructed'
+  // CHECK-FIXES: const auto& AutoCopyConstructed(ExpensiveTypeReference());
+  ExpensiveToCopyType VarAssigned = ExpensiveTypeReference();
+  // CHECK-MESSAGES: [[@LINE-1]]:23: warning: the variable 'VarAssigned'
+  // CHECK-FIXES: const ExpensiveToCopyType& VarAssigned = ExpensiveTypeReference();
+  ExpensiveToCopyType VarCopyConstructed(ExpensiveTypeReference());
+  // CHECK-MESSAGES: [[@LINE-1]]:23: warning: the variable 'VarCopyConstructed'
+  // CHECK-FIXES: const ExpensiveToCopyType& VarCopyConstructed(ExpensiveTypeReference());
+}
+
+void positiveNonConstVarInCodeBlock(const ExpensiveToCopyType &Obj) {
+  {
+    auto Assigned = Obj.reference();
+    // CHECK-MESSAGES: [[@LINE-1]]:10: warning: the variable 'Assigned'
+    // CHECK-FIXES: const auto& Assigned = Obj.reference();
+    Assigned.reference();
+    useAsConstReference(Assigned);
+    useByValue(Assigned);
+  }
+}
+
+void negativeNonConstVarWithNonConstUse(const ExpensiveToCopyType &Obj) {
+  {
+    auto NonConstInvoked = Obj.reference();
+    // CHECK-FIXES: auto NonConstInvoked = Obj.reference();
+    NonConstInvoked.nonConstMethod();
+  }
+  {
+    auto Reassigned = Obj.reference();
+    // CHECK-FIXES: auto Reassigned = Obj.reference();
+    Reassigned = ExpensiveToCopyType();
+  }
+  {
+    auto MutatedByReference = Obj.reference();
+    // CHECK-FIXES: auto MutatedByReference = Obj.reference();
+    mutate(MutatedByReference);
+  }
+  {
+    auto MutatedByPointer = Obj.reference();
+    // CHECK-FIXES: auto MutatedByPointer = Obj.reference();
+    mutate(&MutatedByPointer);
+  }
+}
+
+void PositiveMethodCallNonConstRefNotModified(ExpensiveToCopyType &Obj) {
+  const auto AutoAssigned = Obj.reference();
+  // CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 'AutoAssigned'
+  // CHECK-FIXES: const auto& AutoAssigned = Obj.reference();
+}
+
+void NegativeMethodCallNonConstRefIsModified(ExpensiveToCopyType &Obj) {
+  const auto AutoAssigned = Obj.reference();
+  const auto AutoCopyConstructed(Obj.reference());
+  const ExpensiveToCopyType VarAssigned = Obj.reference();
+  const ExpensiveToCopyType VarCopyConstructed(Obj.reference());
+  mutate(&Obj);
+}
+
+void PositiveMethodCallNonConstNotModified(ExpensiveToCopyType Obj) {
+  const auto AutoAssigned = Obj.reference();
+  // CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 'AutoAssigned'
+  // CHECK-FIXES: const auto& AutoAssigned = Obj.reference();
+}
+
+void NegativeMethodCallNonConstValueArgumentIsModified(ExpensiveToCopyType Obj) {
+  Obj.nonConstMethod();
+  const auto AutoAssigned = Obj.reference();
+}
+
+void PositiveMethodCallNonConstPointerNotModified(ExpensiveToCopyType *const Obj) {
+  const auto AutoAssigned = Obj->reference();
+  // CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 'AutoAssigned'
+  // CHECK-FIXES: const auto& AutoAssigned = Obj->reference();
+  Obj->constMethod();
+}
+
+void NegativeMethodCallNonConstPointerIsModified(ExpensiveToCopyType *const Obj) {
+  const auto AutoAssigned = Obj->reference();
+  const auto AutoCopyConstructed(Obj->reference());
+  const ExpensiveToCopyType VarAssigned = Obj->reference();
+  const ExpensiveToCopyType VarCopyConstructed(Obj->reference());
+  mutate(Obj);
+}
+
+void PositiveLocalVarIsNotModified() {
+  ExpensiveToCopyType LocalVar;
+  const auto AutoAssigned = LocalVar.reference();
+  // CHECK-MESSAGES: [[@LINE-1]]:14: warning: the const qualified variable 'AutoAssigned'
+  // CHECK-FIXES: const auto& AutoAssigned = LocalVar.reference();
+}
+
+void NegativeLocalVarIsModified() {
+  ExpensiveToCopyType Obj;
+  const auto AutoAssigned = Obj.reference();
+  Obj = AutoAssigned;
+}
+
+struct NegativeConstructor {
+  NegativeConstructor(const ExpensiveToCopyType &Obj) : Obj(Obj) {}
+  ExpensiveToCopyType Obj;
+};
+
+#define UNNECESSARY_COPY_INIT_IN_MACRO_BODY(TYPE)                              \
+  void functionWith##TYPE(const TYPE &T) {                                     \
+    auto AssignedInMacro = T.reference();                                      \
+  }                                                                            \
+// Ensure fix is not applied.
+// CHECK-FIXES: auto AssignedInMacro = T.reference();
+
+UNNECESSARY_COPY_INIT_IN_MACRO_BODY(ExpensiveToCopyType)
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: the variable 'AssignedInMacro' is copy-constructed
+
+#define UNNECESSARY_COPY_INIT_IN_MACRO_ARGUMENT(ARGUMENT) ARGUMENT
+
+void PositiveMacroArgument(const ExpensiveToCopyType &Obj) {
+  UNNECESSARY_COPY_INIT_IN_MACRO_ARGUMENT(auto CopyInMacroArg = Obj.reference());
+  // CHECK-MESSAGES: [[@LINE-1]]:48: warning: the variable 'CopyInMacroArg' is copy-constructed
+  // Ensure fix is not applied.
+  // CHECK-FIXES: auto CopyInMacroArg = Obj.reference()
+}
+
+void PositiveLocalCopyConstMethodInvoked() {
+  ExpensiveToCopyType orig;
+  ExpensiveToCopyType copy_1 = orig;
+  // CHECK-MESSAGES: [[@LINE-1]]:23: warning: local copy 'copy_1' of the variable 'orig' is never modified; consider avoiding the copy [performance-unnecessary-copy-initialization]
+  // CHECK-FIXES: const ExpensiveToCopyType& copy_1 = orig;
+  copy_1.constMethod();
+  orig.constMethod();
+}
+
+void PositiveLocalCopyUsingExplicitCopyCtor() {
+  ExpensiveToCopyType orig;
+  ExpensiveToCopyType copy_2(orig);
+  // CHECK-MESSAGES: [[@LINE-1]]:23: warning: local copy 'copy_2'
+  // CHECK-FIXES: const ExpensiveToCopyType& copy_2(orig);
+  copy_2.constMethod();
+  orig.constMethod();
+}
+
+void PositiveLocalCopyCopyIsArgument(const ExpensiveToCopyType &orig) {
+  ExpensiveToCopyType copy_3 = orig;
+  // CHECK-MESSAGES: [[@LINE-1]]:23: warning: local copy 'copy_3'
+  // CHECK-FIXES: const ExpensiveToCopyType& copy_3 = orig;
+  copy_3.constMethod();
+}
+
+void PositiveLocalCopyUsedAsConstRef() {
+  ExpensiveToCopyType orig;
+  ExpensiveToCopyType copy_4 = orig;
+  // CHECK-MESSAGES: [[@LINE-1]]:23: warning: local copy 'copy_4'
+  // CHECK-FIXES: const ExpensiveToCopyType& copy_4 = orig;
+  useAsConstReference(orig);
+}
+
+void PositiveLocalCopyTwice() {
+  ExpensiveToCopyType orig;
+  ExpensiveToCopyType copy_5 = orig;
+  // CHECK-MESSAGES: [[@LINE-1]]:23: warning: local copy 'copy_5'
+  // CHECK-FIXES: const ExpensiveToCopyType& copy_5 = orig;
+  ExpensiveToCopyType copy_6 = copy_5;
+  // CHECK-MESSAGES: [[@LINE-1]]:23: warning: local copy 'copy_6'
+  // CHECK-FIXES: const ExpensiveToCopyType& copy_6 = copy_5;
+  copy_5.constMethod();
+  copy_6.constMethod();
+  orig.constMethod();
+}
+
+
+void PositiveLocalCopyWeirdCopy() {
+  WeirdCopyCtorType orig;
+  WeirdCopyCtorType weird_1(orig);
+  // CHECK-MESSAGES: [[@LINE-1]]:21: warning: local copy 'weird_1'
+  // CHECK-FIXES: const WeirdCopyCtorType& weird_1(orig);
+  weird_1.constMethod();
+
+  WeirdCopyCtorType weird_2 = orig;
+  // CHECK-MESSAGES: [[@LINE-1]]:21: warning: local copy 'weird_2'
+  // CHECK-FIXES: const WeirdCopyCtorType& weird_2 = orig;
+  weird_2.constMethod();
+}
+
+void NegativeLocalCopySimpleTypes() {
+  int i1 = 0;
+  int i2 = i1;
+}
+
+void NegativeLocalCopyCopyIsModified() {
+  ExpensiveToCopyType orig;
+  ExpensiveToCopyType neg_copy_1 = orig;
+  neg_copy_1.nonConstMethod();
+}
+
+void NegativeLocalCopyOriginalIsModified() {
+  ExpensiveToCopyType orig;
+  ExpensiveToCopyType neg_copy_2 = orig;
+  orig.nonConstMethod();
+}
+
+void NegativeLocalCopyUsedAsRefArg() {
+  ExpensiveToCopyType orig;
+  ExpensiveToCopyType neg_copy_3 = orig;
+  mutate(neg_copy_3);
+}
+
+void NegativeLocalCopyUsedAsPointerArg() {
+  ExpensiveToCopyType orig;
+  ExpensiveToCopyType neg_copy_4 = orig;
+  mutate(&neg_copy_4);
+}
+
+void NegativeLocalCopyCopyFromGlobal() {
+  ExpensiveToCopyType neg_copy_5 = global_expensive_to_copy_type;
+}
+
+void NegativeLocalCopyCopyToStatic() {
+  ExpensiveToCopyType orig;
+  static ExpensiveToCopyType neg_copy_6 = orig;
+}
+
+void NegativeLocalCopyNonConstInForLoop() {
+  ExpensiveToCopyType orig;
+  for (ExpensiveToCopyType neg_copy_7 = orig; orig.constMethod();
+       orig.nonConstMethod()) {
+    orig.constMethod();
+  }
+}
+
+void NegativeLocalCopyWeirdNonCopy() {
+  WeirdCopyCtorType orig;
+  WeirdCopyCtorType neg_weird_1(orig, false);
+  WeirdCopyCtorType neg_weird_2(orig, true);
+}
+void WarningOnlyMultiDeclStmt() {
+  ExpensiveToCopyType orig;
+  ExpensiveToCopyType copy = orig, copy2;
+  // CHECK-MESSAGES: [[@LINE-1]]:23: warning: local copy 'copy' of the variable 'orig' is never modified; consider avoiding the copy [performance-unnecessary-copy-initialization]
+  // CHECK-FIXES: ExpensiveToCopyType copy = orig, copy2;
+}
+
+class Element {};
+class Container {
+public:
+  class Iterator {
+  public:
+    void operator++();
+    Element operator*();
+    bool operator!=(const Iterator &);
+    WeirdCopyCtorType c;
+  };
+  const Iterator &begin() const;
+  const Iterator &end() const;
+};
+
+void implicitVarFalsePositive() {
+  for (const Element &E : Container()) {
+  }
+}
diff --git a/test/clang-tidy/performance-unnecessary-value-param-delayed.cpp b/test/clang-tidy/performance-unnecessary-value-param-delayed.cpp
new file mode 100644 (file)
index 0000000..623bf8b
--- /dev/null
@@ -0,0 +1,181 @@
+// RUN: %check_clang_tidy %s performance-unnecessary-value-param %t -- -- -fdelayed-template-parsing
+
+struct ExpensiveToCopyType {
+  const ExpensiveToCopyType & constReference() const {
+    return *this;
+  }
+  void nonConstMethod();
+  virtual ~ExpensiveToCopyType();
+};
+
+void mutate(ExpensiveToCopyType &);
+void mutate(ExpensiveToCopyType *);
+void useAsConstReference(const ExpensiveToCopyType &);
+void useByValue(ExpensiveToCopyType);
+
+// This class simulates std::pair<>. It is trivially copy constructible
+// and trivially destructible, but not trivially copy assignable.
+class SomewhatTrivial {
+ public:
+  SomewhatTrivial();
+  SomewhatTrivial(const SomewhatTrivial&) = default;
+  ~SomewhatTrivial() = default;
+  SomewhatTrivial& operator=(const SomewhatTrivial&);
+};
+
+void positiveExpensiveConstValue(const ExpensiveToCopyType Obj);
+// CHECK-FIXES: void positiveExpensiveConstValue(const ExpensiveToCopyType& Obj);
+void positiveExpensiveConstValue(const ExpensiveToCopyType Obj) {
+  // CHECK-MESSAGES: [[@LINE-1]]:60: warning: the const qualified parameter 'Obj' is copied for each invocation; consider making it a reference [performance-unnecessary-value-param]
+  // CHECK-FIXES: void positiveExpensiveConstValue(const ExpensiveToCopyType& Obj) {
+}
+
+void positiveExpensiveValue(ExpensiveToCopyType Obj);
+// CHECK-FIXES: void positiveExpensiveValue(const ExpensiveToCopyType& Obj);
+void positiveExpensiveValue(ExpensiveToCopyType Obj) {
+  // CHECK-MESSAGES: [[@LINE-1]]:49: warning: the parameter 'Obj' is copied for each invocation but only used as a const reference; consider making it a const reference [performance-unnecessary-value-param]
+  // CHECK-FIXES: void positiveExpensiveValue(const ExpensiveToCopyType& Obj) {
+  Obj.constReference();
+  useAsConstReference(Obj);
+  auto Copy = Obj;
+  useByValue(Obj);
+}
+
+void positiveWithComment(const ExpensiveToCopyType /* important */ S);
+// CHECK-FIXES: void positiveWithComment(const ExpensiveToCopyType& /* important */ S);
+void positiveWithComment(const ExpensiveToCopyType /* important */ S) {
+  // CHECK-MESSAGES: [[@LINE-1]]:68: warning: the const qualified
+  // CHECK-FIXES: void positiveWithComment(const ExpensiveToCopyType& /* important */ S) {
+}
+
+void positiveUnnamedParam(const ExpensiveToCopyType) {
+  // CHECK-MESSAGES: [[@LINE-1]]:52: warning: the const qualified parameter #1
+  // CHECK-FIXES: void positiveUnnamedParam(const ExpensiveToCopyType&) {
+}
+
+void positiveAndNegative(const ExpensiveToCopyType ConstCopy, const ExpensiveToCopyType& ConstRef, ExpensiveToCopyType Copy);
+// CHECK-FIXES: void positiveAndNegative(const ExpensiveToCopyType& ConstCopy, const ExpensiveToCopyType& ConstRef, const ExpensiveToCopyType& Copy);
+void positiveAndNegative(const ExpensiveToCopyType ConstCopy, const ExpensiveToCopyType& ConstRef, ExpensiveToCopyType Copy) {
+  // CHECK-MESSAGES: [[@LINE-1]]:52: warning: the const qualified parameter 'ConstCopy'
+  // CHECK-MESSAGES: [[@LINE-2]]:120: warning: the parameter 'Copy'
+  // CHECK-FIXES: void positiveAndNegative(const ExpensiveToCopyType& ConstCopy, const ExpensiveToCopyType& ConstRef, const ExpensiveToCopyType& Copy) {
+}
+
+struct PositiveConstValueConstructor {
+  PositiveConstValueConstructor(const ExpensiveToCopyType ConstCopy) {}
+  // CHECK-MESSAGES: [[@LINE-1]]:59: warning: the const qualified parameter 'ConstCopy'
+  // CHECK-FIXES: PositiveConstValueConstructor(const ExpensiveToCopyType& ConstCopy) {}
+};
+
+template <typename T> void templateWithNonTemplatizedParameter(const ExpensiveToCopyType S, T V) {
+  // CHECK-MESSAGES: [[@LINE-1]]:90: warning: the const qualified parameter 'S'
+  // CHECK-FIXES: template <typename T> void templateWithNonTemplatizedParameter(const ExpensiveToCopyType& S, T V) {
+}
+
+void instantiated() {
+  templateWithNonTemplatizedParameter(ExpensiveToCopyType(), ExpensiveToCopyType());
+  templateWithNonTemplatizedParameter(ExpensiveToCopyType(), 5);
+}
+
+template <typename T> void negativeTemplateType(const T V) {
+}
+
+void negativeArray(const ExpensiveToCopyType[]) {
+}
+
+void negativePointer(ExpensiveToCopyType* Obj) {
+}
+
+void negativeConstPointer(const ExpensiveToCopyType* Obj) {
+}
+
+void negativeConstReference(const ExpensiveToCopyType& Obj) {
+}
+
+void negativeReference(ExpensiveToCopyType& Obj) {
+}
+
+void negativeUniversalReference(ExpensiveToCopyType&& Obj) {
+}
+
+void negativeSomewhatTrivialConstValue(const SomewhatTrivial Somewhat) {
+}
+
+void negativeSomewhatTrivialValue(SomewhatTrivial Somewhat) {
+}
+
+void negativeConstBuiltIn(const int I) {
+}
+
+void negativeValueBuiltIn(int I) {
+}
+
+void negativeValueIsMutatedByReference(ExpensiveToCopyType Obj) {
+  mutate(Obj);
+}
+
+void negativeValueIsMutatatedByPointer(ExpensiveToCopyType Obj) {
+  mutate(&Obj);
+}
+
+void negativeValueIsReassigned(ExpensiveToCopyType Obj) {
+  Obj = ExpensiveToCopyType();
+}
+
+void negativeValueNonConstMethodIsCalled(ExpensiveToCopyType Obj) {
+  Obj.nonConstMethod();
+}
+
+struct PositiveValueUnusedConstructor {
+  PositiveValueUnusedConstructor(ExpensiveToCopyType Copy) {}
+  // CHECK-MESSAGES: [[@LINE-1]]:54: warning: the parameter 'Copy'
+  // CHECK-FIXES: PositiveValueUnusedConstructor(const ExpensiveToCopyType& Copy) {}
+};
+
+struct PositiveValueCopiedConstructor {
+  PositiveValueCopiedConstructor(ExpensiveToCopyType Copy) : Field(Copy) {}
+  // CHECK-MESSAGES: [[@LINE-1]]:54: warning: the parameter 'Copy'
+  // CHECK-FIXES: PositiveValueCopiedConstructor(const ExpensiveToCopyType& Copy) : Field(Copy) {}
+  ExpensiveToCopyType Field;
+};
+
+template <typename T>
+struct Container {
+  typedef const T & const_reference;
+};
+
+void NegativeTypedefParam(const Container<ExpensiveToCopyType>::const_reference Param) {
+}
+
+#define UNNECESSARY_VALUE_PARAM_IN_MACRO_BODY()         \
+  void inMacro(const ExpensiveToCopyType T) {           \
+  }                                                     \
+// Ensure fix is not applied.
+// CHECK-FIXES: void inMacro(const ExpensiveToCopyType T) {
+
+UNNECESSARY_VALUE_PARAM_IN_MACRO_BODY()
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: the const qualified parameter 'T'
+
+#define UNNECESSARY_VALUE_PARAM_IN_MACRO_ARGUMENT(ARGUMENT)     \
+  ARGUMENT
+
+UNNECESSARY_VALUE_PARAM_IN_MACRO_ARGUMENT(void inMacroArgument(const ExpensiveToCopyType InMacroArg) {})
+// CHECK-MESSAGES: [[@LINE-1]]:90: warning: the const qualified parameter 'InMacroArg'
+// CHECK-FIXES: void inMacroArgument(const ExpensiveToCopyType InMacroArg) {}
+
+struct VirtualMethod {
+  virtual ~VirtualMethod() {}
+  virtual void handle(ExpensiveToCopyType T) const = 0;
+};
+
+struct NegativeOverriddenMethod : public VirtualMethod {
+  void handle(ExpensiveToCopyType Overridden) const {
+    // CHECK-FIXES: handle(ExpensiveToCopyType Overridden) const {
+  }
+};
+
+struct NegativeDeletedMethod {
+  ~NegativeDeletedMethod() {}
+  NegativeDeletedMethod& operator=(NegativeDeletedMethod N) = delete;
+  // CHECK-FIXES: NegativeDeletedMethod& operator=(NegativeDeletedMethod N) = delete;
+};
diff --git a/test/clang-tidy/performance-unnecessary-value-param-header.cpp b/test/clang-tidy/performance-unnecessary-value-param-header.cpp
new file mode 100644 (file)
index 0000000..9e4c246
--- /dev/null
@@ -0,0 +1,20 @@
+// RUN: rm -rf %t
+// RUN: mkdir %t
+// RUN: cp %S/Inputs/performance-unnecessary-value-param/header.h %t/header.h
+// RUN: %check_clang_tidy %s performance-unnecessary-value-param %t/temp -- -- -std=c++11 -I %t
+// RUN: diff %t/header.h %S/Inputs/performance-unnecessary-value-param/header-fixed.h
+
+#include "header.h"
+
+
+
+int f1(int n, ABC v1, ABC v2) {
+  // CHECK-MESSAGES: [[@LINE-1]]:19: warning: the parameter 'v1' is copied for each invocation but only used as a const reference; consider making it a const reference [performance-unnecessary-value-param]
+  // CHECK-MESSAGES: [[@LINE-2]]:27: warning: the parameter 'v2' is copied for each invocation but only used as a const reference; consider making it a const reference [performance-unnecessary-value-param]
+  // CHECK-FIXES: int f1(int n, const ABC& v1, const ABC& v2) {
+  return v1.get(n) + v2.get(n);
+}
+int f2(int n, ABC v2) {
+  // CHECK-MESSAGES: [[@LINE-1]]:19: warning: the parameter 'v2' is copied for each invocation but only used as a const reference; consider making it a const reference [performance-unnecessary-value-param]
+  // CHECK-FIXES: int f2(int n, const ABC& v2) {
+}
diff --git a/test/clang-tidy/performance-unnecessary-value-param-incomplete-type.cpp b/test/clang-tidy/performance-unnecessary-value-param-incomplete-type.cpp
new file mode 100644 (file)
index 0000000..cd8c4da
--- /dev/null
@@ -0,0 +1,9 @@
+// RUN: %check_clang_tidy %s performance-unnecessary-value-param %t -- -fix-errors -- --std=c++11
+
+// Ensure that incomplete types result in an error from the frontend and not a
+// clang-tidy diagnostic about IncompleteType being expensive to copy.
+struct IncompleteType;
+void NegativeForIncompleteType(IncompleteType I) {
+  // CHECK-MESSAGES: [[@LINE-1]]:47: error: variable has incomplete type 'IncompleteType' [clang-diagnostic-error]
+}
+
diff --git a/test/clang-tidy/performance-unnecessary-value-param.cpp b/test/clang-tidy/performance-unnecessary-value-param.cpp
new file mode 100644 (file)
index 0000000..5491cbb
--- /dev/null
@@ -0,0 +1,350 @@
+// RUN: %check_clang_tidy %s performance-unnecessary-value-param %t
+
+// CHECK-FIXES: #include <utility>
+
+struct ExpensiveToCopyType {
+  const ExpensiveToCopyType & constReference() const {
+    return *this;
+  }
+  void nonConstMethod();
+  virtual ~ExpensiveToCopyType();
+};
+
+void mutate(ExpensiveToCopyType &);
+void mutate(ExpensiveToCopyType *);
+void useAsConstReference(const ExpensiveToCopyType &);
+void useByValue(ExpensiveToCopyType);
+
+// This class simulates std::pair<>. It is trivially copy constructible
+// and trivially destructible, but not trivially copy assignable.
+class SomewhatTrivial {
+ public:
+  SomewhatTrivial();
+  SomewhatTrivial(const SomewhatTrivial&) = default;
+  ~SomewhatTrivial() = default;
+  SomewhatTrivial& operator=(const SomewhatTrivial&);
+};
+
+struct MoveOnlyType {
+  MoveOnlyType(const MoveOnlyType &) = delete;
+  MoveOnlyType(MoveOnlyType &&) = default;
+  ~MoveOnlyType();
+  void constMethod() const;
+};
+
+struct ExpensiveMovableType {
+  ExpensiveMovableType();
+  ExpensiveMovableType(ExpensiveMovableType &&);
+  ExpensiveMovableType(const ExpensiveMovableType &) = default;
+  ExpensiveMovableType &operator=(const ExpensiveMovableType &) = default;
+  ExpensiveMovableType &operator=(ExpensiveMovableType &&);
+  ~ExpensiveMovableType();
+};
+
+void positiveExpensiveConstValue(const ExpensiveToCopyType Obj);
+// CHECK-FIXES: void positiveExpensiveConstValue(const ExpensiveToCopyType& Obj);
+void positiveExpensiveConstValue(const ExpensiveToCopyType Obj) {
+  // CHECK-MESSAGES: [[@LINE-1]]:60: warning: the const qualified parameter 'Obj' is copied for each invocation; consider making it a reference [performance-unnecessary-value-param]
+  // CHECK-FIXES: void positiveExpensiveConstValue(const ExpensiveToCopyType& Obj) {
+}
+
+void positiveExpensiveValue(ExpensiveToCopyType Obj);
+// CHECK-FIXES: void positiveExpensiveValue(const ExpensiveToCopyType& Obj);
+void positiveExpensiveValue(ExpensiveToCopyType Obj) {
+  // CHECK-MESSAGES: [[@LINE-1]]:49: warning: the parameter 'Obj' is copied for each invocation but only used as a const reference; consider making it a const reference [performance-unnecessary-value-param]
+  // CHECK-FIXES: void positiveExpensiveValue(const ExpensiveToCopyType& Obj) {
+  Obj.constReference();
+  useAsConstReference(Obj);
+  auto Copy = Obj;
+  useByValue(Obj);
+}
+
+void positiveWithComment(const ExpensiveToCopyType /* important */ S);
+// CHECK-FIXES: void positiveWithComment(const ExpensiveToCopyType& /* important */ S);
+void positiveWithComment(const ExpensiveToCopyType /* important */ S) {
+  // CHECK-MESSAGES: [[@LINE-1]]:68: warning: the const qualified
+  // CHECK-FIXES: void positiveWithComment(const ExpensiveToCopyType& /* important */ S) {
+}
+
+void positiveUnnamedParam(const ExpensiveToCopyType) {
+  // CHECK-MESSAGES: [[@LINE-1]]:52: warning: the const qualified parameter #1
+  // CHECK-FIXES: void positiveUnnamedParam(const ExpensiveToCopyType&) {
+}
+
+void positiveAndNegative(const ExpensiveToCopyType ConstCopy, const ExpensiveToCopyType& ConstRef, ExpensiveToCopyType Copy);
+// CHECK-FIXES: void positiveAndNegative(const ExpensiveToCopyType& ConstCopy, const ExpensiveToCopyType& ConstRef, const ExpensiveToCopyType& Copy);
+void positiveAndNegative(const ExpensiveToCopyType ConstCopy, const ExpensiveToCopyType& ConstRef, ExpensiveToCopyType Copy) {
+  // CHECK-MESSAGES: [[@LINE-1]]:52: warning: the const qualified parameter 'ConstCopy'
+  // CHECK-MESSAGES: [[@LINE-2]]:120: warning: the parameter 'Copy'
+  // CHECK-FIXES: void positiveAndNegative(const ExpensiveToCopyType& ConstCopy, const ExpensiveToCopyType& ConstRef, const ExpensiveToCopyType& Copy) {
+}
+
+struct PositiveConstValueConstructor {
+  PositiveConstValueConstructor(const ExpensiveToCopyType ConstCopy) {}
+  // CHECK-MESSAGES: [[@LINE-1]]:59: warning: the const qualified parameter 'ConstCopy'
+  // CHECK-FIXES: PositiveConstValueConstructor(const ExpensiveToCopyType& ConstCopy) {}
+};
+
+template <typename T> void templateWithNonTemplatizedParameter(const ExpensiveToCopyType S, T V) {
+  // CHECK-MESSAGES: [[@LINE-1]]:90: warning: the const qualified parameter 'S'
+  // CHECK-FIXES: template <typename T> void templateWithNonTemplatizedParameter(const ExpensiveToCopyType& S, T V) {
+}
+
+void instantiated() {
+  templateWithNonTemplatizedParameter(ExpensiveToCopyType(), ExpensiveToCopyType());
+  templateWithNonTemplatizedParameter(ExpensiveToCopyType(), 5);
+}
+
+template <typename T> void negativeTemplateType(const T V) {
+}
+
+void negativeArray(const ExpensiveToCopyType[]) {
+}
+
+void negativePointer(ExpensiveToCopyType* Obj) {
+}
+
+void negativeConstPointer(const ExpensiveToCopyType* Obj) {
+}
+
+void negativeConstReference(const ExpensiveToCopyType& Obj) {
+}
+
+void negativeReference(ExpensiveToCopyType& Obj) {
+}
+
+void negativeUniversalReference(ExpensiveToCopyType&& Obj) {
+}
+
+void negativeSomewhatTrivialConstValue(const SomewhatTrivial Somewhat) {
+}
+
+void negativeSomewhatTrivialValue(SomewhatTrivial Somewhat) {
+}
+
+void negativeConstBuiltIn(const int I) {
+}
+
+void negativeValueBuiltIn(int I) {
+}
+
+void negativeValueIsMutatedByReference(ExpensiveToCopyType Obj) {
+  mutate(Obj);
+}
+
+void negativeValueIsMutatatedByPointer(ExpensiveToCopyType Obj) {
+  mutate(&Obj);
+}
+
+void negativeValueIsReassigned(ExpensiveToCopyType Obj) {
+  Obj = ExpensiveToCopyType();
+}
+
+void negativeValueNonConstMethodIsCalled(ExpensiveToCopyType Obj) {
+  Obj.nonConstMethod();
+}
+
+struct PositiveValueUnusedConstructor {
+  PositiveValueUnusedConstructor(ExpensiveToCopyType Copy) {}
+  // CHECK-MESSAGES: [[@LINE-1]]:54: warning: the parameter 'Copy'
+  // CHECK-FIXES: PositiveValueUnusedConstructor(const ExpensiveToCopyType& Copy) {}
+};
+
+struct PositiveValueCopiedConstructor {
+  PositiveValueCopiedConstructor(ExpensiveToCopyType Copy) : Field(Copy) {}
+  // CHECK-MESSAGES: [[@LINE-1]]:54: warning: the parameter 'Copy'
+  // CHECK-FIXES: PositiveValueCopiedConstructor(const ExpensiveToCopyType& Copy) : Field(Copy) {}
+  ExpensiveToCopyType Field;
+};
+
+struct PositiveValueMovableConstructor {
+  PositiveValueMovableConstructor(ExpensiveMovableType Copy) : Field(Copy) {}
+  // CHECK-MESSAGES: [[@LINE-1]]:70: warning: parameter 'Copy'
+  // CHECK-FIXES: PositiveValueMovableConstructor(ExpensiveMovableType Copy) : Field(std::move(Copy)) {}
+  ExpensiveMovableType Field;
+};
+
+struct NegativeValueMovedConstructor {
+  NegativeValueMovedConstructor(ExpensiveMovableType Copy) : Field(static_cast<ExpensiveMovableType &&>(Copy)) {}
+  ExpensiveMovableType Field;
+};
+
+template <typename T>
+struct Container {
+  typedef const T & const_reference;
+};
+
+void NegativeTypedefParam(const Container<ExpensiveToCopyType>::const_reference Param) {
+}
+
+#define UNNECESSARY_VALUE_PARAM_IN_MACRO_BODY()         \
+  void inMacro(const ExpensiveToCopyType T) {           \
+  }                                                     \
+// Ensure fix is not applied.
+// CHECK-FIXES: void inMacro(const ExpensiveToCopyType T) {
+
+UNNECESSARY_VALUE_PARAM_IN_MACRO_BODY()
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: the const qualified parameter 'T'
+
+#define UNNECESSARY_VALUE_PARAM_IN_MACRO_ARGUMENT(ARGUMENT)     \
+  ARGUMENT
+
+UNNECESSARY_VALUE_PARAM_IN_MACRO_ARGUMENT(void inMacroArgument(const ExpensiveToCopyType InMacroArg) {})
+// CHECK-MESSAGES: [[@LINE-1]]:90: warning: the const qualified parameter 'InMacroArg'
+// CHECK-FIXES: void inMacroArgument(const ExpensiveToCopyType InMacroArg) {}
+
+struct VirtualMethod {
+  virtual ~VirtualMethod() {}
+  virtual void handle(ExpensiveToCopyType T) const = 0;
+};
+
+struct NegativeOverriddenMethod : public VirtualMethod {
+  void handle(ExpensiveToCopyType Overridden) const {
+    // CHECK-FIXES: handle(ExpensiveToCopyType Overridden) const {
+  }
+};
+
+struct VirtualMethodWarningOnly {
+  virtual void methodWithExpensiveValueParam(ExpensiveToCopyType T) {}
+  // CHECK-MESSAGES: [[@LINE-1]]:66: warning: the parameter 'T' is copied
+  // CHECK-FIXES: virtual void methodWithExpensiveValueParam(ExpensiveToCopyType T) {}
+  virtual ~VirtualMethodWarningOnly() {}
+};
+
+struct PositiveNonVirualMethod {
+  void method(const ExpensiveToCopyType T) {}
+  // CHECK-MESSAGES: [[@LINE-1]]:41: warning: the const qualified parameter 'T' is copied
+  // CHECK-FIXES: void method(const ExpensiveToCopyType& T) {}
+};
+
+struct NegativeDeletedMethod {
+  ~NegativeDeletedMethod() {}
+  NegativeDeletedMethod& operator=(NegativeDeletedMethod N) = delete;
+  // CHECK-FIXES: NegativeDeletedMethod& operator=(NegativeDeletedMethod N) = delete;
+};
+
+void NegativeMoveOnlyTypePassedByValue(MoveOnlyType M) {
+  M.constMethod();
+}
+
+void PositiveMoveOnCopyConstruction(ExpensiveMovableType E) {
+  auto F = E;
+  // CHECK-MESSAGES: [[@LINE-1]]:12: warning: parameter 'E' is passed by value and only copied once; consider moving it to avoid unnecessary copies [performance-unnecessary-value-param]
+  // CHECK-FIXES: auto F = std::move(E);
+}
+
+void PositiveConstRefNotMoveSinceReferencedMultipleTimes(ExpensiveMovableType E) {
+  // CHECK-MESSAGES: [[@LINE-1]]:79: warning: the parameter 'E' is copied
+  // CHECK-FIXES: void PositiveConstRefNotMoveSinceReferencedMultipleTimes(const ExpensiveMovableType& E) {
+  auto F = E;
+  auto G = E;
+}
+
+void PositiveMoveOnCopyAssignment(ExpensiveMovableType E) {
+  ExpensiveMovableType F;
+  F = E;
+  // CHECK-MESSAGES: [[@LINE-1]]:7: warning: parameter 'E' is passed by value
+  // CHECK-FIXES: F = std::move(E);
+}
+
+struct NotCopyAssigned {
+  NotCopyAssigned &operator=(const ExpensiveMovableType &);
+};
+
+void PositiveNoMoveForNonCopyAssigmentOperator(ExpensiveMovableType E) {
+  // CHECK-MESSAGES: [[@LINE-1]]:69: warning: the parameter 'E' is copied
+  // CHECK-FIXES: void PositiveNoMoveForNonCopyAssigmentOperator(const ExpensiveMovableType& E) {
+  NotCopyAssigned N;
+  N = E;
+}
+
+// The argument could be moved but is not since copy statement is inside a loop.
+void PositiveNoMoveInsideLoop(ExpensiveMovableType E) {
+  // CHECK-MESSAGES: [[@LINE-1]]:52: warning: the parameter 'E' is copied
+  // CHECK-FIXES: void PositiveNoMoveInsideLoop(const ExpensiveMovableType& E) {
+  for (;;) {
+    auto F = E;
+  }
+}
+
+void PositiveConstRefNotMoveConstructible(ExpensiveToCopyType T) {
+  // CHECK-MESSAGES: [[@LINE-1]]:63: warning: the parameter 'T' is copied
+  // CHECK-FIXES: void PositiveConstRefNotMoveConstructible(const ExpensiveToCopyType& T) {
+  auto U = T;
+}
+
+void PositiveConstRefNotMoveAssignable(ExpensiveToCopyType A) {
+  // CHECK-MESSAGES: [[@LINE-1]]:60: warning: the parameter 'A' is copied
+  // CHECK-FIXES: void PositiveConstRefNotMoveAssignable(const ExpensiveToCopyType& A) {
+  ExpensiveToCopyType B;
+  B = A;
+}
+
+// Case where parameter in declaration is already const-qualified but not in
+// implementation. Make sure a second 'const' is not added to the declaration.
+void PositiveConstDeclaration(const ExpensiveToCopyType A);
+// CHECK-FIXES: void PositiveConstDeclaration(const ExpensiveToCopyType& A);
+void PositiveConstDeclaration(ExpensiveToCopyType A) {
+  // CHECK-MESSAGES: [[@LINE-1]]:51: warning: the parameter 'A' is copied
+  // CHECK-FIXES: void PositiveConstDeclaration(const ExpensiveToCopyType& A) {
+}
+
+void PositiveNonConstDeclaration(ExpensiveToCopyType A);
+// CHECK-FIXES: void PositiveNonConstDeclaration(const ExpensiveToCopyType& A);
+void PositiveNonConstDeclaration(const ExpensiveToCopyType A) {
+  // CHECK-MESSAGES: [[@LINE-1]]:60: warning: the const qualified parameter 'A'
+  // CHECK-FIXES: void PositiveNonConstDeclaration(const ExpensiveToCopyType& A) {
+}
+
+void PositiveOnlyMessageAsReferencedInCompilationUnit(ExpensiveToCopyType A) {
+  // CHECK-MESSAGES: [[@LINE-1]]:75: warning: the parameter 'A' is copied
+  // CHECK-FIXES: void PositiveOnlyMessageAsReferencedInCompilationUnit(ExpensiveToCopyType A) {
+}
+
+void ReferenceFunctionOutsideOfCallExpr() {
+  void (*ptr)(ExpensiveToCopyType) = &PositiveOnlyMessageAsReferencedInCompilationUnit;
+}
+
+void PositiveMessageAndFixAsFunctionIsCalled(ExpensiveToCopyType A) {
+  // CHECK-MESSAGES: [[@LINE-1]]:66: warning: the parameter 'A' is copied
+  // CHECK-FIXES: void PositiveMessageAndFixAsFunctionIsCalled(const ExpensiveToCopyType& A) {
+}
+
+void ReferenceFunctionByCallingIt() {
+  PositiveMessageAndFixAsFunctionIsCalled(ExpensiveToCopyType());
+}
+
+// Virtual method overrides of dependent types cannot be recognized unless they
+// are marked as override or final. Test that check is not triggered on methods
+// marked with override or final.
+template <typename T>
+struct NegativeDependentTypeInterface {
+  virtual void Method(ExpensiveToCopyType E) = 0;
+};
+
+template <typename T>
+struct NegativeOverrideImpl : public NegativeDependentTypeInterface<T> {
+  void Method(ExpensiveToCopyType E) override {}
+};
+
+template <typename T>
+struct NegativeFinalImpl : public NegativeDependentTypeInterface<T> {
+  void Method(ExpensiveToCopyType E) final {}
+};
+
+struct PositiveConstructor {
+  PositiveConstructor(ExpensiveToCopyType E) : E(E) {}
+  // CHECK-MESSAGES: [[@LINE-1]]:43: warning: the parameter 'E' is copied
+  // CHECK-FIXES: PositiveConstructor(const ExpensiveToCopyType& E) : E(E) {}
+
+  ExpensiveToCopyType E;
+};
+
+struct NegativeUsingConstructor : public PositiveConstructor {
+  using PositiveConstructor::PositiveConstructor;
+};
+
+void fun() {
+  ExpensiveToCopyType E;
+  NegativeUsingConstructor S(E);
+}
diff --git a/test/clang-tidy/readability-avoid-const-params-in-decls.cpp b/test/clang-tidy/readability-avoid-const-params-in-decls.cpp
new file mode 100644 (file)
index 0000000..c2c12a4
--- /dev/null
@@ -0,0 +1,133 @@
+// RUN: %check_clang_tidy %s readability-avoid-const-params-in-decls %t
+
+using alias_type = bool;
+using alias_const_type = const bool;
+
+
+void F1(const int i);
+// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: parameter 'i' is const-qualified in the function declaration; const-qualification of parameters only has an effect in function definitions [readability-avoid-const-params-in-decls]
+// CHECK-FIXES: void F1(int i);
+
+void F2(const int *const i);
+// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: parameter 'i' is const-qualified
+// CHECK-FIXES: void F2(const int *i);
+
+void F3(int const i);
+// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: parameter 'i' is const-qualified
+// CHECK-FIXES: void F3(int i);
+
+void F4(alias_type const i);
+// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: parameter 'i' is const-qualified
+// CHECK-FIXES: void F4(alias_type i);
+
+void F5(const int);
+// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: parameter 1 is const-qualified
+// CHECK-FIXES: void F5(int);
+
+void F6(const int *const);
+// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: parameter 1 is const-qualified
+// CHECK-FIXES: void F6(const int *);
+
+void F7(int, const int);
+// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: parameter 2 is const-qualified
+// CHECK-FIXES: void F7(int, int);
+
+void F8(const int, const int);
+// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: parameter 1 is const-qualified
+// CHECK-MESSAGES: :[[@LINE-2]]:20: warning: parameter 2 is const-qualified
+// CHECK-FIXES: void F8(int, int);
+
+void F9(const int long_name);
+// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: parameter 'long_name'
+// CHECK-FIXES: void F9(int long_name);
+
+void F10(const int *const *const long_name);
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: parameter 'long_name'
+// CHECK-FIXES: void F10(const int *const *long_name);
+
+void F11(const unsigned int /*v*/);
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: parameter 1
+// CHECK-FIXES: void F11(unsigned int /*v*/);
+
+void F12(const bool b = true);
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: parameter 'b'
+// CHECK-FIXES: void F12(bool b = true);
+
+template<class T>
+int F13(const bool b = true);
+// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: parameter 'b'
+// CHECK-FIXES: int F13(bool b = true);
+int f13 = F13<int>();
+
+struct Foo {
+  Foo(const int i);
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: parameter 'i'
+  // CHECK-FIXES: Foo(int i);
+
+  void operator()(const int i);
+  // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: parameter 'i'
+  // CHECK-FIXES: void operator()(int i);
+};
+
+template <class T>
+struct FooT {
+  FooT(const int i);
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: parameter 'i'
+  // CHECK-FIXES: FooT(int i);
+
+  void operator()(const int i);
+  // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: parameter 'i'
+  // CHECK-FIXES: void operator()(int i);
+};
+FooT<int> f(1);
+
+// Do not match on definitions
+void NF1(const int i) {}
+void NF2(const int *const i) {}
+void NF3(int const i) {}
+void NF4(alias_type const i) {}
+void NF5(const int) {}
+void NF6(const int *const) {}
+void NF7(int, const int) {}
+void NF8(const int, const int) {}
+template <class T>
+int NF9(const int, const int) { return 0; }
+int nf9 = NF9<int>(1, 2);
+
+// Do not match on inline member functions
+struct Bar {
+  Bar(const int i) {}
+
+  void operator()(const int i) {}
+};
+
+// Do not match on inline member functions of a templated class
+template <class T>
+struct BarT {
+  BarT(const int i) {}
+
+  void operator()(const int i) {}
+};
+BarT<int> b(1);
+
+// Do not match on other stuff
+void NF(const alias_type& i);
+void NF(const int &i);
+void NF(const int *i);
+void NF(alias_const_type i);
+void NF(const alias_type&);
+void NF(const int&);
+void NF(const int*);
+void NF(const int* const*);
+void NF(alias_const_type);
+
+// Regression test for when the 'const' token is not in the code.
+#define CONCAT(a, b) a##b
+void ConstNotVisible(CONCAT(cons, t) int i);
+// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: parameter 'i'
+// We warn, but we can't give a fix
+// CHECK-FIXES: void ConstNotVisible(CONCAT(cons, t) int i);
+
+// Regression test. We should not warn (or crash) on lambda expressions
+auto lambda_with_name = [](const int n) {};
+auto lambda_without_name = [](const int) {};
diff --git a/test/clang-tidy/readability-braces-around-statements-assert-failure.cpp b/test/clang-tidy/readability-braces-around-statements-assert-failure.cpp
new file mode 100644 (file)
index 0000000..1462687
--- /dev/null
@@ -0,0 +1,12 @@
+// RUN: clang-tidy -checks='-*,readability-braces-around-statements' %s --
+
+// Note: this test expects no assert failure happened in clang-tidy.
+
+int test_failure() {
+  if (std::rand()) {
+  }
+}
+
+void test_failure2() {
+  for (a b c;;
+}
diff --git a/test/clang-tidy/readability-braces-around-statements-few-lines.cpp b/test/clang-tidy/readability-braces-around-statements-few-lines.cpp
new file mode 100644 (file)
index 0000000..00d253a
--- /dev/null
@@ -0,0 +1,30 @@
+// RUN: %check_clang_tidy %s readability-braces-around-statements %t -- -config="{CheckOptions: [{key: readability-braces-around-statements.ShortStatementLines, value: 4}]}" --
+
+void do_something(const char *) {}
+
+bool cond(const char *) {
+  return false;
+}
+
+void test() {
+  if (cond("if1") /*comment*/) do_something("same-line");
+
+  if (cond("if2"))
+    do_something("single-line");
+
+  if (cond("if3") /*comment*/)
+    // some comment
+    do_something("three"
+                 "lines");
+
+  if (cond("if4") /*comment*/)
+    // some comment
+    do_something("many"
+                 "many"
+                 "many"
+                 "many"
+                 "lines");
+  // CHECK-MESSAGES: :[[@LINE-7]]:31: warning: statement should be inside braces
+  // CHECK-FIXES: if (cond("if4") /*comment*/) {
+  // CHECK-FIXES: }
+}
diff --git a/test/clang-tidy/readability-braces-around-statements-format.cpp b/test/clang-tidy/readability-braces-around-statements-format.cpp
new file mode 100644 (file)
index 0000000..cc1ce92
--- /dev/null
@@ -0,0 +1,33 @@
+// RUN: %check_clang_tidy %s readability-braces-around-statements %t -- -format-style="{IndentWidth: 3}" --
+
+void do_something(const char *) {}
+
+bool cond(const char *) {
+  return false;
+}
+
+void test() {
+  if (cond("if0") /*comment*/) do_something("same-line");
+  // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: statement should be inside braces
+  // CHECK-FIXES: {{^}}   if (cond("if0") /*comment*/) {{{$}}
+  // CHECK-FIXES-NEXT: {{^}}      do_something("same-line");{{$}}
+  // CHECK-FIXES-NEXT: {{^}}   }{{$}}
+
+  if (1) while (2) if (3) for (;;) do ; while(false) /**/;/**/
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: statement should be inside braces
+  // CHECK-MESSAGES: :[[@LINE-2]]:19: warning: statement should be inside braces
+  // CHECK-MESSAGES: :[[@LINE-3]]:26: warning: statement should be inside braces
+  // CHECK-MESSAGES: :[[@LINE-4]]:35: warning: statement should be inside braces
+  // CHECK-MESSAGES: :[[@LINE-5]]:38: warning: statement should be inside braces
+  // CHECK-FIXES:      {{^}}   if (1) {{{$}}
+  // CHECK-FIXES-NEXT: {{^}}      while (2) {
+  // CHECK-FIXES-NEXT: {{^}}         if (3) {
+  // CHECK-FIXES-NEXT: {{^}}            for (;;) {
+  // CHECK-FIXES-NEXT: {{^}}               do {
+  // CHECK-FIXES-NEXT: {{^}}                  ;
+  // CHECK-FIXES-NEXT: {{^}}               } while (false) /**/; /**/
+  // CHECK-FIXES-NEXT: {{^}}            }
+  // CHECK-FIXES-NEXT: {{^}}         }
+  // CHECK-FIXES-NEXT: {{^}}      }
+  // CHECK-FIXES-NEXT: {{^}}   }
+}
diff --git a/test/clang-tidy/readability-braces-around-statements-same-line.cpp b/test/clang-tidy/readability-braces-around-statements-same-line.cpp
new file mode 100644 (file)
index 0000000..504043f
--- /dev/null
@@ -0,0 +1,36 @@
+// RUN: %check_clang_tidy %s readability-braces-around-statements %t -- -config="{CheckOptions: [{key: readability-braces-around-statements.ShortStatementLines, value: 1}]}" --
+
+void do_something(const char *) {}
+
+bool cond(const char *) {
+  return false;
+}
+
+void test() {
+  if (cond("if1") /*comment*/) do_something("same-line");
+
+  if (cond("if2"))
+    do_something("single-line");
+  // CHECK-MESSAGES: :[[@LINE-2]]:19: warning: statement should be inside braces
+  // CHECK-FIXES: if (cond("if2")) {
+  // CHECK-FIXES: }
+
+  if (cond("if3") /*comment*/)
+    // some comment
+    do_something("three"
+                 "lines");
+  // CHECK-MESSAGES: :[[@LINE-4]]:31: warning: statement should be inside braces
+  // CHECK-FIXES: if (cond("if3") /*comment*/) {
+  // CHECK-FIXES: }
+
+  if (cond("if4") /*comment*/)
+    // some comment
+    do_something("many"
+                 "many"
+                 "many"
+                 "many"
+                 "lines");
+  // CHECK-MESSAGES: :[[@LINE-7]]:31: warning: statement should be inside braces
+  // CHECK-FIXES: if (cond("if4") /*comment*/) {
+  // CHECK-FIXES: }
+}
diff --git a/test/clang-tidy/readability-braces-around-statements-single-line.cpp b/test/clang-tidy/readability-braces-around-statements-single-line.cpp
new file mode 100644 (file)
index 0000000..3edbd4b
--- /dev/null
@@ -0,0 +1,33 @@
+// RUN: %check_clang_tidy %s readability-braces-around-statements %t -- -config="{CheckOptions: [{key: readability-braces-around-statements.ShortStatementLines, value: 2}]}" --
+
+void do_something(const char *) {}
+
+bool cond(const char *) {
+  return false;
+}
+
+void test() {
+  if (cond("if1") /*comment*/) do_something("same-line");
+
+  if (cond("if2"))
+    do_something("single-line");
+
+  if (cond("if3") /*comment*/)
+    // some comment
+    do_something("three"
+                 "lines");
+  // CHECK-MESSAGES: :[[@LINE-4]]:31: warning: statement should be inside braces
+  // CHECK-FIXES: if (cond("if3") /*comment*/) {
+  // CHECK-FIXES: }
+
+  if (cond("if4") /*comment*/)
+    // some comment
+    do_something("many"
+                 "many"
+                 "many"
+                 "many"
+                 "lines");
+  // CHECK-MESSAGES: :[[@LINE-7]]:31: warning: statement should be inside braces
+  // CHECK-FIXES: if (cond("if4") /*comment*/) {
+  // CHECK-FIXES: }
+}
diff --git a/test/clang-tidy/readability-braces-around-statements.cpp b/test/clang-tidy/readability-braces-around-statements.cpp
new file mode 100644 (file)
index 0000000..9730735
--- /dev/null
@@ -0,0 +1,205 @@
+// RUN: %check_clang_tidy %s readability-braces-around-statements %t
+
+void do_something(const char *) {}
+
+bool cond(const char *) {
+  return false;
+}
+
+#define EMPTY_MACRO
+#define EMPTY_MACRO_FUN()
+
+void test() {
+  if (cond("if0") /*comment*/) do_something("same-line");
+  // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: statement should be inside braces
+  // CHECK-FIXES:   if (cond("if0") /*comment*/) { do_something("same-line");
+  // CHECK-FIXES: }
+
+  if (cond("if0.1"))
+    do_something("single-line");
+  // CHECK-MESSAGES: :[[@LINE-2]]:21: warning: statement should be inside braces
+  // CHECK-FIXES: if (cond("if0.1")) {
+  // CHECK-FIXES: }
+
+  if (cond("if1") /*comment*/)
+    // some comment
+    do_something("if1");
+  // CHECK-MESSAGES: :[[@LINE-3]]:31: warning: statement should be inside braces
+  // CHECK-FIXES: if (cond("if1") /*comment*/) {
+  // CHECK-FIXES: }
+  if (cond("if2")) {
+    do_something("if2");
+  }
+  if (cond("if3"))
+    ;
+  // CHECK-MESSAGES: :[[@LINE-2]]:19: warning: statement should be inside braces
+  // CHECK-FIXES: if (cond("if3")) {
+  // CHECK-FIXES: }
+
+  if (cond("if-else1"))
+    do_something("if-else1");
+  // CHECK-MESSAGES: :[[@LINE-2]]:24: warning: statement should be inside braces
+  // CHECK-FIXES: if (cond("if-else1")) {
+  // CHECK-FIXES: } else {
+  else
+    do_something("if-else1 else");
+  // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: statement should be inside braces
+  // CHECK-FIXES: }
+  if (cond("if-else2")) {
+    do_something("if-else2");
+  } else {
+    do_something("if-else2 else");
+  }
+
+  if (cond("if-else if-else1"))
+    do_something("if");
+  // CHECK-MESSAGES: :[[@LINE-2]]:32: warning: statement should be inside braces
+  // CHECK-FIXES: } else if (cond("else if1")) {
+  else if (cond("else if1"))
+    do_something("else if");
+  // CHECK-MESSAGES: :[[@LINE-2]]:29: warning: statement should be inside braces
+  else
+    do_something("else");
+  // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: statement should be inside braces
+  // CHECK-FIXES: }
+  if (cond("if-else if-else2")) {
+    do_something("if");
+  } else if (cond("else if2")) {
+    do_something("else if");
+  } else {
+    do_something("else");
+  }
+
+  for (;;)
+    do_something("for");
+  // CHECK-MESSAGES: :[[@LINE-2]]:11: warning: statement should be inside braces
+  // CHECK-FIXES: for (;;) {
+  // CHECK-FIXES: }
+  for (;;) {
+    do_something("for");
+  }
+  for (;;)
+    ;
+  // CHECK-MESSAGES: :[[@LINE-2]]:11: warning: statement should be inside braces
+  // CHECK-FIXES: for (;;) {
+  // CHECK-FIXES: }
+
+  int arr[4] = {1, 2, 3, 4};
+  for (int a : arr)
+    do_something("for-range");
+  // CHECK-MESSAGES: :[[@LINE-2]]:20: warning: statement should be inside braces
+  // CHECK-FIXES: for (int a : arr) {
+  // CHECK-FIXES: }
+  for (int a : arr) {
+    do_something("for-range");
+  }
+  for (int a : arr)
+    ;
+  // CHECK-MESSAGES: :[[@LINE-2]]:20: warning: statement should be inside braces
+  // CHECK-FIXES: for (int a : arr) {
+  // CHECK-FIXES: }
+
+  while (cond("while1"))
+    do_something("while");
+  // CHECK-MESSAGES: :[[@LINE-2]]:25: warning: statement should be inside braces
+  // CHECK-FIXES: while (cond("while1")) {
+  // CHECK-FIXES: }
+  while (cond("while2")) {
+    do_something("while");
+  }
+  while (false)
+    ;
+  // CHECK-MESSAGES: :[[@LINE-2]]:16: warning: statement should be inside braces
+  // CHECK-FIXES: while (false) {
+  // CHECK-FIXES: }
+
+  do
+    do_something("do1");
+  while (cond("do1"));
+  // CHECK-MESSAGES: :[[@LINE-3]]:5: warning: statement should be inside braces
+  // CHECK-FIXES: do {
+  // CHECK-FIXES: } while (cond("do1"));
+  do {
+    do_something("do2");
+  } while (cond("do2"));
+
+  do
+    ;
+  while (false);
+  // CHECK-MESSAGES: :[[@LINE-3]]:5: warning: statement should be inside braces
+  // CHECK-FIXES: do {
+  // CHECK-FIXES: } while (false);
+
+  if (cond("ifif1"))
+    // comment
+    if (cond("ifif2"))
+      // comment
+      /*comment*/ ; // comment
+  // CHECK-MESSAGES: :[[@LINE-5]]:21: warning: statement should be inside braces
+  // CHECK-MESSAGES: :[[@LINE-4]]:23: warning: statement should be inside braces
+  // CHECK-FIXES: if (cond("ifif1")) {
+  // CHECK-FIXES: if (cond("ifif2")) {
+  // CHECK-FIXES: }
+  // CHECK-FIXES-NEXT: }
+
+  if (cond("ifif3"))
+    // comment
+    if (cond("ifif4")) {
+      // comment
+      /*comment*/; // comment
+    }
+  // CHECK-MESSAGES: :[[@LINE-6]]:21: warning: statement should be inside braces
+  // CHECK-FIXES: if (cond("ifif3")) {
+  // CHECK-FIXES: }
+
+  if (cond("ifif5"))
+    ; /* multi-line
+        comment */
+  // CHECK-MESSAGES: :[[@LINE-3]]:21: warning: statement should be inside braces
+  // CHECK-FIXES: if (cond("ifif5")) {
+  // CHECK-FIXES: }/* multi-line
+
+  if (1) while (2) if (3) for (;;) do ; while(false) /**/;/**/
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: statement should be inside braces
+  // CHECK-MESSAGES: :[[@LINE-2]]:19: warning: statement should be inside braces
+  // CHECK-MESSAGES: :[[@LINE-3]]:26: warning: statement should be inside braces
+  // CHECK-MESSAGES: :[[@LINE-4]]:35: warning: statement should be inside braces
+  // CHECK-MESSAGES: :[[@LINE-5]]:38: warning: statement should be inside braces
+  // CHECK-FIXES: if (1) { while (2) { if (3) { for (;;) { do { ; } while(false) /**/;/**/
+  // CHECK-FIXES-NEXT: }
+  // CHECK-FIXES-NEXT: }
+  // CHECK-FIXES-NEXT: }
+  // CHECK-FIXES-NEXT: }
+}
+
+void f(const char *p) {
+  if (!p)
+    f("\
+");
+  // CHECK-MESSAGES: :[[@LINE-3]]:10: warning: statement should be inside braces
+  // CHECK-FIXES:      {{^  }}if (!p) {{{$}}
+  // CHECK-FIXES-NEXT: {{^    }}f("\{{$}}
+  // CHECK-FIXES-_NEXT: {{^}}");{{$}} FIXME: This breaks (http://llvm.org/PR26228)
+  // CHECK-FIXES-_NEXT: {{^}}}{{$}}
+}
+
+#define M(x) x
+
+int test_macros(bool b) {
+  if (b) {
+    return 1;
+  } else
+    M(return 2);
+  // CHECK-MESSAGES: :[[@LINE-2]]:9: warning: statement should be inside braces
+  // CHECK-FIXES: } else {
+  // CHECK-FIXES-NEXT:   M(return 2);
+  // CHECK-FIXES-NEXT: }
+  M(
+    for (;;)
+      ;
+  );
+  // CHECK-MESSAGES: :[[@LINE-3]]:13: warning: statement should be inside braces
+  // CHECK-FIXES: {{^}}    for (;;) {{{$}}
+  // CHECK-FIXES-NEXT: {{^      ;$}}
+  // CHECK-FIXES-NEXT: {{^}$}}
+}
diff --git a/test/clang-tidy/readability-container-size-empty.cpp b/test/clang-tidy/readability-container-size-empty.cpp
new file mode 100644 (file)
index 0000000..3c3c9a6
--- /dev/null
@@ -0,0 +1,439 @@
+// RUN: %check_clang_tidy %s readability-container-size-empty %t
+
+namespace std {
+template <typename T> struct vector {
+  vector();
+  bool operator==(const vector<T>& other) const;
+  bool operator!=(const vector<T>& other) const;
+  unsigned long size() const;
+  bool empty() const;
+};
+
+template <typename T> struct basic_string {
+  basic_string();
+  bool operator==(const basic_string<T>& other) const;
+  bool operator!=(const basic_string<T>& other) const;
+  bool operator==(const char *) const;
+  bool operator!=(const char *) const;
+  basic_string<T> operator+(const basic_string<T>& other) const;
+  unsigned long size() const;
+  bool empty() const;
+};
+
+typedef basic_string<char> string;
+typedef basic_string<wchar_t> wstring;
+
+inline namespace __v2 {
+template <typename T> struct set {
+  set();
+  bool operator==(const set<T>& other) const;
+  bool operator!=(const set<T>& other) const;
+  unsigned long size() const;
+  bool empty() const;
+};
+}
+
+}
+
+template <typename T>
+class TemplatedContainer {
+public:
+  bool operator==(const TemplatedContainer<T>& other) const;
+  bool operator!=(const TemplatedContainer<T>& other) const;
+  int size() const;
+  bool empty() const;
+};
+
+template <typename T>
+class PrivateEmpty {
+public:
+  bool operator==(const PrivateEmpty<T>& other) const;
+  bool operator!=(const PrivateEmpty<T>& other) const;
+  int size() const;
+private:
+  bool empty() const;
+};
+
+struct BoolSize {
+  bool size() const;
+  bool empty() const;
+};
+
+struct EnumSize {
+  enum E { one };
+  enum E size() const;
+  bool empty() const;
+};
+
+class Container {
+public:
+  bool operator==(const Container& other) const;
+  int size() const;
+  bool empty() const;
+};
+
+class Derived : public Container {
+};
+
+class Container2 {
+public:
+  int size() const;
+  bool empty() const { return size() == 0; }
+};
+
+class Container3 {
+public:
+  int size() const;
+  bool empty() const;
+};
+
+bool Container3::empty() const { return this->size() == 0; }
+
+class Container4 {
+public:
+  bool operator==(const Container4& rhs) const;
+  int size() const;
+  bool empty() const { return *this == Container4(); }
+};
+
+std::string s_func() {
+  return std::string();
+}
+
+int main() {
+  std::set<int> intSet;
+  std::string str;
+  std::string str2;
+  std::wstring wstr;
+  str.size() + 0;
+  str.size() - 0;
+  0 + str.size();
+  0 - str.size();
+  if (intSet.size() == 0)
+    ;
+  // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used to check for emptiness instead of 'size' [readability-container-size-empty]
+  // CHECK-FIXES: {{^  }}if (intSet.empty()){{$}}
+  // CHECK-MESSAGES: :32:8: note: method 'set<int>'::empty() defined here
+  if (intSet == std::set<int>())
+    ;
+  // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used to check for emptiness
+  // CHECK-FIXES: {{^  }}if (intSet.empty()){{$}}
+  // CHECK-MESSAGES: :32:8: note: method 'set<int>'::empty() defined here
+  if (s_func() == "")
+    ;
+  // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+  // CHECK-FIXES: {{^  }}if (s_func().empty()){{$}}
+  if (str.size() == 0)
+    ;
+  // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+  // CHECK-FIXES: {{^  }}if (str.empty()){{$}}
+  if ((str + str2).size() == 0)
+    ;
+  // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+  // CHECK-FIXES: {{^  }}if ((str + str2).empty()){{$}}
+  if (str == "")
+    ;
+  // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+  // CHECK-FIXES: {{^  }}if (str.empty()){{$}}
+  if (str + str2 == "")
+    ;
+  // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+  // CHECK-FIXES: {{^  }}if ((str + str2).empty()){{$}}
+  if (wstr.size() == 0)
+    ;
+  // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+  // CHECK-FIXES: {{^  }}if (wstr.empty()){{$}}
+  if (wstr == "")
+    ;
+  // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+  // CHECK-FIXES: {{^  }}if (wstr.empty()){{$}}
+  std::vector<int> vect;
+  if (vect.size() == 0)
+    ;
+  // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+  // CHECK-FIXES: {{^  }}if (vect.empty()){{$}}
+  if (vect == std::vector<int>())
+    ;
+  // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+  // CHECK-FIXES: {{^  }}if (vect.empty()){{$}}
+  if (vect.size() != 0)
+    ;
+  // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+  // CHECK-FIXES: {{^  }}if (!vect.empty()){{$}}
+  if (vect != std::vector<int>())
+    ;
+  // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+  // CHECK-FIXES: {{^  }}if (!vect.empty()){{$}}
+  if (0 == vect.size())
+    ;
+  // CHECK-MESSAGES: :[[@LINE-2]]:12: warning: the 'empty' method should be used
+  // CHECK-FIXES: {{^  }}if (vect.empty()){{$}}
+  if (0 != vect.size())
+    ;
+  // CHECK-MESSAGES: :[[@LINE-2]]:12: warning: the 'empty' method should be used
+  // CHECK-FIXES: {{^  }}if (!vect.empty()){{$}}
+  if (std::vector<int>() == vect)
+    ;
+  // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+  // CHECK-FIXES: {{^  }}if (vect.empty()){{$}}
+  if (std::vector<int>() != vect)
+    ;
+  // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+  // CHECK-FIXES: {{^  }}if (!vect.empty()){{$}}
+  if (vect.size() > 0)
+    ;
+  // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+  // CHECK-FIXES: {{^  }}if (!vect.empty()){{$}}
+  if (0 < vect.size())
+    ;
+  // CHECK-MESSAGES: :[[@LINE-2]]:11: warning: the 'empty' method should be used
+  // CHECK-FIXES: {{^  }}if (!vect.empty()){{$}}
+  if (vect.size() < 1)
+    ;
+  // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+  // CHECK-FIXES: {{^  }}if (vect.empty()){{$}}
+  if (1 > vect.size())
+    ;
+  // CHECK-MESSAGES: :[[@LINE-2]]:11: warning: the 'empty' method should be used
+  // CHECK-FIXES: {{^  }}if (vect.empty()){{$}}
+  if (vect.size() >= 1)
+    ;
+  // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+  // CHECK-FIXES: {{^  }}if (!vect.empty()){{$}}
+  if (1 <= vect.size())
+    ;
+  // CHECK-MESSAGES: :[[@LINE-2]]:12: warning: the 'empty' method should be used
+  // CHECK-FIXES: {{^  }}if (!vect.empty()){{$}}
+  if (vect.size() > 1) // no warning
+    ;
+  if (1 < vect.size()) // no warning
+    ;
+  if (vect.size() <= 1) // no warning
+    ;
+  if (1 >= vect.size()) // no warning
+    ;
+  if (!vect.size())
+    ;
+  // CHECK-MESSAGES: :[[@LINE-2]]:8: warning: the 'empty' method should be used
+  // CHECK-FIXES: {{^  }}if (vect.empty()){{$}}
+  if (vect.size())
+    ;
+  // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+  // CHECK-FIXES: {{^  }}if (!vect.empty()){{$}}
+
+  if (vect.empty())
+    ;
+
+  const std::vector<int> vect2;
+  if (vect2.size() != 0)
+    ;
+  // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+  // CHECK-FIXES: {{^  }}if (!vect2.empty()){{$}}
+
+  std::vector<int> *vect3 = new std::vector<int>();
+  if (vect3->size() == 0)
+    ;
+  // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+  // CHECK-FIXES: {{^  }}if (vect3->empty()){{$}}
+  if ((*vect3).size() == 0)
+    ;
+  // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+  // CHECK-FIXES: {{^  }}if ((*vect3).empty()){{$}}
+  if ((*vect3) == std::vector<int>())
+    ;
+  // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+  // CHECK-FIXES: {{^  }}if (vect3->empty()){{$}}
+  if (*vect3 == std::vector<int>())
+    ;
+  // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+  // CHECK-FIXES: {{^  }}if (vect3->empty()){{$}}
+
+  delete vect3;
+
+  const std::vector<int> &vect4 = vect2;
+  if (vect4.size() == 0)
+    ;
+  // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+  // CHECK-FIXES: {{^  }}if (vect4.empty()){{$}}
+  if (vect4 == std::vector<int>())
+    ;
+  // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+  // CHECK-FIXES: {{^  }}if (vect4.empty()){{$}}
+
+  TemplatedContainer<void> templated_container;
+  if (templated_container.size() == 0)
+    ;
+  // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+  // CHECK-FIXES: {{^  }}if (templated_container.empty()){{$}}
+  if (templated_container == TemplatedContainer<void>())
+    ;
+  // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+  // CHECK-FIXES: {{^  }}if (templated_container.empty()){{$}}
+  if (templated_container.size() != 0)
+    ;
+  // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+  // CHECK-FIXES: {{^  }}if (!templated_container.empty()){{$}}
+  if (templated_container != TemplatedContainer<void>())
+    ;
+  // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+  // CHECK-FIXES: {{^  }}if (!templated_container.empty()){{$}}
+  if (0 == templated_container.size())
+    ;
+  // CHECK-MESSAGES: :[[@LINE-2]]:12: warning: the 'empty' method should be used
+  // CHECK-FIXES: {{^  }}if (templated_container.empty()){{$}}
+  if (TemplatedContainer<void>() == templated_container)
+    ;
+  // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+  // CHECK-FIXES: {{^  }}if (templated_container.empty()){{$}}
+  if (0 != templated_container.size())
+    ;
+  // CHECK-MESSAGES: :[[@LINE-2]]:12: warning: the 'empty' method should be used
+  // CHECK-FIXES: {{^  }}if (!templated_container.empty()){{$}}
+  if (TemplatedContainer<void>() != templated_container)
+    ;
+  // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+  // CHECK-FIXES: {{^  }}if (!templated_container.empty()){{$}}
+  if (templated_container.size() > 0)
+    ;
+  // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+  // CHECK-FIXES: {{^  }}if (!templated_container.empty()){{$}}
+  if (0 < templated_container.size())
+    ;
+  // CHECK-MESSAGES: :[[@LINE-2]]:11: warning: the 'empty' method should be used
+  // CHECK-FIXES: {{^  }}if (!templated_container.empty()){{$}}
+  if (templated_container.size() < 1)
+    ;
+  // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+  // CHECK-FIXES: {{^  }}if (templated_container.empty()){{$}}
+  if (1 > templated_container.size())
+    ;
+  // CHECK-MESSAGES: :[[@LINE-2]]:11: warning: the 'empty' method should be used
+  // CHECK-FIXES: {{^  }}if (templated_container.empty()){{$}}
+  if (templated_container.size() >= 1)
+    ;
+  // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+  // CHECK-FIXES: {{^  }}if (!templated_container.empty()){{$}}
+  if (1 <= templated_container.size())
+    ;
+  // CHECK-MESSAGES: :[[@LINE-2]]:12: warning: the 'empty' method should be used
+  // CHECK-FIXES: {{^  }}if (!templated_container.empty()){{$}}
+  if (templated_container.size() > 1) // no warning
+    ;
+  if (1 < templated_container.size()) // no warning
+    ;
+  if (templated_container.size() <= 1) // no warning
+    ;
+  if (1 >= templated_container.size()) // no warning
+    ;
+  if (!templated_container.size())
+    ;
+  // CHECK-MESSAGES: :[[@LINE-2]]:8: warning: the 'empty' method should be used
+  // CHECK-FIXES: {{^  }}if (templated_container.empty()){{$}}
+  if (templated_container.size())
+    ;
+  // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+  // CHECK-FIXES: {{^  }}if (!templated_container.empty()){{$}}
+
+  if (templated_container.empty())
+    ;
+
+  // No warnings expected.
+  PrivateEmpty<void> private_empty;
+  if (private_empty.size() == 0)
+    ;
+  if (private_empty == PrivateEmpty<void>())
+    ;
+  if (private_empty.size() != 0)
+    ;
+  if (private_empty != PrivateEmpty<void>())
+    ;
+  if (0 == private_empty.size())
+    ;
+  if (PrivateEmpty<void>() == private_empty)
+    ;
+  if (0 != private_empty.size())
+    ;
+  if (PrivateEmpty<void>() != private_empty)
+    ;
+  if (private_empty.size() > 0)
+    ;
+  if (0 < private_empty.size())
+    ;
+  if (private_empty.size() < 1)
+    ;
+  if (1 > private_empty.size())
+    ;
+  if (private_empty.size() >= 1)
+    ;
+  if (1 <= private_empty.size())
+    ;
+  if (private_empty.size() > 1)
+    ;
+  if (1 < private_empty.size())
+    ;
+  if (private_empty.size() <= 1)
+    ;
+  if (1 >= private_empty.size())
+    ;
+  if (!private_empty.size())
+    ;
+  if (private_empty.size())
+    ;
+
+  // Types with weird size() return type.
+  BoolSize bs;
+  if (bs.size() == 0)
+    ;
+  EnumSize es;
+  if (es.size() == 0)
+    ;
+
+  Derived derived;
+  if (derived.size() == 0)
+    ;
+  // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+  // CHECK-FIXES: {{^  }}if (derived.empty()){{$}}
+  if (derived == Derived())
+    ;
+  // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+  // CHECK-FIXES: {{^  }}if (derived.empty()){{$}}
+}
+
+#define CHECKSIZE(x) if (x.size())
+// CHECK-FIXES: #define CHECKSIZE(x) if (x.size())
+
+template <typename T> void f() {
+  std::vector<T> v;
+  if (v.size())
+    ;
+  // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used to check for emptiness instead of 'size' [readability-container-size-empty]
+  // CHECK-FIXES: {{^  }}if (!v.empty()){{$}}
+  if (v == std::vector<T>())
+    ;
+  // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used to check for emptiness instead of comparing to an empty object [readability-container-size-empty]
+  // CHECK-FIXES: {{^  }}if (v.empty()){{$}}
+  // CHECK-FIXES-NEXT: ;
+  CHECKSIZE(v);
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: the 'empty' method should be used
+  // CHECK-MESSAGES: CHECKSIZE(v);
+
+  TemplatedContainer<T> templated_container;
+  if (templated_container.size())
+    ;
+  // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+  // CHECK-FIXES: {{^  }}if (!templated_container.empty()){{$}}
+  if (templated_container != TemplatedContainer<T>())
+    ;
+  // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+  // CHECK-FIXES: {{^  }}if (!templated_container.empty()){{$}}
+  // CHECK-FIXES-NEXT: ;
+  CHECKSIZE(templated_container);
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: the 'empty' method should be used
+  // CHECK-MESSAGES: CHECKSIZE(templated_container);
+}
+
+void g() {
+  f<int>();
+  f<double>();
+  f<char *>();
+}
diff --git a/test/clang-tidy/readability-delete-null-pointer.cpp b/test/clang-tidy/readability-delete-null-pointer.cpp
new file mode 100644 (file)
index 0000000..b46e52a
--- /dev/null
@@ -0,0 +1,86 @@
+// RUN: %check_clang_tidy %s readability-delete-null-pointer %t
+
+#define NULL 0
+
+void f() {
+  int *p = 0;
+
+  // #1
+  if (p) { // #2
+    delete p;
+  } // #3
+  // CHECK-MESSAGES: :[[@LINE-3]]:3: warning: 'if' statement is unnecessary; deleting null pointer has no effect [readability-delete-null-pointer]
+
+  // CHECK-FIXES: {{^  }}// #1
+  // CHECK-FIXES-NEXT: {{^  }}// #2
+  // CHECK-FIXES-NEXT: delete p;
+  // CHECK-FIXES-NEXT: {{^  }}// #3
+
+  int *p2 = new int[3];
+  // #4
+  if (p2) // #5
+    delete[] p2;
+  // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: 'if' statement is unnecessary;
+
+  // CHECK-FIXES: // #4
+  // CHECK-FIXES-NEXT: {{^  }}// #5
+  // CHECK-FIXES-NEXT: delete[] p2;
+
+  int *p3 = 0;
+  if (NULL != p3) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 'if' statement is unnecessary;
+    delete p3;
+  }
+  // CHECK-FIXES-NOT: if (NULL != p3) {
+  // CHECK-FIXES: delete p3;
+
+  int *p4 = nullptr;
+  if (p4 != nullptr) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 'if' statement is unnecessary;
+    delete p4;
+  }
+  // CHECK-FIXES-NOT: if (p4 != nullptr) {
+  // CHECK-FIXES: delete p4;
+
+  char *c;
+  if (c != 0) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 'if' statement is unnecessary;
+    delete c;
+  }
+  // CHECK-FIXES-NOT: if (c != 0) {
+  // CHECK-FIXES: delete c;
+
+  char *c2;
+  if (c2) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: 'if' statement is unnecessary;
+    // CHECK-FIXES: } else {
+    // CHECK-FIXES: c2 = c;
+    delete c2;
+  } else {
+    c2 = c;
+  }
+  struct A {
+    void foo() {
+      if (mp) // #6
+        delete mp;
+      // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: 'if' statement is unnecessary; deleting null pointer has no effect [readability-delete-null-pointer]
+      // CHECK-FIXES: {{^      }}// #6
+      // CHECK-FIXES-NEXT: delete mp;
+    }
+    int *mp;
+  };
+}
+
+void g() {
+  int *p5, *p6;
+  if (p5)
+    delete p6;
+
+  if (p5 && p6)
+    delete p5;
+
+  if (p6) {
+    int x = 5;
+    delete p6;
+  }
+}
diff --git a/test/clang-tidy/readability-deleted-default.cpp b/test/clang-tidy/readability-deleted-default.cpp
new file mode 100644 (file)
index 0000000..bf27e00
--- /dev/null
@@ -0,0 +1,127 @@
+// RUN: %check_clang_tidy %s readability-deleted-default %t -- -- -std=c++11 -fno-ms-compatibility
+
+class NoDefault {
+public:
+  NoDefault() = delete;
+  NoDefault(NoDefault &&Other) = delete;
+  NoDefault(const NoDefault &Other) = delete;
+};
+
+class MissingEverything {
+public:
+  MissingEverything() = default;
+  // CHECK-MESSAGES: warning: default constructor is explicitly defaulted but implicitly deleted, probably because a non-static data member or a base class is lacking a default constructor; definition can either be removed or explicitly deleted [readability-deleted-default]
+  MissingEverything(MissingEverything &&Other) = default;
+  // CHECK-MESSAGES: warning: move constructor is explicitly defaulted but implicitly deleted, probably because a non-static data member or a base class is neither copyable nor movable; definition can either be removed or explicitly deleted [readability-deleted-default]
+  MissingEverything(const MissingEverything &Other) = default;
+  // CHECK-MESSAGES: warning: copy constructor is explicitly defaulted but implicitly deleted, probably because a non-static data member or a base class is not copyable; definition can either be removed or explicitly deleted [readability-deleted-default]
+  MissingEverything &operator=(MissingEverything &&Other) = default;
+  // CHECK-MESSAGES: warning: move assignment operator is explicitly defaulted but implicitly deleted, probably because a base class or a non-static data member is not assignable, e.g. because the latter is marked 'const'; definition can either be removed or explicitly deleted [readability-deleted-default]
+  MissingEverything &operator=(const MissingEverything &Other) = default;
+  // CHECK-MESSAGES: warning: copy assignment operator is explicitly defaulted but implicitly deleted, probably because a base class or a non-static data member is not assignable, e.g. because the latter is marked 'const'; definition can either be removed or explicitly deleted [readability-deleted-default]
+
+private:
+  NoDefault ND;
+};
+
+class NotAssignable {
+public:
+  NotAssignable(NotAssignable &&Other) = default;
+  NotAssignable(const NotAssignable &Other) = default;
+  NotAssignable &operator=(NotAssignable &&Other) = default;
+  // CHECK-MESSAGES: warning: move assignment operator is explicitly defaulted but implicitly deleted
+  NotAssignable &operator=(const NotAssignable &Other) = default;
+  // CHECK-MESSAGES: warning: copy assignment operator is explicitly defaulted but implicitly deleted
+
+private:
+  const int I = 0;
+};
+
+class Movable {
+public:
+  Movable() = default;
+  Movable(Movable &&Other) = default;
+  Movable(const Movable &Other) = delete;
+  Movable &operator=(Movable &&Other) = default;
+  Movable &operator=(const Movable &Other) = delete;
+};
+
+class NotCopyable {
+public:
+  NotCopyable(NotCopyable &&Other) = default;
+  NotCopyable(const NotCopyable &Other) = default;
+  // CHECK-MESSAGES: warning: copy constructor is explicitly defaulted but implicitly deleted
+  NotCopyable &operator=(NotCopyable &&Other) = default;
+  NotCopyable &operator=(const NotCopyable &Other) = default;
+  // CHECK-MESSAGES: warning: copy assignment operator is explicitly defaulted but implicitly deleted
+private:
+  Movable M;
+};
+
+template <typename T> class Templated {
+public:
+  // No warning here, it is a templated class.
+  Templated() = default;
+  Templated(Templated &&Other) = default;
+  Templated(const Templated &Other) = default;
+  Templated &operator=(Templated &&Other) = default;
+  Templated &operator=(const Templated &Other) = default;
+
+  class InnerTemplated {
+  public:
+    // This class is not in itself templated, but we still don't have warning.
+    InnerTemplated() = default;
+    InnerTemplated(InnerTemplated &&Other) = default;
+    InnerTemplated(const InnerTemplated &Other) = default;
+    InnerTemplated &operator=(InnerTemplated &&Other) = default;
+    InnerTemplated &operator=(const InnerTemplated &Other) = default;
+
+  private:
+    T TVar;
+  };
+
+  class InnerNotTemplated {
+  public:
+    // This one could technically have warnings, but currently doesn't.
+    InnerNotTemplated() = default;
+    InnerNotTemplated(InnerNotTemplated &&Other) = default;
+    InnerNotTemplated(const InnerNotTemplated &Other) = default;
+    InnerNotTemplated &operator=(InnerNotTemplated &&Other) = default;
+    InnerNotTemplated &operator=(const InnerNotTemplated &Other) = default;
+
+  private:
+    int I;
+  };
+
+private:
+  const T TVar{};
+};
+
+int FunctionWithInnerClass() {
+  class InnerNotAssignable {
+  public:
+    InnerNotAssignable &operator=(InnerNotAssignable &&Other) = default;
+    // CHECK-MESSAGES: warning: move assignment operator is explicitly defaulted but implicitly deleted
+  private:
+    const int I = 0;
+  };
+  return 1;
+};
+
+template <typename T>
+int TemplateFunctionWithInnerClass() {
+  class InnerNotAssignable {
+  public:
+    InnerNotAssignable &operator=(InnerNotAssignable &&Other) = default;
+  private:
+    const T TVar{};
+  };
+  return 1;
+};
+
+void Foo() {
+  Templated<const int> V1;
+  Templated<int>::InnerTemplated V2;
+  Templated<float>::InnerNotTemplated V3;
+  TemplateFunctionWithInnerClass<int>();
+}
diff --git a/test/clang-tidy/readability-else-after-return.cpp b/test/clang-tidy/readability-else-after-return.cpp
new file mode 100644 (file)
index 0000000..782c929
--- /dev/null
@@ -0,0 +1,89 @@
+// RUN: %check_clang_tidy %s readability-else-after-return %t -- -- -std=c++11 -fexceptions
+
+void f(int a) {
+  if (a > 0)
+    return;
+  else // comment-0
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: do not use 'else' after 'return'
+  // CHECK-FIXES: {{^}}  // comment-0
+    return;
+
+  if (a > 0) {
+    return;
+  } else { // comment-1
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: do not use 'else' after 'return'
+  // CHECK-FIXES: {{^}}  } // comment-1
+    return;
+  }
+
+  if (a > 0) {
+    f(0);
+    if (a > 10)
+      return;
+  } else {
+    return;
+  }
+
+  if (a > 0)
+    f(0);
+  else if (a > 10)
+    return;
+  else // comment-2
+  // CHECK-FIXES-NOT: {{^}}  // comment-2
+    f(0);
+
+  if (a > 0)
+    if (a < 10)
+      return;
+    else // comment-3
+    // CHECK-FIXES-NOT: {{^}}    // comment-3
+      f(0);
+  else
+    if (a > 10)
+      return;
+    else // comment-4
+    // CHECK-FIXES-NOT: {{^}}    // comment-4
+      f(0);
+
+  if (a > 0) {
+    if (a < 10)
+      return;
+    else // comment-5
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: do not use 'else' after 'return'
+    // CHECK-FIXES: {{^}}    // comment-5
+      f(0);
+  } else {
+    if (a > 10)
+      return;
+    else // comment-6
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: do not use 'else' after 'return'
+    // CHECK-FIXES: {{^}}    // comment-6
+      f(0);
+  }
+}
+
+void foo() {
+  for (unsigned x = 0; x < 42; ++x) {
+    if (x) {
+      continue;
+    } else { // comment-7
+    // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: do not use 'else' after 'continue'
+    // CHECK-FIXES: {{^}}    } // comment-7
+      x++;
+    }
+    if (x) {
+      break;
+    } else { // comment-8
+    // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: do not use 'else' after 'break'
+    // CHECK-FIXES: {{^}}    } // comment-8
+      x++;
+    }
+    if (x) {
+      throw 42;
+    } else { // comment-9
+    // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: do not use 'else' after 'throw'
+    // CHECK-FIXES: {{^}}    } // comment-9
+      x++;
+    }
+  }
+}
diff --git a/test/clang-tidy/readability-function-size.cpp b/test/clang-tidy/readability-function-size.cpp
new file mode 100644 (file)
index 0000000..c75f5b6
--- /dev/null
@@ -0,0 +1,142 @@
+// RUN: %check_clang_tidy %s readability-function-size %t -- -config='{CheckOptions: [{key: readability-function-size.LineThreshold, value: 0}, {key: readability-function-size.StatementThreshold, value: 0}, {key: readability-function-size.BranchThreshold, value: 0}, {key: readability-function-size.ParameterThreshold, value: 5}, {key: readability-function-size.NestingThreshold, value: 2}]}' -- -std=c++11
+
+// Bad formatting is intentional, don't run clang-format over the whole file!
+
+void foo1() {
+}
+
+void foo2() {;}
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'foo2' exceeds recommended size/complexity thresholds [readability-function-size]
+// CHECK-MESSAGES: :[[@LINE-2]]:6: note: 1 statements (threshold 0)
+
+void foo3() {
+;
+
+}
+// CHECK-MESSAGES: :[[@LINE-4]]:6: warning: function 'foo3' exceeds recommended size/complexity
+// CHECK-MESSAGES: :[[@LINE-5]]:6: note: 3 lines including whitespace and comments (threshold 0)
+// CHECK-MESSAGES: :[[@LINE-6]]:6: note: 1 statements (threshold 0)
+
+void foo4(int i) { if (i) {} else; {}
+}
+// CHECK-MESSAGES: :[[@LINE-2]]:6: warning: function 'foo4' exceeds recommended size/complexity
+// CHECK-MESSAGES: :[[@LINE-3]]:6: note: 1 lines including whitespace and comments (threshold 0)
+// CHECK-MESSAGES: :[[@LINE-4]]:6: note: 3 statements (threshold 0)
+// CHECK-MESSAGES: :[[@LINE-5]]:6: note: 1 branches (threshold 0)
+
+void foo5(int i) {for(;i;)while(i)
+do;while(i);
+}
+// CHECK-MESSAGES: :[[@LINE-3]]:6: warning: function 'foo5' exceeds recommended size/complexity
+// CHECK-MESSAGES: :[[@LINE-4]]:6: note: 2 lines including whitespace and comments (threshold 0)
+// CHECK-MESSAGES: :[[@LINE-5]]:6: note: 7 statements (threshold 0)
+// CHECK-MESSAGES: :[[@LINE-6]]:6: note: 3 branches (threshold 0)
+
+template <typename T> T foo6(T i) {return i;
+}
+int x = foo6(0);
+// CHECK-MESSAGES: :[[@LINE-3]]:25: warning: function 'foo6' exceeds recommended size/complexity
+// CHECK-MESSAGES: :[[@LINE-4]]:25: note: 1 lines including whitespace and comments (threshold 0)
+// CHECK-MESSAGES: :[[@LINE-5]]:25: note: 1 statements (threshold 0)
+
+void foo7(int p1, int p2, int p3, int p4, int p5, int p6) {;}
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'foo7' exceeds recommended size/complexity
+// CHECK-MESSAGES: :[[@LINE-2]]:6: note: 1 statements (threshold 0)
+// CHECK-MESSAGES: :[[@LINE-3]]:6: note: 6 parameters (threshold 5)
+
+void bar1() { [](){;;;;;;;;;;;if(1){}}();
+
+
+}
+// CHECK-MESSAGES: :[[@LINE-4]]:6: warning: function 'bar1' exceeds recommended size/complexity
+// CHECK-MESSAGES: :[[@LINE-5]]:6: note: 3 lines including whitespace and comments (threshold 0)
+// CHECK-MESSAGES: :[[@LINE-6]]:6: note: 14 statements (threshold 0)
+// CHECK-MESSAGES: :[[@LINE-7]]:6: note: 1 branches (threshold 0)
+
+void bar2() { class A { void barx() {;;} }; }
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'bar2' exceeds recommended size/complexity
+// CHECK-MESSAGES: :[[@LINE-2]]:6: note: 3 statements (threshold 0)
+//
+// CHECK-MESSAGES: :[[@LINE-4]]:30: warning: function 'barx' exceeds recommended size/complexity
+// CHECK-MESSAGES: :[[@LINE-5]]:30: note: 2 statements (threshold 0)
+
+#define macro() {int x; {int y; {int z;}}}
+
+void baz0() { // 1
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'baz0' exceeds recommended size/complexity
+  // CHECK-MESSAGES: :[[@LINE-2]]:6: note: 27 lines including whitespace and comments (threshold 0)
+  // CHECK-MESSAGES: :[[@LINE-3]]:6: note: 9 statements (threshold 0)
+  int a;
+  { // 2
+    int b;
+    { // 3
+// CHECK-MESSAGES: :[[@LINE-1]]:5: note: nesting level 3 starts here (threshold 2)
+      int c;
+      { // 4
+        int d;
+      }
+    }
+  }
+  { // 2
+    int e;
+  }
+  { // 2
+    { // 3
+// CHECK-MESSAGES: :[[@LINE-1]]:5: note: nesting level 3 starts here (threshold 2)
+      int j;
+    }
+  }
+  macro()
+// CHECK-MESSAGES: :[[@LINE-1]]:3: note: nesting level 3 starts here (threshold 2)
+// CHECK-MESSAGES: :[[@LINE-28]]:25: note: expanded from macro 'macro'
+}
+
+// check that nested if's are not reported. this was broken initially
+void nesting_if() { // 1
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'nesting_if' exceeds recommended size/complexity
+  // CHECK-MESSAGES: :[[@LINE-2]]:6: note: 22 lines including whitespace and comments (threshold 0)
+  // CHECK-MESSAGES: :[[@LINE-3]]:6: note: 18 statements (threshold 0)
+  // CHECK-MESSAGES: :[[@LINE-4]]:6: note: 6 branches (threshold 0)
+  if (true) { // 2
+     int j;
+  } else if (true) { // 2
+     int j;
+     if (true) { // 3
+       // CHECK-MESSAGES: :[[@LINE-1]]:16: note: nesting level 3 starts here (threshold 2)
+       int j;
+     }
+  } else if (true) { // 2
+     int j;
+     if (true) { // 3
+       // CHECK-MESSAGES: :[[@LINE-1]]:16: note: nesting level 3 starts here (threshold 2)
+       int j;
+     }
+  } else if (true) { // 2
+     int j;
+  }
+}
+
+// however this should warn
+void bad_if_nesting() { // 1
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'bad_if_nesting' exceeds recommended size/complexity
+// CHECK-MESSAGES: :[[@LINE-2]]:6: note: 22 lines including whitespace and comments (threshold 0)
+// CHECK-MESSAGES: :[[@LINE-3]]:6: note: 12 statements (threshold 0)
+// CHECK-MESSAGES: :[[@LINE-4]]:6: note: 4 branches (threshold 0)
+  if (true) {    // 2
+    int j;
+  } else { // 2
+    if (true) { // 3
+      // CHECK-MESSAGES: :[[@LINE-1]]:15: note: nesting level 3 starts here (threshold 2)
+      int j;
+    } else { // 3
+      // CHECK-MESSAGES: :[[@LINE-1]]:12: note: nesting level 3 starts here (threshold 2)
+      if (true) { // 4
+        int j;
+      } else { // 4
+        if (true) { // 5
+          int j;
+        }
+      }
+    }
+  }
+}
diff --git a/test/clang-tidy/readability-identifier-naming.cpp b/test/clang-tidy/readability-identifier-naming.cpp
new file mode 100644 (file)
index 0000000..60bd69a
--- /dev/null
@@ -0,0 +1,423 @@
+// Remove UNSUPPORTED for powerpc64le when the problem introduced by
+// r288563 is resolved.
+// UNSUPPORTED: powerpc64le
+// RUN: %check_clang_tidy %s readability-identifier-naming %t -- \
+// RUN:   -config='{CheckOptions: [ \
+// RUN:     {key: readability-identifier-naming.AbstractClassCase, value: CamelCase}, \
+// RUN:     {key: readability-identifier-naming.AbstractClassPrefix, value: 'A'}, \
+// RUN:     {key: readability-identifier-naming.ClassCase, value: CamelCase}, \
+// RUN:     {key: readability-identifier-naming.ClassPrefix, value: 'C'}, \
+// RUN:     {key: readability-identifier-naming.ClassConstantCase, value: CamelCase}, \
+// RUN:     {key: readability-identifier-naming.ClassConstantPrefix, value: 'k'}, \
+// RUN:     {key: readability-identifier-naming.ClassMemberCase, value: CamelCase}, \
+// RUN:     {key: readability-identifier-naming.ClassMethodCase, value: camelBack}, \
+// RUN:     {key: readability-identifier-naming.ConstantCase, value: UPPER_CASE}, \
+// RUN:     {key: readability-identifier-naming.ConstantSuffix, value: '_CST'}, \
+// RUN:     {key: readability-identifier-naming.ConstexprFunctionCase, value: lower_case}, \
+// RUN:     {key: readability-identifier-naming.ConstexprMethodCase, value: lower_case}, \
+// RUN:     {key: readability-identifier-naming.ConstexprVariableCase, value: lower_case}, \
+// RUN:     {key: readability-identifier-naming.EnumCase, value: CamelCase}, \
+// RUN:     {key: readability-identifier-naming.EnumPrefix, value: 'E'}, \
+// RUN:     {key: readability-identifier-naming.EnumConstantCase, value: UPPER_CASE}, \
+// RUN:     {key: readability-identifier-naming.FunctionCase, value: camelBack}, \
+// RUN:     {key: readability-identifier-naming.GlobalConstantCase, value: UPPER_CASE}, \
+// RUN:     {key: readability-identifier-naming.GlobalFunctionCase, value: CamelCase}, \
+// RUN:     {key: readability-identifier-naming.GlobalVariableCase, value: lower_case}, \
+// RUN:     {key: readability-identifier-naming.GlobalVariablePrefix, value: 'g_'}, \
+// RUN:     {key: readability-identifier-naming.InlineNamespaceCase, value: lower_case}, \
+// RUN:     {key: readability-identifier-naming.LocalConstantCase, value: CamelCase}, \
+// RUN:     {key: readability-identifier-naming.LocalConstantPrefix, value: 'k'}, \
+// RUN:     {key: readability-identifier-naming.LocalVariableCase, value: lower_case}, \
+// RUN:     {key: readability-identifier-naming.MemberCase, value: CamelCase}, \
+// RUN:     {key: readability-identifier-naming.MemberPrefix, value: 'm_'}, \
+// RUN:     {key: readability-identifier-naming.ConstantMemberCase, value: lower_case}, \
+// RUN:     {key: readability-identifier-naming.PrivateMemberPrefix, value: '__'}, \
+// RUN:     {key: readability-identifier-naming.ProtectedMemberPrefix, value: '_'}, \
+// RUN:     {key: readability-identifier-naming.PublicMemberCase, value: lower_case}, \
+// RUN:     {key: readability-identifier-naming.MethodCase, value: camelBack}, \
+// RUN:     {key: readability-identifier-naming.PrivateMethodPrefix, value: '__'}, \
+// RUN:     {key: readability-identifier-naming.ProtectedMethodPrefix, value: '_'}, \
+// RUN:     {key: readability-identifier-naming.NamespaceCase, value: lower_case}, \
+// RUN:     {key: readability-identifier-naming.ParameterCase, value: camelBack}, \
+// RUN:     {key: readability-identifier-naming.ParameterPrefix, value: 'a_'}, \
+// RUN:     {key: readability-identifier-naming.ConstantParameterCase, value: camelBack}, \
+// RUN:     {key: readability-identifier-naming.ConstantParameterPrefix, value: 'i_'}, \
+// RUN:     {key: readability-identifier-naming.ParameterPackCase, value: camelBack}, \
+// RUN:     {key: readability-identifier-naming.PureFunctionCase, value: lower_case}, \
+// RUN:     {key: readability-identifier-naming.PureMethodCase, value: camelBack}, \
+// RUN:     {key: readability-identifier-naming.StaticConstantCase, value: UPPER_CASE}, \
+// RUN:     {key: readability-identifier-naming.StaticVariableCase, value: camelBack}, \
+// RUN:     {key: readability-identifier-naming.StaticVariablePrefix, value: 's_'}, \
+// RUN:     {key: readability-identifier-naming.StructCase, value: lower_case}, \
+// RUN:     {key: readability-identifier-naming.TemplateParameterCase, value: UPPER_CASE}, \
+// RUN:     {key: readability-identifier-naming.TemplateTemplateParameterCase, value: CamelCase}, \
+// RUN:     {key: readability-identifier-naming.TemplateUsingCase, value: lower_case}, \
+// RUN:     {key: readability-identifier-naming.TemplateUsingPrefix, value: 'u_'}, \
+// RUN:     {key: readability-identifier-naming.TypeTemplateParameterCase, value: camelBack}, \
+// RUN:     {key: readability-identifier-naming.TypeTemplateParameterSuffix, value: '_t'}, \
+// RUN:     {key: readability-identifier-naming.TypedefCase, value: lower_case}, \
+// RUN:     {key: readability-identifier-naming.TypedefSuffix, value: '_t'}, \
+// RUN:     {key: readability-identifier-naming.UnionCase, value: CamelCase}, \
+// RUN:     {key: readability-identifier-naming.UnionPrefix, value: 'U'}, \
+// RUN:     {key: readability-identifier-naming.UsingCase, value: lower_case}, \
+// RUN:     {key: readability-identifier-naming.ValueTemplateParameterCase, value: camelBack}, \
+// RUN:     {key: readability-identifier-naming.VariableCase, value: lower_case}, \
+// RUN:     {key: readability-identifier-naming.VirtualMethodCase, value: Camel_Snake_Case}, \
+// RUN:     {key: readability-identifier-naming.VirtualMethodPrefix, value: 'v_'}, \
+// RUN:     {key: readability-identifier-naming.MacroDefinitionCase, value: UPPER_CASE}, \
+// RUN:     {key: readability-identifier-naming.TypeAliasCase, value: camel_Snake_Back}, \
+// RUN:     {key: readability-identifier-naming.TypeAliasSuffix, value: '_t'}, \
+// RUN:     {key: readability-identifier-naming.IgnoreFailedSplit, value: 0} \
+// RUN:   ]}' -- -std=c++11 -fno-delayed-template-parsing \
+// RUN:   -I%S/Inputs/readability-identifier-naming \
+// RUN:   -isystem %S/Inputs/readability-identifier-naming/system
+
+// clang-format off
+
+#include <system-header.h>
+#include "user-header.h"
+// NO warnings or fixes expected from declarations within header files without
+// the -header-filter= option
+
+namespace FOO_NS {
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: invalid case style for namespace 'FOO_NS' [readability-identifier-naming]
+// CHECK-FIXES: {{^}}namespace foo_ns {{{$}}
+inline namespace InlineNamespace {
+// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: invalid case style for inline namespace 'InlineNamespace'
+// CHECK-FIXES: {{^}}inline namespace inline_namespace {{{$}}
+
+SYSTEM_NS::structure g_s1;
+// NO warnings or fixes expected as SYSTEM_NS and structure are declared in a header file
+
+USER_NS::object g_s2;
+// NO warnings or fixes expected as USER_NS and object are declared in a header file
+
+SYSTEM_MACRO(var1);
+// NO warnings or fixes expected as var1 is from macro expansion
+
+USER_MACRO(var2);
+// NO warnings or fixes expected as var2 is declared in a macro expansion
+
+#define BLA int FOO_bar
+BLA;
+// NO warnings or fixes expected as FOO_bar is from macro expansion
+
+int global0;
+#define USE_NUMBERED_GLOBAL(number) auto use_global##number = global##number
+USE_NUMBERED_GLOBAL(0);
+// NO warnings or fixes expected as global0 is pieced together in a macro
+// expansion.
+
+int global1;
+#define USE_NUMBERED_BAL(prefix, number) \
+  auto use_##prefix##bal##number = prefix##bal##number
+USE_NUMBERED_BAL(glo, 1);
+// NO warnings or fixes expected as global1 is pieced together in a macro
+// expansion.
+
+int global2;
+#define USE_RECONSTRUCTED(glo, bal) auto use_##glo##bal = glo##bal
+USE_RECONSTRUCTED(glo, bal2);
+// NO warnings or fixes expected as global2 is pieced together in a macro
+// expansion.
+
+int global;
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: invalid case style for global variable 'global'
+// CHECK-FIXES: {{^}}int g_global;{{$}}
+#define USE_IN_MACRO(m) auto use_##m = m
+USE_IN_MACRO(global);
+
+int global3;
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: invalid case style for global variable 'global3'
+// CHECK-FIXES: {{^}}int g_global3;{{$}}
+#define ADD_TO_SELF(m) (m) + (m)
+int g_twice_global3 = ADD_TO_SELF(global3);
+// CHECK-FIXES: {{^}}int g_twice_global3 = ADD_TO_SELF(g_global3);{{$}}
+
+enum my_enumeration {
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: invalid case style for enum 'my_enumeration'
+// CHECK-FIXES: {{^}}enum EMyEnumeration {{{$}}
+    MyConstant = 1,
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: invalid case style for enum constant 'MyConstant'
+// CHECK-FIXES: {{^}}    MY_CONSTANT = 1,{{$}}
+    your_CONST = 1,
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: invalid case style for enum constant 'your_CONST'
+// CHECK-FIXES: {{^}}    YOUR_CONST = 1,{{$}}
+    THIS_ConstValue = 1,
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: invalid case style for enum constant 'THIS_ConstValue'
+// CHECK-FIXES: {{^}}    THIS_CONST_VALUE = 1,{{$}}
+};
+
+constexpr int ConstExpr_variable = MyConstant;
+// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: invalid case style for constexpr variable 'ConstExpr_variable'
+// CHECK-FIXES: {{^}}constexpr int const_expr_variable = MY_CONSTANT;{{$}}
+
+class my_class {
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: invalid case style for class 'my_class'
+// CHECK-FIXES: {{^}}class CMyClass {{{$}}
+public:
+    my_class();
+// CHECK-FIXES: {{^}}    CMyClass();{{$}}
+
+    my_class(void*) : my_class() {}
+// CHECK-FIXES: {{^}}    CMyClass(void*) : CMyClass() {}{{$}}
+
+    ~
+      my_class();
+// (space in destructor token test, we could check trigraph but they will be deprecated)
+// CHECK-FIXES: {{^}}    ~{{$}}
+// CHECK-FIXES: {{^}}      CMyClass();{{$}}
+
+private:
+  const int MEMBER_one_1 = ConstExpr_variable;
+// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: invalid case style for constant member 'MEMBER_one_1'
+// CHECK-FIXES: {{^}}  const int member_one_1 = const_expr_variable;{{$}}
+  int member2 = 2;
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: invalid case style for private member 'member2'
+// CHECK-FIXES: {{^}}  int __member2 = 2;{{$}}
+  int _memberWithExtraUnderscores_ = 42;
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: invalid case style for private member '_memberWithExtraUnderscores_'
+// CHECK-FIXES: {{^}}  int __memberWithExtraUnderscores = 42;{{$}}
+
+private:
+    int private_member = 3;
+// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: invalid case style for private member 'private_member'
+// CHECK-FIXES: {{^}}    int __private_member = 3;{{$}}
+
+protected:
+    int ProtMember;
+// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: invalid case style for protected member 'ProtMember'
+// CHECK-FIXES: {{^}}    int _ProtMember;{{$}}
+
+public:
+    int PubMem;
+// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: invalid case style for public member 'PubMem'
+// CHECK-FIXES: {{^}}    int pub_mem;{{$}}
+
+    static const int classConstant;
+// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: invalid case style for class constant 'classConstant'
+// CHECK-FIXES: {{^}}    static const int kClassConstant;{{$}}
+    static int ClassMember_2;
+// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: invalid case style for class member 'ClassMember_2'
+// CHECK-FIXES: {{^}}    static int ClassMember2;{{$}}
+};
+class my_class;
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: invalid case style for class 'my_class'
+// CHECK-FIXES: {{^}}class CMyClass;{{$}}
+
+class my_forward_declared_class; // No warning should be triggered.
+
+const int my_class::classConstant = 4;
+// CHECK-MESSAGES: :[[@LINE-1]]:21: warning: invalid case style for class constant 'classConstant'
+// CHECK-FIXES: {{^}}const int CMyClass::kClassConstant = 4;{{$}}
+
+int my_class::ClassMember_2 = 5;
+// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: invalid case style for class member 'ClassMember_2'
+// CHECK-FIXES: {{^}}int CMyClass::ClassMember2 = 5;{{$}}
+
+class my_derived_class : public virtual my_class {};
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: invalid case style for class 'my_derived_class'
+// CHECK-FIXES: {{^}}class CMyDerivedClass : public virtual CMyClass {};{{$}}
+
+class CMyWellNamedClass {};
+// No warning expected as this class is well named.
+
+template <typename t_t>
+class CMyWellNamedClass2 : public my_class {
+  // CHECK-FIXES: {{^}}class CMyWellNamedClass2 : public CMyClass {{{$}}
+  t_t my_Bad_Member;
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: invalid case style for private member 'my_Bad_Member'
+  // CHECK-FIXES: {{^}}  t_t __my_Bad_Member;{{$}}
+  int my_Other_Bad_Member = 42;
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: invalid case style for private member 'my_Other_Bad_Member'
+  // CHECK-FIXES: {{^}}  int __my_Other_Bad_Member = 42;{{$}}
+public:
+  CMyWellNamedClass2() = default;
+  CMyWellNamedClass2(CMyWellNamedClass2 const&) = default;
+  CMyWellNamedClass2(CMyWellNamedClass2 &&) = default;
+  CMyWellNamedClass2(t_t a_v, void *a_p) : my_class(a_p), my_Bad_Member(a_v) {}
+  // CHECK-FIXES: {{^}}  CMyWellNamedClass2(t_t a_v, void *a_p) : CMyClass(a_p), __my_Bad_Member(a_v) {}{{$}}
+
+  CMyWellNamedClass2(t_t a_v) : my_class(), my_Bad_Member(a_v), my_Other_Bad_Member(11) {}
+  // CHECK-FIXES: {{^}}  CMyWellNamedClass2(t_t a_v) : CMyClass(), __my_Bad_Member(a_v), __my_Other_Bad_Member(11) {}{{$}}
+};
+void InstantiateClassMethods() {
+  // Ensure we trigger the instantiation of each constructor
+  CMyWellNamedClass2<int> x;
+  CMyWellNamedClass2<int> x2 = x;
+  CMyWellNamedClass2<int> x3 = static_cast<CMyWellNamedClass2<int>&&>(x2);
+  CMyWellNamedClass2<int> x4(42);
+  CMyWellNamedClass2<int> x5(42, nullptr);
+}
+
+template<typename T>
+// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: invalid case style for type template parameter 'T'
+// CHECK-FIXES: {{^}}template<typename t_t>{{$}}
+class my_templated_class : CMyWellNamedClass {};
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: invalid case style for class 'my_templated_class'
+// CHECK-FIXES: {{^}}class CMyTemplatedClass : CMyWellNamedClass {};{{$}}
+
+template<typename T>
+// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: invalid case style for type template parameter 'T'
+// CHECK-FIXES: {{^}}template<typename t_t>{{$}}
+class my_other_templated_class : my_templated_class<  my_class>, private my_derived_class {};
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: invalid case style for class 'my_other_templated_class'
+// CHECK-FIXES: {{^}}class CMyOtherTemplatedClass : CMyTemplatedClass<  CMyClass>, private CMyDerivedClass {};{{$}}
+
+template<typename t_t>
+using mysuper_tpl_t = my_other_templated_class  <:: FOO_NS  ::my_class>;
+// CHECK-FIXES: {{^}}using mysuper_tpl_t = CMyOtherTemplatedClass  <:: foo_ns  ::CMyClass>;{{$}}
+
+const int global_Constant = 6;
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: invalid case style for global constant 'global_Constant'
+// CHECK-FIXES: {{^}}const int GLOBAL_CONSTANT = 6;{{$}}
+int Global_variable = 7;
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: invalid case style for global variable 'Global_variable'
+// CHECK-FIXES: {{^}}int g_global_variable = 7;{{$}}
+
+void global_function(int PARAMETER_1, int const CONST_parameter) {
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: invalid case style for global function 'global_function'
+// CHECK-MESSAGES: :[[@LINE-2]]:26: warning: invalid case style for parameter 'PARAMETER_1'
+// CHECK-MESSAGES: :[[@LINE-3]]:49: warning: invalid case style for constant parameter 'CONST_parameter'
+// CHECK-FIXES: {{^}}void GlobalFunction(int a_parameter1, int const i_constParameter) {{{$}}
+    static const int THIS_static_ConsTant = 4;
+// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: invalid case style for static constant 'THIS_static_ConsTant'
+// CHECK-FIXES: {{^}}    static const int THIS_STATIC_CONS_TANT = 4;{{$}}
+    static int THIS_static_variable;
+// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: invalid case style for static variable 'THIS_static_variable'
+// CHECK-FIXES: {{^}}    static int s_thisStaticVariable;{{$}}
+    int const local_Constant = 3;
+// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: invalid case style for local constant 'local_Constant'
+// CHECK-FIXES: {{^}}    int const kLocalConstant = 3;{{$}}
+    int LOCAL_VARIABLE;
+// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: invalid case style for local variable 'LOCAL_VARIABLE'
+// CHECK-FIXES: {{^}}    int local_variable;{{$}}
+
+    int LOCAL_Array__[] = {0, 1, 2};
+// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: invalid case style for local variable 'LOCAL_Array__'
+// CHECK-FIXES: {{^}}    int local_array[] = {0, 1, 2};{{$}}
+
+    for (auto _ : LOCAL_Array__) {
+    }
+}
+
+template<typename ... TYPE_parameters>
+// CHECK-MESSAGES: :[[@LINE-1]]:23: warning: invalid case style for type template parameter 'TYPE_parameters'
+// CHECK-FIXES: {{^}}template<typename ... typeParameters_t>{{$}}
+void Global_Fun(TYPE_parameters... PARAMETER_PACK) {
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: invalid case style for global function 'Global_Fun'
+// CHECK-MESSAGES: :[[@LINE-2]]:36: warning: invalid case style for parameter pack 'PARAMETER_PACK'
+// CHECK-FIXES: {{^}}void GlobalFun(typeParameters_t... parameterPack) {{{$}}
+    global_function(1, 2);
+// CHECK-FIXES: {{^}}    GlobalFunction(1, 2);{{$}}
+    FOO_bar = Global_variable;
+// CHECK-FIXES: {{^}}    FOO_bar = g_global_variable;{{$}}
+// NO fix expected for FOO_bar declared in macro expansion
+}
+
+template<template<typename> class TPL_parameter, int COUNT_params, typename ... TYPE_parameters>
+// CHECK-MESSAGES: :[[@LINE-1]]:35: warning: invalid case style for template template parameter 'TPL_parameter'
+// CHECK-MESSAGES: :[[@LINE-2]]:54: warning: invalid case style for value template parameter 'COUNT_params'
+// CHECK-MESSAGES: :[[@LINE-3]]:81: warning: invalid case style for type template parameter 'TYPE_parameters'
+// CHECK-FIXES: {{^}}template<template<typename> class TplParameter, int countParams, typename ... typeParameters_t>{{$}}
+class test_CLASS {
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: invalid case style for class 'test_CLASS'
+// CHECK-FIXES: {{^}}class CTestClass {{{$}}
+};
+
+class abstract_class {
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: invalid case style for abstract class 'abstract_class'
+// CHECK-FIXES: {{^}}class AAbstractClass {{{$}}
+    virtual ~abstract_class() = 0;
+// CHECK-FIXES: {{^}}    virtual ~AAbstractClass() = 0;{{$}}
+    virtual void VIRTUAL_METHOD();
+// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: invalid case style for virtual method 'VIRTUAL_METHOD'
+// CHECK-FIXES: {{^}}    virtual void v_Virtual_Method();{{$}}
+    void non_Virtual_METHOD() {}
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: invalid case style for private method 'non_Virtual_METHOD'
+// CHECK-FIXES: {{^}}    void __non_Virtual_METHOD() {}{{$}}
+
+public:
+    static void CLASS_METHOD() {}
+// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: invalid case style for class method 'CLASS_METHOD'
+// CHECK-FIXES: {{^}}    static void classMethod() {}{{$}}
+
+    constexpr int CST_expr_Method() { return 2; }
+// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: invalid case style for constexpr method 'CST_expr_Method'
+// CHECK-FIXES: {{^}}    constexpr int cst_expr_method() { return 2; }{{$}}
+
+private:
+    void PRIVate_Method();
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: invalid case style for private method 'PRIVate_Method'
+// CHECK-FIXES: {{^}}    void __PRIVate_Method();{{$}}
+protected:
+    void protected_Method();
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: invalid case style for protected method 'protected_Method'
+// CHECK-FIXES: {{^}}    void _protected_Method();{{$}}
+public:
+    void public_Method();
+// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: invalid case style for method 'public_Method'
+// CHECK-FIXES: {{^}}    void publicMethod();{{$}}
+};
+
+constexpr int CE_function() { return 3; }
+// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: invalid case style for constexpr function 'CE_function'
+// CHECK-FIXES: {{^}}constexpr int ce_function() { return 3; }{{$}}
+
+struct THIS___Structure {
+// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: invalid case style for struct 'THIS___Structure'
+// CHECK-FIXES: {{^}}struct this_structure {{{$}}
+    THIS___Structure();
+// CHECK-FIXES: {{^}}    this_structure();{{$}}
+
+  union __MyUnion_is_wonderful__ {};
+// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: invalid case style for union '__MyUnion_is_wonderful__'
+// CHECK-FIXES: {{^}}  union UMyUnionIsWonderful {};{{$}}
+};
+
+typedef THIS___Structure struct_type;
+// CHECK-MESSAGES: :[[@LINE-1]]:26: warning: invalid case style for typedef 'struct_type'
+// CHECK-FIXES: {{^}}typedef this_structure struct_type_t;{{$}}
+
+struct_type GlobalTypedefTestFunction(struct_type a_argument1) {
+// CHECK-FIXES: {{^}}struct_type_t GlobalTypedefTestFunction(struct_type_t a_argument1) {
+    struct_type typedef_test_1;
+// CHECK-FIXES: {{^}}    struct_type_t typedef_test_1;
+}
+
+using my_struct_type = THIS___Structure;
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: invalid case style for type alias 'my_struct_type'
+// CHECK-FIXES: {{^}}using my_Struct_Type_t = this_structure;{{$}}
+
+template<typename t_t>
+using SomeOtherTemplate = my_other_templated_class  <:: FOO_NS  ::my_class>;
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: invalid case style for type alias 'SomeOtherTemplate'
+// CHECK-FIXES: {{^}}using some_Other_Template_t = CMyOtherTemplatedClass  <:: foo_ns  ::CMyClass>;{{$}}
+
+static void static_Function() {
+// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: invalid case style for function 'static_Function'
+// CHECK-FIXES: {{^}}static void staticFunction() {{{$}}
+
+  ::FOO_NS::InlineNamespace::abstract_class::CLASS_METHOD();
+// CHECK-FIXES: {{^}}  ::foo_ns::inline_namespace::AAbstractClass::classMethod();{{$}}
+  ::FOO_NS::InlineNamespace::static_Function();
+// CHECK-FIXES: {{^}}  ::foo_ns::inline_namespace::staticFunction();{{$}}
+
+  using ::FOO_NS::InlineNamespace::CE_function;
+// CHECK-FIXES: {{^}}  using ::foo_ns::inline_namespace::ce_function;{{$}}
+}
+
+#define MY_TEST_Macro(X) X()
+// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: invalid case style for macro definition 'MY_TEST_Macro'
+// CHECK-FIXES: {{^}}#define MY_TEST_MACRO(X) X()
+
+void MY_TEST_Macro(function) {}
+// CHECK-FIXES: {{^}}void MY_TEST_MACRO(function) {}
+}
+}
+
+template <typename t_t> struct a {
+  typename t_t::template b<> c;
+};
+
+template <template <typename> class A> struct b { A<int> c; };
diff --git a/test/clang-tidy/readability-implicit-bool-cast-allow-conditional-casts.cpp b/test/clang-tidy/readability-implicit-bool-cast-allow-conditional-casts.cpp
new file mode 100644 (file)
index 0000000..19c08ec
--- /dev/null
@@ -0,0 +1,66 @@
+// RUN: %check_clang_tidy %s readability-implicit-bool-cast %t \
+// RUN: -config='{CheckOptions: \
+// RUN:  [{key: readability-implicit-bool-cast.AllowConditionalIntegerCasts, value: 1}, \
+// RUN:   {key: readability-implicit-bool-cast.AllowConditionalPointerCasts, value: 1}]}' \
+// RUN: -- -std=c++11
+
+template<typename T>
+void functionTaking(T);
+
+int functionReturningInt();
+int* functionReturningPointer();
+
+struct Struct {
+  int member;
+};
+
+
+void regularImplicitCastIntegerToBoolIsNotIgnored() {
+  int integer = 0;
+  functionTaking<bool>(integer);
+  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'int' -> bool [readability-implicit-bool-cast]
+  // CHECK-FIXES: functionTaking<bool>(integer != 0);
+}
+
+void implicitCastIntegerToBoolInConditionalsIsAllowed() {
+  if (functionReturningInt()) {}
+  if (!functionReturningInt()) {}
+  if (functionReturningInt() && functionReturningPointer()) {}
+  if (!functionReturningInt() && !functionReturningPointer()) {}
+  for (; functionReturningInt(); ) {}
+  for (; functionReturningPointer(); ) {}
+  for (; functionReturningInt() && !functionReturningPointer() || (!functionReturningInt() && functionReturningPointer()); ) {}
+  while (functionReturningInt()) {}
+  while (functionReturningPointer()) {}
+  while (functionReturningInt() && !functionReturningPointer() || (!functionReturningInt() && functionReturningPointer())) {}
+  int value1 = functionReturningInt() ? 1 : 2;
+  int value2 = !functionReturningInt() ? 1 : 2;
+  int value3 = (functionReturningInt() && functionReturningPointer() || !functionReturningInt()) ? 1 : 2;
+  int value4 = functionReturningInt() ?: value3;
+  int *p1 = functionReturningPointer() ?: &value3;
+}
+
+void regularImplicitCastPointerToBoolIsNotIgnored() {
+  int* pointer = nullptr;
+  functionTaking<bool>(pointer);
+  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'int *' -> bool
+  // CHECK-FIXES: functionTaking<bool>(pointer != nullptr);
+
+  int Struct::* memberPointer = &Struct::member;
+  functionTaking<bool>(memberPointer);
+  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'int Struct::*' -> bool
+  // CHECK-FIXES: functionTaking<bool>(memberPointer != nullptr);
+}
+
+void implicitCastPointerToBoolInConditionalsIsAllowed() {
+  if (functionReturningPointer()) {}
+  if (not functionReturningPointer()) {}
+  int value1 = functionReturningPointer() ? 1 : 2;
+  int value2 = (not functionReturningPointer()) ? 1 : 2;
+
+  int Struct::* memberPointer = &Struct::member;
+  if (memberPointer) {}
+  if (memberPointer) {}
+  int value3 = memberPointer ? 1 : 2;
+  int value4 = (not memberPointer) ? 1 : 2;
+}
diff --git a/test/clang-tidy/readability-implicit-bool-cast-cxx98.cpp b/test/clang-tidy/readability-implicit-bool-cast-cxx98.cpp
new file mode 100644 (file)
index 0000000..791c7a1
--- /dev/null
@@ -0,0 +1,45 @@
+// RUN: %check_clang_tidy %s readability-implicit-bool-cast %t -- -- -std=c++98
+
+// We need NULL macro, but some buildbots don't like including <cstddef> header
+// This is a portable way of getting it to work
+#undef NULL
+#define NULL 0L
+
+template<typename T>
+void functionTaking(T);
+
+struct Struct {
+  int member;
+};
+
+void useOldNullMacroInReplacements() {
+  int* pointer = NULL;
+  functionTaking<bool>(pointer);
+  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'int *' -> bool [readability-implicit-bool-cast]
+  // CHECK-FIXES: functionTaking<bool>(pointer != 0);
+
+  int Struct::* memberPointer = NULL;
+  functionTaking<bool>(!memberPointer);
+  // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: implicit cast 'int Struct::*' -> bool
+  // CHECK-FIXES: functionTaking<bool>(memberPointer == 0);
+}
+
+void fixFalseLiteralConvertingToNullPointer() {
+  functionTaking<int*>(false);
+  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast bool -> 'int *'
+  // CHECK-FIXES: functionTaking<int*>(0);
+
+  int* pointer = NULL;
+  if (pointer == false) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: implicit cast bool -> 'int *'
+  // CHECK-FIXES: if (pointer == 0) {}
+
+  functionTaking<int Struct::*>(false);
+  // CHECK-MESSAGES: :[[@LINE-1]]:33: warning: implicit cast bool -> 'int Struct::*'
+  // CHECK-FIXES: functionTaking<int Struct::*>(0);
+
+  int Struct::* memberPointer = NULL;
+  if (memberPointer != false) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast bool -> 'int Struct::*'
+  // CHECK-FIXES: if (memberPointer != 0) {}
+}
diff --git a/test/clang-tidy/readability-implicit-bool-cast.cpp b/test/clang-tidy/readability-implicit-bool-cast.cpp
new file mode 100644 (file)
index 0000000..9e4aac7
--- /dev/null
@@ -0,0 +1,439 @@
+// RUN: %check_clang_tidy %s readability-implicit-bool-cast %t
+
+// We need NULL macro, but some buildbots don't like including <cstddef> header
+// This is a portable way of getting it to work
+#undef NULL
+#define NULL 0L
+
+template<typename T>
+void functionTaking(T);
+
+struct Struct {
+  int member;
+};
+
+
+////////// Implicit cast from bool.
+
+void implicitCastFromBoolSimpleCases() {
+  bool boolean = true;
+
+  functionTaking<bool>(boolean);
+
+  functionTaking<int>(boolean);
+  // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: implicit cast bool -> 'int' [readability-implicit-bool-cast]
+  // CHECK-FIXES: functionTaking<int>(static_cast<int>(boolean));
+
+  functionTaking<unsigned long>(boolean);
+  // CHECK-MESSAGES: :[[@LINE-1]]:33: warning: implicit cast bool -> 'unsigned long'
+  // CHECK-FIXES: functionTaking<unsigned long>(static_cast<unsigned long>(boolean));
+
+  functionTaking<char>(boolean);
+  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast bool -> 'char'
+  // CHECK-FIXES: functionTaking<char>(static_cast<char>(boolean));
+
+  functionTaking<float>(boolean);
+  // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: implicit cast bool -> 'float'
+  // CHECK-FIXES: functionTaking<float>(static_cast<float>(boolean));
+
+  functionTaking<double>(boolean);
+  // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: implicit cast bool -> 'double'
+  // CHECK-FIXES: functionTaking<double>(static_cast<double>(boolean));
+}
+
+float implicitCastFromBoolInReturnValue() {
+  bool boolean = false;
+  return boolean;
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: implicit cast bool -> 'float'
+  // CHECK-FIXES: return static_cast<float>(boolean);
+}
+
+void implicitCastFromBoolInSingleBoolExpressions(bool b1, bool b2) {
+  bool boolean = true;
+  boolean = b1 ^ b2;
+  boolean = b1 && b2;
+  boolean |= !b1 || !b2;
+  boolean &= b1;
+  boolean = b1 == true;
+  boolean = b2 != false;
+
+  int integer = boolean - 3;
+  // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: implicit cast bool -> 'int'
+  // CHECK-FIXES: int integer = static_cast<int>(boolean) - 3;
+
+  float floating = boolean / 0.3f;
+  // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: implicit cast bool -> 'float'
+  // CHECK-FIXES: float floating = static_cast<float>(boolean) / 0.3f;
+
+  char character = boolean;
+  // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: implicit cast bool -> 'char'
+  // CHECK-FIXES: char character = static_cast<char>(boolean);
+}
+
+void implicitCastFromBoollInComplexBoolExpressions() {
+  bool boolean = true;
+  bool anotherBoolean = false;
+
+  int integer = boolean && anotherBoolean;
+  // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: implicit cast bool -> 'int'
+  // CHECK-FIXES: int integer = static_cast<int>(boolean && anotherBoolean);
+
+  unsigned long unsignedLong = (! boolean) + 4ul;
+  // CHECK-MESSAGES: :[[@LINE-1]]:32: warning: implicit cast bool -> 'unsigned long'
+  // CHECK-FIXES: unsigned long unsignedLong = static_cast<unsigned long>(! boolean) + 4ul;
+
+  float floating = (boolean || anotherBoolean) * 0.3f;
+  // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: implicit cast bool -> 'float'
+  // CHECK-FIXES: float floating = static_cast<float>(boolean || anotherBoolean) * 0.3f;
+
+  double doubleFloating = (boolean && (anotherBoolean || boolean)) * 0.3;
+  // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: implicit cast bool -> 'double'
+  // CHECK-FIXES: double doubleFloating = static_cast<double>(boolean && (anotherBoolean || boolean)) * 0.3;
+}
+
+void implicitCastFromBoolLiterals() {
+  functionTaking<int>(true);
+  // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: implicit cast bool -> 'int'
+  // CHECK-FIXES: functionTaking<int>(1);
+
+  functionTaking<unsigned long>(false);
+  // CHECK-MESSAGES: :[[@LINE-1]]:33: warning: implicit cast bool -> 'unsigned long'
+  // CHECK-FIXES: functionTaking<unsigned long>(0u);
+
+  functionTaking<signed char>(true);
+  // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: implicit cast bool -> 'signed char'
+  // CHECK-FIXES: functionTaking<signed char>(1);
+
+  functionTaking<float>(false);
+  // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: implicit cast bool -> 'float'
+  // CHECK-FIXES: functionTaking<float>(0.0f);
+
+  functionTaking<double>(true);
+  // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: implicit cast bool -> 'double'
+  // CHECK-FIXES: functionTaking<double>(1.0);
+}
+
+void implicitCastFromBoolInComparisons() {
+  bool boolean = true;
+  int integer = 0;
+
+  functionTaking<bool>(boolean == integer);
+  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast bool -> 'int'
+  // CHECK-FIXES: functionTaking<bool>(static_cast<int>(boolean) == integer);
+
+  functionTaking<bool>(integer != boolean);
+  // CHECK-MESSAGES: :[[@LINE-1]]:35: warning: implicit cast bool -> 'int'
+  // CHECK-FIXES: functionTaking<bool>(integer != static_cast<int>(boolean));
+}
+
+void ignoreBoolComparisons() {
+  bool boolean = true;
+  bool anotherBoolean = false;
+
+  functionTaking<bool>(boolean == anotherBoolean);
+  functionTaking<bool>(boolean != anotherBoolean);
+}
+
+void ignoreExplicitCastsFromBool() {
+  bool boolean = true;
+
+  int integer = static_cast<int>(boolean) + 3;
+  float floating = static_cast<float>(boolean) * 0.3f;
+  char character = static_cast<char>(boolean);
+}
+
+void ignoreImplicitCastFromBoolInMacroExpansions() {
+  bool boolean = true;
+
+  #define CAST_FROM_BOOL_IN_MACRO_BODY boolean + 3
+  int integerFromMacroBody = CAST_FROM_BOOL_IN_MACRO_BODY;
+
+  #define CAST_FROM_BOOL_IN_MACRO_ARGUMENT(x) x + 3
+  int integerFromMacroArgument = CAST_FROM_BOOL_IN_MACRO_ARGUMENT(boolean);
+}
+
+namespace ignoreImplicitCastFromBoolInTemplateInstantiations {
+
+template<typename T>
+void templateFunction() {
+  bool boolean = true;
+  T uknownType = boolean + 3;
+}
+
+void useOfTemplateFunction() {
+  templateFunction<int>();
+}
+
+} // namespace ignoreImplicitCastFromBoolInTemplateInstantiations
+
+////////// Implicit cast to bool.
+
+void implicitCastToBoolSimpleCases() {
+  int integer = 10;
+  functionTaking<bool>(integer);
+  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'int' -> bool
+  // CHECK-FIXES: functionTaking<bool>(integer != 0);
+
+  unsigned long unsignedLong = 10;
+  functionTaking<bool>(unsignedLong);
+  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'unsigned long' -> bool
+  // CHECK-FIXES: functionTaking<bool>(unsignedLong != 0u);
+
+  float floating = 0.0f;
+  functionTaking<bool>(floating);
+  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'float' -> bool
+  // CHECK-FIXES: functionTaking<bool>(floating != 0.0f);
+
+  double doubleFloating = 1.0f;
+  functionTaking<bool>(doubleFloating);
+  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'double' -> bool
+  // CHECK-FIXES: functionTaking<bool>(doubleFloating != 0.0);
+
+  signed char character = 'a';
+  functionTaking<bool>(character);
+  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'signed char' -> bool
+  // CHECK-FIXES: functionTaking<bool>(character != 0);
+
+  int* pointer = nullptr;
+  functionTaking<bool>(pointer);
+  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'int *' -> bool
+  // CHECK-FIXES: functionTaking<bool>(pointer != nullptr);
+
+  auto pointerToMember = &Struct::member;
+  functionTaking<bool>(pointerToMember);
+  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'int Struct::*' -> bool
+  // CHECK-FIXES: functionTaking<bool>(pointerToMember != nullptr);
+}
+
+void implicitCastToBoolInSingleExpressions() {
+  int integer = 10;
+  bool boolComingFromInt = integer;
+  // CHECK-MESSAGES: :[[@LINE-1]]:28: warning: implicit cast 'int' -> bool
+  // CHECK-FIXES: bool boolComingFromInt = integer != 0;
+
+  float floating = 10.0f;
+  bool boolComingFromFloat = floating;
+  // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: implicit cast 'float' -> bool
+  // CHECK-FIXES: bool boolComingFromFloat = floating != 0.0f;
+
+  signed char character = 'a';
+  bool boolComingFromChar = character;
+  // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: implicit cast 'signed char' -> bool
+  // CHECK-FIXES: bool boolComingFromChar = character != 0;
+
+  int* pointer = nullptr;
+  bool boolComingFromPointer = pointer;
+  // CHECK-MESSAGES: :[[@LINE-1]]:32: warning: implicit cast 'int *' -> bool
+  // CHECK-FIXES: bool boolComingFromPointer = pointer != nullptr;
+}
+
+void implicitCastToBoolInComplexExpressions() {
+  bool boolean = true;
+
+  int integer = 10;
+  int anotherInteger = 20;
+  bool boolComingFromInteger = integer + anotherInteger;
+  // CHECK-MESSAGES: :[[@LINE-1]]:32: warning: implicit cast 'int' -> bool
+  // CHECK-FIXES: bool boolComingFromInteger = (integer + anotherInteger) != 0;
+
+  float floating = 0.2f;
+  bool boolComingFromFloating = floating - 0.3f || boolean;
+  // CHECK-MESSAGES: :[[@LINE-1]]:33: warning: implicit cast 'float' -> bool
+  // CHECK-FIXES: bool boolComingFromFloating = ((floating - 0.3f) != 0.0f) || boolean;
+
+  double doubleFloating = 0.3;
+  bool boolComingFromDoubleFloating = (doubleFloating - 0.4) && boolean;
+  // CHECK-MESSAGES: :[[@LINE-1]]:39: warning: implicit cast 'double' -> bool
+  // CHECK-FIXES: bool boolComingFromDoubleFloating = ((doubleFloating - 0.4) != 0.0) && boolean;
+}
+
+void implicitCastInNegationExpressions() {
+  int integer = 10;
+  bool boolComingFromNegatedInt = !integer;
+  // CHECK-MESSAGES: :[[@LINE-1]]:36: warning: implicit cast 'int' -> bool
+  // CHECK-FIXES: bool boolComingFromNegatedInt = integer == 0;
+
+  float floating = 10.0f;
+  bool boolComingFromNegatedFloat = ! floating;
+  // CHECK-MESSAGES: :[[@LINE-1]]:39: warning: implicit cast 'float' -> bool
+  // CHECK-FIXES: bool boolComingFromNegatedFloat = floating == 0.0f;
+
+  signed char character = 'a';
+  bool boolComingFromNegatedChar = (! character);
+  // CHECK-MESSAGES: :[[@LINE-1]]:39: warning: implicit cast 'signed char' -> bool
+  // CHECK-FIXES: bool boolComingFromNegatedChar = (character == 0);
+
+  int* pointer = nullptr;
+  bool boolComingFromNegatedPointer = not pointer;
+  // CHECK-MESSAGES: :[[@LINE-1]]:43: warning: implicit cast 'int *' -> bool
+  // CHECK-FIXES: bool boolComingFromNegatedPointer = pointer == nullptr;
+}
+
+void implicitCastToBoolInControlStatements() {
+  int integer = 10;
+  if (integer) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: implicit cast 'int' -> bool
+  // CHECK-FIXES: if (integer != 0) {}
+
+  long int longInteger = 0.2f;
+  for (;longInteger;) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: implicit cast 'long' -> bool
+  // CHECK-FIXES: for (;longInteger != 0;) {}
+
+  float floating = 0.3f;
+  while (floating) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: implicit cast 'float' -> bool
+  // CHECK-FIXES: while (floating != 0.0f) {}
+
+  double doubleFloating = 0.4;
+  do {} while (doubleFloating);
+  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: implicit cast 'double' -> bool
+  // CHECK-FIXES: do {} while (doubleFloating != 0.0);
+}
+
+bool implicitCastToBoolInReturnValue() {
+  float floating = 1.0f;
+  return floating;
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: implicit cast 'float' -> bool
+  // CHECK-FIXES: return floating != 0.0f;
+}
+
+void implicitCastToBoolFromLiterals() {
+  functionTaking<bool>(0);
+  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'int' -> bool
+  // CHECK-FIXES: functionTaking<bool>(false);
+
+  functionTaking<bool>(1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'int' -> bool
+  // CHECK-FIXES: functionTaking<bool>(true);
+
+  functionTaking<bool>(2ul);
+  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'unsigned long' -> bool
+  // CHECK-FIXES: functionTaking<bool>(true);
+
+
+  functionTaking<bool>(0.0f);
+  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'float' -> bool
+  // CHECK-FIXES: functionTaking<bool>(false);
+
+  functionTaking<bool>(1.0f);
+  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'float' -> bool
+  // CHECK-FIXES: functionTaking<bool>(true);
+
+  functionTaking<bool>(2.0);
+  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'double' -> bool
+  // CHECK-FIXES: functionTaking<bool>(true);
+
+
+  functionTaking<bool>('\0');
+  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'char' -> bool
+  // CHECK-FIXES: functionTaking<bool>(false);
+
+  functionTaking<bool>('a');
+  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'char' -> bool
+  // CHECK-FIXES: functionTaking<bool>(true);
+
+
+  functionTaking<bool>("");
+  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'const char *' -> bool
+  // CHECK-FIXES: functionTaking<bool>(true);
+
+  functionTaking<bool>("abc");
+  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'const char *' -> bool
+  // CHECK-FIXES: functionTaking<bool>(true);
+
+  functionTaking<bool>(NULL);
+  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'long' -> bool
+  // CHECK-FIXES: functionTaking<bool>(false);
+}
+
+void implicitCastToBoolFromUnaryMinusAndZeroLiterals() {
+  functionTaking<bool>(-0);
+  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'int' -> bool
+  // CHECK-FIXES: functionTaking<bool>((-0) != 0);
+
+  functionTaking<bool>(-0.0f);
+  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'float' -> bool
+  // CHECK-FIXES: functionTaking<bool>((-0.0f) != 0.0f);
+
+  functionTaking<bool>(-0.0);
+  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'double' -> bool
+  // CHECK-FIXES: functionTaking<bool>((-0.0) != 0.0);
+}
+
+void implicitCastToBoolInWithOverloadedOperators() {
+  struct UserStruct {
+    int operator()(int x) { return x; }
+    int operator+(int y) { return y; }
+  };
+
+  UserStruct s;
+
+  functionTaking<bool>(s(0));
+  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'int' -> bool
+  // CHECK-FIXES: functionTaking<bool>(s(0) != 0);
+
+  functionTaking<bool>(s + 2);
+  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: implicit cast 'int' -> bool
+  // CHECK-FIXES: functionTaking<bool>((s + 2) != 0);
+}
+
+int functionReturningInt();
+int* functionReturningPointer();
+
+void ignoreImplicitCastToBoolWhenDeclaringVariableInControlStatements() {
+  if (int integer = functionReturningInt()) {}
+
+  while (int* pointer = functionReturningPointer()) {}
+}
+
+void ignoreExplicitCastsToBool() {
+  int integer = 10;
+  bool boolComingFromInt = static_cast<bool>(integer);
+
+  float floating = 10.0f;
+  bool boolComingFromFloat = static_cast<bool>(floating);
+
+  char character = 'a';
+  bool boolComingFromChar = static_cast<bool>(character);
+
+  int* pointer = nullptr;
+  bool booleanComingFromPointer = static_cast<bool>(pointer);
+}
+
+void ignoreImplicitCastToBoolInMacroExpansions() {
+  int integer = 3;
+
+  #define CAST_TO_BOOL_IN_MACRO_BODY integer && false
+  bool boolFromMacroBody = CAST_TO_BOOL_IN_MACRO_BODY;
+
+  #define CAST_TO_BOOL_IN_MACRO_ARGUMENT(x) x || true
+  bool boolFromMacroArgument = CAST_TO_BOOL_IN_MACRO_ARGUMENT(integer);
+}
+
+namespace ignoreImplicitCastToBoolInTemplateInstantiations {
+
+template<typename T>
+void templateFunction() {
+  T unknownType = 0;
+  bool boolean = unknownType;
+}
+
+void useOfTemplateFunction() {
+  templateFunction<int>();
+}
+
+} // namespace ignoreImplicitCastToBoolInTemplateInstantiations
+
+namespace ignoreUserDefinedConversionOperator {
+
+struct StructWithUserConversion {
+  operator bool();
+};
+
+void useOfUserConversion() {
+  StructWithUserConversion structure;
+  functionTaking<bool>(structure);
+}
+
+} // namespace ignoreUserDefinedConversionOperator
diff --git a/test/clang-tidy/readability-inconsistent-declaration-parameter-name.cpp b/test/clang-tidy/readability-inconsistent-declaration-parameter-name.cpp
new file mode 100644 (file)
index 0000000..d2c2d7d
--- /dev/null
@@ -0,0 +1,188 @@
+// RUN: %check_clang_tidy %s readability-inconsistent-declaration-parameter-name %t -- -- -std=c++11 -fno-delayed-template-parsing
+
+void consistentFunction(int a, int b, int c);
+void consistentFunction(int a, int b, int c);
+void consistentFunction(int a, int b, int /*c*/);
+void consistentFunction(int /*c*/, int /*c*/, int /*c*/);
+
+//////////////////////////////////////////////////////
+
+// CHECK-MESSAGES: :[[@LINE+1]]:6: warning: function 'inconsistentFunction' has 2 other declarations with different parameter names [readability-inconsistent-declaration-parameter-name]
+void inconsistentFunction(int a, int b, int c);
+// CHECK-MESSAGES: :[[@LINE+2]]:6: note: the 1st inconsistent declaration seen here
+// CHECK-MESSAGES: :[[@LINE+1]]:6: note: differing parameters are named here: ('d', 'e', 'f'), in the other declaration: ('a', 'b', 'c')
+void inconsistentFunction(int d, int e, int f);
+// CHECK-MESSAGES: :[[@LINE+2]]:6: note: the 2nd inconsistent declaration seen here
+// CHECK-MESSAGES: :[[@LINE+1]]:6: note: differing parameters are named here: ('x', 'y', 'z'), in the other declaration: ('a', 'b', 'c')
+void inconsistentFunction(int x, int y, int z);
+
+//////////////////////////////////////////////////////
+
+// CHECK-MESSAGES: :[[@LINE+4]]:6: warning: function 'inconsistentFunctionWithVisibleDefinition' has a definition with different parameter names [readability-inconsistent-declaration-parameter-name]
+// CHECK-MESSAGES: :[[@LINE+9]]:6: note: the definition seen here
+// CHECK-MESSAGES: :[[@LINE+2]]:6: note: differing parameters are named here: ('a'), in definition: ('c')
+// CHECK-FIXES: void inconsistentFunctionWithVisibleDefinition(int c);
+void inconsistentFunctionWithVisibleDefinition(int a);
+// CHECK-MESSAGES: :[[@LINE+4]]:6: warning: function 'inconsistentFunctionWithVisibleDefinition' has a definition
+// CHECK-MESSAGES: :[[@LINE+4]]:6: note: the definition seen here
+// CHECK-MESSAGES: :[[@LINE+2]]:6: note: differing parameters are named here: ('b'), in definition: ('c')
+// CHECK-FIXES: void inconsistentFunctionWithVisibleDefinition(int c);
+void inconsistentFunctionWithVisibleDefinition(int b);
+void inconsistentFunctionWithVisibleDefinition(int c) { c; }
+
+// CHECK-MESSAGES: :[[@LINE+3]]:6: warning: function 'inconsidentFunctionWithUnreferencedParameterInDefinition' has a definition
+// CHECK-MESSAGES: :[[@LINE+3]]:6: note: the definition seen here
+// CHECK-MESSAGES: :[[@LINE+1]]:6: note: differing parameters are named here: ('a'), in definition: ('b')
+void inconsidentFunctionWithUnreferencedParameterInDefinition(int a);
+void inconsidentFunctionWithUnreferencedParameterInDefinition(int b) {}
+
+//////////////////////////////////////////////////////
+
+struct Struct {
+// CHECK-MESSAGES: :[[@LINE+4]]:8: warning: function 'Struct::inconsistentFunction' has a definition
+// CHECK-MESSAGES: :[[@LINE+6]]:14: note: the definition seen here
+// CHECK-MESSAGES: :[[@LINE+2]]:8: note: differing parameters are named here: ('a'), in definition: ('b')
+// CHECK-FIXES: void inconsistentFunction(int b);
+  void inconsistentFunction(int a);
+};
+
+void Struct::inconsistentFunction(int b) { b = 0; }
+
+//////////////////////////////////////////////////////
+
+struct SpecialFunctions {
+// CHECK-MESSAGES: :[[@LINE+4]]:3: warning: function 'SpecialFunctions::SpecialFunctions' has a definition
+// CHECK-MESSAGES: :[[@LINE+12]]:19: note: the definition seen here
+// CHECK-MESSAGES: :[[@LINE+2]]:3: note: differing parameters are named here: ('a'), in definition: ('b')
+// CHECK-FIXES: SpecialFunctions(int b);
+  SpecialFunctions(int a);
+
+// CHECK-MESSAGES: :[[@LINE+4]]:21: warning: function 'SpecialFunctions::operator=' has a definition
+// CHECK-MESSAGES: :[[@LINE+8]]:37: note: the definition seen here
+// CHECK-MESSAGES: :[[@LINE+2]]:21: note: differing parameters are named here: ('a'), in definition: ('b')
+// CHECK-FIXES: SpecialFunctions& operator=(const SpecialFunctions& b);
+  SpecialFunctions& operator=(const SpecialFunctions& a);
+};
+
+SpecialFunctions::SpecialFunctions(int b) { b; }
+
+SpecialFunctions& SpecialFunctions::operator=(const SpecialFunctions& b) { b; return *this; }
+
+//////////////////////////////////////////////////////
+
+// CHECK-MESSAGES: :[[@LINE+5]]:6: warning: function 'templateFunctionWithSeparateDeclarationAndDefinition' has a definition
+// CHECK-MESSAGES: :[[@LINE+7]]:6: note: the definition seen here
+// CHECK-MESSAGES: :[[@LINE+3]]:6: note: differing parameters are named here: ('a'), in definition: ('b')
+// CHECK-FIXES: void templateFunctionWithSeparateDeclarationAndDefinition(T b);
+template<typename T>
+void templateFunctionWithSeparateDeclarationAndDefinition(T a);
+
+template<typename T>
+void templateFunctionWithSeparateDeclarationAndDefinition(T b) { b; }
+
+//////////////////////////////////////////////////////
+
+template<typename T>
+void templateFunctionWithSpecializations(T a) { a; }
+
+template<>
+// CHECK-MESSAGES: :[[@LINE+3]]:6: warning: function template specialization 'templateFunctionWithSpecializations<int>' has a primary template declaration with different parameter names [readability-inconsistent-declaration-parameter-name]
+// CHECK-MESSAGES: :[[@LINE-4]]:6: note: the primary template declaration seen here
+// CHECK-MESSAGES: :[[@LINE+1]]:6: note: differing parameters are named here: ('b'), in primary template declaration: ('a')
+void templateFunctionWithSpecializations(int b) { b; }
+
+template<>
+// CHECK-MESSAGES: :[[@LINE+3]]:6: warning: function template specialization 'templateFunctionWithSpecializations<float>' has a primary template
+// CHECK-MESSAGES: :[[@LINE-10]]:6: note: the primary template declaration seen here
+// CHECK-MESSAGES: :[[@LINE+1]]:6: note: differing parameters are named here: ('c'), in primary template declaration: ('a')
+void templateFunctionWithSpecializations(float c) { c; }
+
+//////////////////////////////////////////////////////
+
+template<typename T>
+void templateFunctionWithoutDefinitionButWithSpecialization(T a);
+
+template<>
+// CHECK-MESSAGES: :[[@LINE+3]]:6: warning: function template specialization 'templateFunctionWithoutDefinitionButWithSpecialization<int>' has a primary template
+// CHECK-MESSAGES: :[[@LINE-4]]:6: note: the primary template declaration seen here
+// CHECK-MESSAGES: :[[@LINE+1]]:6: note: differing parameters are named here: ('b'), in primary template declaration: ('a')
+void templateFunctionWithoutDefinitionButWithSpecialization(int b) { b; }
+
+//////////////////////////////////////////////////////
+
+template<typename T>
+void templateFunctionWithSeparateSpecializationDeclarationAndDefinition(T a);
+
+template<>
+// CHECK-MESSAGES: :[[@LINE+3]]:6: warning: function template specialization 'templateFunctionWithSeparateSpecializationDeclarationAndDefinition<int>' has a primary template
+// CHECK-MESSAGES: :[[@LINE-4]]:6: note: the primary template declaration seen here
+// CHECK-MESSAGES: :[[@LINE+1]]:6: note: differing parameters are named here: ('b'), in primary template declaration: ('a')
+void templateFunctionWithSeparateSpecializationDeclarationAndDefinition(int b);
+
+template<>
+// CHECK-MESSAGES: :[[@LINE+3]]:6: warning: function template specialization 'templateFunctionWithSeparateSpecializationDeclarationAndDefinition<int>' has a primary template
+// CHECK-MESSAGES: :[[@LINE-10]]:6: note: the primary template declaration seen here
+// CHECK-MESSAGES: :[[@LINE+1]]:6: note: differing parameters are named here: ('c'), in primary template declaration: ('a')
+void templateFunctionWithSeparateSpecializationDeclarationAndDefinition(int c) { c; }
+
+//////////////////////////////////////////////////////
+
+template<typename T>
+class ClassTemplate
+{
+public:
+// CHECK-MESSAGES: :[[@LINE+4]]:10: warning: function 'ClassTemplate::functionInClassTemplateWithSeparateDeclarationAndDefinition' has a definition
+// CHECK-MESSAGES: :[[@LINE+7]]:24: note: the definition seen here
+// CHECK-MESSAGES: :[[@LINE+2]]:10: note: differing parameters are named here: ('a'), in definition: ('b')
+// CHECK-FIXES: void functionInClassTemplateWithSeparateDeclarationAndDefinition(int b);
+    void functionInClassTemplateWithSeparateDeclarationAndDefinition(int a);
+};
+
+template<typename T>
+void ClassTemplate<T>::functionInClassTemplateWithSeparateDeclarationAndDefinition(int b) { b; }
+
+//////////////////////////////////////////////////////
+
+class Class
+{
+public:
+    template<typename T>
+// CHECK-MESSAGES: :[[@LINE+4]]:8: warning: function 'Class::memberFunctionTemplateWithSeparateDeclarationAndDefinition' has a definition
+// CHECK-MESSAGES: :[[@LINE+12]]:13: note: the definition seen here
+// CHECK-MESSAGES: :[[@LINE+2]]:8: note: differing parameters are named here: ('a'), in definition: ('b')
+// CHECK-FIXES: void memberFunctionTemplateWithSeparateDeclarationAndDefinition(T b);
+  void memberFunctionTemplateWithSeparateDeclarationAndDefinition(T a);
+
+  template<typename T>
+  void memberFunctionTemplateWithSpecializations(T a) { a; }
+};
+
+//////////////////////////////////////////////////////
+
+template<typename T>
+void Class::memberFunctionTemplateWithSeparateDeclarationAndDefinition(T b) { b; }
+
+//////////////////////////////////////////////////////
+
+template<>
+// CHECK-MESSAGES: :[[@LINE+3]]:13: warning: function template specialization 'Class::memberFunctionTemplateWithSpecializations<int>' has a primary template
+// CHECK-MESSAGES: :[[@LINE-12]]:8: note: the primary template declaration seen here
+// CHECK-MESSAGES: :[[@LINE+1]]:13: note: differing parameters are named here: ('b'), in primary template declaration: ('a')
+void Class::memberFunctionTemplateWithSpecializations(int b) { b; }
+
+template<>
+// CHECK-MESSAGES: :[[@LINE+3]]:13: warning: function template specialization 'Class::memberFunctionTemplateWithSpecializations<float>' has a primary template
+// CHECK-MESSAGES: :[[@LINE-18]]:8: note: the primary template declaration seen here
+// CHECK-MESSAGES: :[[@LINE+1]]:13: note: differing parameters are named here: ('c'), in primary template declaration: ('a')
+void Class::memberFunctionTemplateWithSpecializations(float c) { c; }
+
+//////////////////////////////////////////////////////
+
+#define DECLARE_FUNCTION_WITH_PARAM_NAME(function_name, param_name) \
+  void function_name(int param_name)
+
+// CHECK-MESSAGES: :[[@LINE+1]]:34: warning: function 'macroFunction' has 1 other declaration with different parameter names [readability-inconsistent-declaration-parameter-name]
+DECLARE_FUNCTION_WITH_PARAM_NAME(macroFunction, a);
+// CHECK-MESSAGES: :[[@LINE+2]]:34: note: the 1st inconsistent declaration seen here
+// CHECK-MESSAGES: :[[@LINE+1]]:34: note: differing parameters are named here: ('b'), in the other declaration: ('a')
+DECLARE_FUNCTION_WITH_PARAM_NAME(macroFunction, b);
diff --git a/test/clang-tidy/readability-misleading-indentation.cpp b/test/clang-tidy/readability-misleading-indentation.cpp
new file mode 100644 (file)
index 0000000..59d5f40
--- /dev/null
@@ -0,0 +1,111 @@
+// RUN: %check_clang_tidy %s readability-misleading-indentation %t
+
+void foo1();
+void foo2();
+
+#define BLOCK \
+  if (cond1)  \
+    foo1();   \
+    foo2();
+
+int main()
+{
+  bool cond1 = true;
+  bool cond2 = true;
+
+  if (cond1)
+    if (cond2)
+      foo1();
+  else
+    foo2();
+  // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: different indentation for 'if' and corresponding 'else' [readability-misleading-indentation]
+
+  if (cond1) {
+    if (cond2)
+      foo1();
+  }
+  else
+    foo2();
+
+  if (cond1)
+    if (cond2)
+      foo1();
+    else
+      foo2();
+
+  if (cond2)
+    foo1();
+    foo2();
+    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: misleading indentation: statement is indented too deeply [readability-misleading-indentation]
+    // CHECK-MESSAGES: :[[@LINE-4]]:3: note: did you mean this line to be inside this 'if'
+    foo2(); // No redundant warning.
+
+  if (cond1)
+  {
+    foo1();
+  }
+    foo2();
+
+  if (cond1)
+    foo1();
+  foo2();
+
+  if (cond2)
+    if (cond1) foo1(); else foo2();
+
+  if (cond1) {
+  } else {
+  }
+
+  if (cond1) {
+  }
+  else {
+  }
+
+  if (cond1)
+  {
+  }
+  else
+  {
+  }
+
+  if (cond1)
+    {
+    }
+  else
+    {
+    }
+
+  if(cond1) {
+  }
+  else if (cond2) {
+  }
+  else {
+  }
+
+  if(cond1) {
+  }
+  else if (cond2) {
+  }
+       else {
+  }
+  // CHECK-MESSAGES: :[[@LINE-2]]:8: warning: different indentation for 'if' and corresponding 'else' [readability-misleading-indentation]
+  
+  if (cond1) {
+    if (cond1) {
+    }
+    else if (cond2) {
+    }
+    else {
+    }
+    if (cond1) {
+    } else if (cond2) {
+    } else if (!cond2) {
+    } else {
+    }
+  }
+  else if (cond2) {
+  }
+
+  BLOCK
+}
diff --git a/test/clang-tidy/readability-misplaced-array-index.cpp b/test/clang-tidy/readability-misplaced-array-index.cpp
new file mode 100644 (file)
index 0000000..43dd458
--- /dev/null
@@ -0,0 +1,34 @@
+// RUN: %check_clang_tidy %s readability-misplaced-array-index %t
+
+#define ABC  "abc"
+
+struct XY { int *X; int *Y; };
+
+void dostuff(int);
+
+void unusualSyntax(int *P1, struct XY *P2) {
+  10[P1] = 0;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: confusing array subscript expression, usually the index is inside the []
+  // CHECK-FIXES: P1[10] = 0;
+
+  10[P2->X] = 0;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: confusing array subscript expression
+  // CHECK-FIXES: P2->X[10] = 0;
+
+  dostuff(1["abc"]);
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: confusing array subscript expression
+  // CHECK-FIXES:  dostuff("abc"[1]);
+
+  dostuff(1[ABC]);
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: confusing array subscript expression
+  // CHECK-FIXES:  dostuff(ABC[1]);
+
+  dostuff(0[0 + ABC]);
+  // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: confusing array subscript expression
+  // CHECK-FIXES:  dostuff(0[0 + ABC]);
+  // No fixit. Probably the code should be ABC[0]
+}
+
+void normalSyntax(int *X) {
+  X[10] = 0;
+}
diff --git a/test/clang-tidy/readability-named-parameter.cpp b/test/clang-tidy/readability-named-parameter.cpp
new file mode 100644 (file)
index 0000000..af2c195
--- /dev/null
@@ -0,0 +1,133 @@
+// RUN: %check_clang_tidy %s readability-named-parameter %t
+
+void Method(char *) { /* */ }
+// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: all parameters should be named in a function
+// CHECK-FIXES: void Method(char * /*unused*/) { /* */ }
+void Method2(char *) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: all parameters should be named in a function
+// CHECK-FIXES: void Method2(char * /*unused*/) {}
+void Method3(char *, void *) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: all parameters should be named in a function
+// CHECK-FIXES: void Method3(char * /*unused*/, void * /*unused*/) {}
+void Method4(char *, int /*unused*/) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: all parameters should be named in a function
+// CHECK-FIXES: void Method4(char * /*unused*/, int /*unused*/) {}
+void operator delete[](void *) throw() {}
+// CHECK-MESSAGES: :[[@LINE-1]]:30: warning: all parameters should be named in a function
+// CHECK-FIXES: void operator delete[](void * /*unused*/) throw() {}
+int Method5(int) { return 0; }
+// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: all parameters should be named in a function
+// CHECK-FIXES: int Method5(int /*unused*/) { return 0; }
+void Method6(void (*)(void *)) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:21: warning: all parameters should be named in a function
+// CHECK-FIXES: void Method6(void (* /*unused*/)(void *)) {}
+template <typename T> void Method7(T) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:37: warning: all parameters should be named in a function
+// CHECK-FIXES: template <typename T> void Method7(T /*unused*/) {}
+
+// Don't warn in macros.
+#define M void MethodM(int) {}
+M
+
+void operator delete(void *x) throw() {}
+void Method7(char * /*x*/) {}
+void Method8(char *x) {}
+typedef void (*TypeM)(int x);
+void operator delete[](void *x) throw();
+void operator delete[](void * /*x*/) throw();
+
+struct X {
+  X operator++(int) {}
+  X operator--(int) {}
+
+  X(X&) = delete;
+  X &operator=(X&) = default;
+
+  const int &i;
+};
+
+void (*Func1)(void *);
+void Func2(void (*func)(void *)) {}
+template <void Func(void *)> void Func3() {}
+
+template <typename T>
+struct Y {
+  void foo(T) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: all parameters should be named in a function
+// CHECK-FIXES: void foo(T /*unused*/) {}
+};
+
+Y<int> y;
+Y<float> z;
+
+struct Base {
+  virtual void foo(bool notThisOne);
+  virtual void foo(int argname);
+};
+
+struct Derived : public Base {
+  void foo(int);
+// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: all parameters should be named in a function
+// CHECK-FIXES: void foo(int /*argname*/);
+};
+
+void FDef(int);
+// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: all parameters should be named in a function
+// CHECK-FIXES: void FDef(int /*n*/);
+void FDef(int n) {}
+
+void FDef2(int, int);
+// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: all parameters should be named in a function
+// CHECK-FIXES: void FDef2(int /*n*/, int /*unused*/);
+void FDef2(int n, int) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: all parameters should be named in a function
+// CHECK-FIXES: void FDef2(int n, int /*unused*/) {}
+
+void FNoDef(int);
+
+class Z {};
+
+Z &operator++(Z&) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: all parameters should be named in a function
+// CHECK-FIXES: Z &operator++(Z& /*unused*/) {}
+
+Z &operator++(Z&, int) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: all parameters should be named in a function
+// CHECK-FIXES: Z &operator++(Z& /*unused*/, int) {}
+
+Z &operator--(Z&) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: all parameters should be named in a function
+// CHECK-FIXES: Z &operator--(Z& /*unused*/) {}
+
+Z &operator--(Z&, int) {}
+// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: all parameters should be named in a function
+// CHECK-FIXES: Z &operator--(Z& /*unused*/, int) {}
+
+namespace testing {
+namespace internal {
+class IgnoredValue {
+ public:
+  template <typename T>
+  IgnoredValue(const T& /* ignored */) {}
+};
+}
+typedef internal::IgnoredValue Unused;
+}
+
+using ::testing::Unused;
+
+void MockFunction(Unused, int q, Unused) {
+  ++q;
+  ++q;
+  ++q;
+}
+
+namespace std {
+typedef decltype(nullptr) nullptr_t;
+}
+
+void f(std::nullptr_t) {}
+
+typedef void (F)(int);
+F f;
+void f(int x) {}
diff --git a/test/clang-tidy/readability-non-const-parameter.cpp b/test/clang-tidy/readability-non-const-parameter.cpp
new file mode 100644 (file)
index 0000000..4b1896c
--- /dev/null
@@ -0,0 +1,279 @@
+// RUN: %check_clang_tidy %s readability-non-const-parameter %t
+
+// Currently the checker only warns about pointer arguments.
+//
+// It can be defined both that the data is const and that the pointer is const,
+// the checker only checks if the data can be const-specified.
+//
+// It does not warn about pointers to records or function pointers.
+
+// Some external function where first argument is nonconst and second is const.
+char *strcpy1(char *dest, const char *src);
+unsigned my_strcpy(char *buf, const char *s);
+unsigned my_strlen(const char *buf);
+
+// CHECK-MESSAGES: :[[@LINE+1]]:29: warning: pointer parameter 'last' can be pointer to const [readability-non-const-parameter]
+void warn1(int *first, int *last) {
+  // CHECK-FIXES: {{^}}void warn1(int *first, const int *last) {{{$}}
+  *first = 0;
+  if (first < last) {
+  } // <- last can be const
+}
+
+// TODO: warning should be written here
+void warn2(char *p) {
+  char buf[10];
+  strcpy1(buf, p);
+}
+
+// CHECK-MESSAGES: :[[@LINE+1]]:19: warning: pointer parameter 'p' can be
+void assign1(int *p) {
+  // CHECK-FIXES: {{^}}void assign1(const int *p) {{{$}}
+  const int *q;
+  q = p;
+}
+
+// CHECK-MESSAGES: :[[@LINE+1]]:19: warning: pointer parameter 'p' can be
+void assign2(int *p) {
+  // CHECK-FIXES: {{^}}void assign2(const int *p) {{{$}}
+  const int *q;
+  q = p + 1;
+}
+
+void assign3(int *p) {
+  *p = 0;
+}
+
+void assign4(int *p) {
+  *p += 2;
+}
+
+void assign5(char *p) {
+  p[0] = 0;
+}
+
+void assign6(int *p) {
+  int *q;
+  q = p++;
+}
+
+void assign7(char *p) {
+  char *a, *b;
+  a = b = p;
+}
+
+void assign8(char *a, char *b) {
+  char *x;
+  x = (a ? a : b);
+}
+
+void assign9(unsigned char *str, const unsigned int i) {
+  unsigned char *p;
+  for (p = str + i; *p;) {
+  }
+}
+
+void assign10(int *buf) {
+  int i, *p;
+  for (i = 0, p = buf; i < 10; i++, p++) {
+    *p = 1;
+  }
+}
+
+// CHECK-MESSAGES: :[[@LINE+1]]:17: warning: pointer parameter 'p' can be
+void init1(int *p) {
+  // CHECK-FIXES: {{^}}void init1(const int *p) {{{$}}
+  const int *q = p;
+}
+
+// CHECK-MESSAGES: :[[@LINE+1]]:17: warning: pointer parameter 'p' can be
+void init2(int *p) {
+  // CHECK-FIXES: {{^}}void init2(const int *p) {{{$}}
+  const int *q = p + 1;
+}
+
+void init3(int *p) {
+  int *q = p;
+}
+
+void init4(float *p) {
+  int *q = (int *)p;
+}
+
+void init5(int *p) {
+  int *i = p ? p : 0;
+}
+
+void init6(int *p) {
+  int *a[] = {p, p, 0};
+}
+
+void init7(int *p, int x) {
+  for (int *q = p + x - 1; 0; q++)
+    ;
+}
+
+// CHECK-MESSAGES: :[[@LINE+1]]:18: warning: pointer parameter 'p' can be
+int return1(int *p) {
+  // CHECK-FIXES: {{^}}int return1(const int *p) {{{$}}
+  return *p;
+}
+
+// CHECK-MESSAGES: :[[@LINE+1]]:25: warning: pointer parameter 'p' can be
+const int *return2(int *p) {
+  // CHECK-FIXES: {{^}}const int *return2(const int *p) {{{$}}
+  return p;
+}
+
+// CHECK-MESSAGES: :[[@LINE+1]]:25: warning: pointer parameter 'p' can be
+const int *return3(int *p) {
+  // CHECK-FIXES: {{^}}const int *return3(const int *p) {{{$}}
+  return p + 1;
+}
+
+// CHECK-MESSAGES: :[[@LINE+1]]:27: warning: pointer parameter 'p' can be
+const char *return4(char *p) {
+  // CHECK-FIXES: {{^}}const char *return4(const char *p) {{{$}}
+  return p ? p : "";
+}
+
+char *return5(char *s) {
+  return s;
+}
+
+char *return6(char *s) {
+  return s + 1;
+}
+
+char *return7(char *a, char *b) {
+  return a ? a : b;
+}
+
+char return8(int *p) {
+  return ++(*p);
+}
+
+void dontwarn1(int *p) {
+  ++(*p);
+}
+
+void dontwarn2(int *p) {
+  (*p)++;
+}
+
+int dontwarn3(_Atomic(int) * p) {
+  return *p;
+}
+
+void callFunction1(char *p) {
+  strcpy1(p, "abc");
+}
+
+void callFunction2(char *p) {
+  strcpy1(&p[0], "abc");
+}
+
+void callFunction3(char *p) {
+  strcpy1(p + 2, "abc");
+}
+
+char *callFunction4(char *p) {
+  return strcpy1(p, "abc");
+}
+
+unsigned callFunction5(char *buf) {
+  unsigned len = my_strlen(buf);
+  return len + my_strcpy(buf, "abc");
+}
+
+void f6(int **p);
+void callFunction6(int *p) { f6(&p); }
+
+typedef union { void *v; } t;
+void f7(t obj);
+void callFunction7(int *p) {
+  f7((t){p});
+}
+
+void f8(int &x);
+void callFunction8(int *p) {
+  f8(*p);
+}
+
+// Don't warn about nonconst function pointers that can be const.
+void functionpointer(double f(double), int x) {
+  f(x);
+}
+
+// TODO: This is a false positive.
+// CHECK-MESSAGES: :[[@LINE+1]]:27: warning: pointer parameter 'p' can be
+int functionpointer2(int *p) {
+  return *p;
+}
+void use_functionpointer2() {
+  int (*fp)(int *) = functionpointer2; // <- the parameter 'p' can't be const
+}
+
+// Don't warn about nonconst record pointers that can be const.
+struct XY {
+  int *x;
+  int *y;
+};
+void recordpointer(struct XY *xy) {
+  *(xy->x) = 0;
+}
+
+class C {
+public:
+  C(int *p) : p(p) {}
+
+private:
+  int *p;
+};
+
+class C2 {
+public:
+  // CHECK-MESSAGES: :[[@LINE+1]]:11: warning: pointer parameter 'p' can be
+  C2(int *p) : p(p) {}
+  // CHECK-FIXES: {{^}}  C2(const int *p) : p(p) {}{{$}}
+
+private:
+  const int *p;
+};
+
+void tempObject(int *p) {
+  C c(p);
+}
+
+// avoid fp for const pointer array
+void constPointerArray(const char *remapped[][2]) {
+  const char *name = remapped[0][0];
+}
+
+class Warn {
+public:
+  // CHECK-MESSAGES: :[[@LINE+1]]:21: warning: pointer parameter 'p' can be
+  void doStuff(int *p) {
+    // CHECK-FIXES: {{^}}  void doStuff(const int *p) {{{$}}
+    x = *p;
+  }
+
+private:
+  int x;
+};
+
+class Base {
+public:
+  // Ensure there is no false positive for this method. It is virtual.
+  virtual void doStuff(int *p) {
+    int x = *p;
+  }
+};
+
+class Derived : public Base {
+public:
+  // Ensure there is no false positive for this method. It overrides a method.
+  void doStuff(int *p) override {
+    int x = *p;
+  }
+};
diff --git a/test/clang-tidy/readability-redundant-control-flow.cpp b/test/clang-tidy/readability-redundant-control-flow.cpp
new file mode 100644 (file)
index 0000000..0352e3a
--- /dev/null
@@ -0,0 +1,224 @@
+// RUN: %check_clang_tidy %s readability-redundant-control-flow %t
+
+void g(int i);
+void j();
+
+void f() {
+  return;
+}
+// CHECK-MESSAGES: :[[@LINE-2]]:3: warning: redundant return statement at the end of a function with a void return type [readability-redundant-control-flow]
+// CHECK-FIXES: {{^}}void f() {{{$}}
+// CHECK-FIXES-NEXT: {{^ *}$}}
+
+void g() {
+  f();
+  return;
+}
+// CHECK-MESSAGES: :[[@LINE-2]]:3: warning: redundant return statement
+// CHECK-FIXES: {{^  }}f();{{$}}
+// CHECK-FIXES-NEXT: {{^ *}$}}
+
+void g(int i) {
+  if (i < 0) {
+    return;
+  }
+  if (i < 10) {
+    f();
+  }
+}
+
+int h() {
+  return 1;
+}
+
+void j() {
+}
+
+void k() {
+  for (int i = 0; i < 10; ++i) {
+    continue;
+  }
+}
+// CHECK-MESSAGES: :[[@LINE-3]]:5: warning: redundant continue statement at the end of loop statement
+// CHECK-FIXES: {{^}}  for (int i = 0; i < 10; ++i) {{{$}}
+// CHECK-FIXES-NEXT: {{^ *}$}}
+
+void k2() {
+  int v[10] = { 0 };
+  for (auto i : v) {
+    continue;
+  }
+}
+// CHECK-MESSAGES: :[[@LINE-3]]:5: warning: redundant continue statement
+// CHECK-FIXES: {{^}}  for (auto i : v) {{{$}}
+// CHECK-FIXES-NEXT: {{^ *}$}}
+
+void m() {
+  int i = 0;
+  do {
+    ++i;
+    continue;
+  } while (i < 10);
+}
+// CHECK-MESSAGES: :[[@LINE-3]]:5: warning: redundant continue statement
+// CHECK-FIXES: {{^  do {$}}
+// CHECK-FIXES-NEXT: {{^}}    ++i;{{$}}
+// CHECK-FIXES-NEXT: {{^ *}}} while (i < 10);{{$}}
+
+void p() {
+  int i = 0;
+  while (i < 10) {
+    ++i;
+    continue;
+  }
+}
+// CHECK-MESSAGES: :[[@LINE-3]]:5: warning: redundant continue statement
+// CHECK-FIXES: {{^}}  while (i < 10) {{{$}}
+// CHECK-FIXES-NEXT: {{^}}    ++i;{{$}}
+// CHECK-FIXES-NEXT: {{^ *}$}}
+
+void im_not_dead(int i) {
+  if (i > 0) {
+    return;
+  }
+  g();
+}
+
+void im_still_not_dead(int i) {
+  for (int j = 0; j < 10; ++j) {
+    if (i < 10) {
+      continue;
+    }
+    g();
+  }
+}
+
+void im_dead(int i) {
+  if (i > 0) {
+    return;
+    g();
+  }
+  g();
+}
+
+void im_still_dead(int i) {
+  for (int j = 0; j < 10; ++j) {
+    if (i < 10) {
+      continue;
+      g();
+    }
+    g();
+  }
+}
+
+void void_return() {
+  return g();
+}
+
+void nested_return_unmolested() {
+  g();
+  {
+    g();
+    return;
+  }
+}
+
+void nested_continue_unmolested() {
+  for (int i = 0; i < 10; ++i) {
+    if (i < 5) {
+      continue;
+    }
+  }
+}
+
+#define MACRO_RETURN_UNMOLESTED(fn_)  \
+  (fn_)();                            \
+  return
+
+#define MACRO_CONTINUE_UNMOLESTED(x_) \
+  do {                                \
+    for (int i = 0; i < (x_); ++i) {  \
+      continue;                       \
+    }                                 \
+  } while (false)
+
+void macro_return() {
+  MACRO_RETURN_UNMOLESTED(g);
+}
+
+void macro_continue() {
+  MACRO_CONTINUE_UNMOLESTED(10);
+}
+
+#define MACRO_RETURN_ARG(stmt_) \
+  stmt_
+
+#define MACRO_CONTINUE_ARG(stmt_)   \
+  do {                              \
+    for (int i = 0; i < 10; ++i) {  \
+      stmt_;                        \
+    }                               \
+  } while (false)
+
+void macro_arg_return() {
+  MACRO_RETURN_ARG(return);
+}
+
+void macro_arg_continue() {
+  MACRO_CONTINUE_ARG(continue);
+}
+
+template <typename T>
+void template_return(T check) {
+  if (check < T(0)) {
+    return;
+  }
+  return;
+}
+// CHECK-MESSAGES: :[[@LINE-2]]:3: warning: redundant return statement
+// CHECK-FIXES: {{^}}  if (check < T(0)) {{{$}}
+// CHECK-FIXES-NEXT: {{^    return;$}}
+// CHECK-FIXES-NEXT: {{^ *}$}}
+// CHECK-FIXES-NEXT: {{^ *}$}}
+
+template <>
+void template_return(int check) {
+  if (check < 0) {
+    return;
+  }
+  return;
+}
+// CHECK-MESSAGES: :[[@LINE-2]]:3: warning: redundant return statement
+// CHECK-FIXES: {{^}}  if (check < 0) {{{$}}
+// CHECK-FIXES-NEXT: {{^    return;$}}
+// CHECK-FIXES-NEXT: {{^ *}$}}
+// CHECK-FIXES-NEXT: {{^ *}$}}
+
+template <typename T>
+void template_loop(T end) {
+  for (T i = 0; i < end; ++i) {
+    continue;
+  }
+}
+// CHECK-MESSAGES: :[[@LINE-3]]:5: warning: redundant continue statement
+// CHECK-FIXES: {{^}}  for (T i = 0; i < end; ++i) {{{$}}
+// CHECK-FIXES-NEXT: {{^ *}$}}
+
+template <>
+void template_loop(int end) {
+  for (int i = 0; i < end; ++i) {
+    continue;
+  }
+}
+// CHECK-MESSAGES: :[[@LINE-3]]:5: warning: redundant continue statement
+// CHECK-FIXES: {{^}}  for (int i = 0; i < end; ++i) {{{$}}
+// CHECK-FIXES-NEXT: {{^ *}$}}
+
+void call_templates() {
+  template_return(10);
+  template_return(10.0f);
+  template_return(10.0);
+  template_loop(10);
+  template_loop(10L);
+  template_loop(10U);
+}
diff --git a/test/clang-tidy/readability-redundant-declaration.cpp b/test/clang-tidy/readability-redundant-declaration.cpp
new file mode 100644 (file)
index 0000000..e1b8023
--- /dev/null
@@ -0,0 +1,44 @@
+// RUN: %check_clang_tidy %s readability-redundant-declaration %t
+
+extern int Xyz;
+extern int Xyz; // Xyz
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: redundant 'Xyz' declaration [readability-redundant-declaration]
+// CHECK-FIXES: {{^}}// Xyz{{$}}
+int Xyz = 123;
+
+extern int A;
+extern int A, B;
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: redundant 'A' declaration
+// CHECK-FIXES: {{^}}extern int A, B;{{$}}
+
+extern int Buf[10];
+extern int Buf[10]; // Buf[10]
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: redundant 'Buf' declaration
+// CHECK-FIXES: {{^}}// Buf[10]{{$}}
+
+static int f();
+static int f(); // f
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: redundant 'f' declaration
+// CHECK-FIXES: {{^}}// f{{$}}
+static int f() {}
+
+// Original check crashed for the code below.
+namespace std {
+typedef decltype(sizeof(0)) size_t;
+}
+void *operator new(std::size_t) __attribute__((__externally_visible__));
+void *operator new[](std::size_t) __attribute__((__externally_visible__));
+
+// Don't warn about static member definition.
+struct C {
+  static int I;
+};
+int C::I;
+
+template <class T>
+struct C2 {
+  C2();
+};
+
+template <class T>
+C2<T>::C2() = default;
diff --git a/test/clang-tidy/readability-redundant-function-ptr-dereference.cpp b/test/clang-tidy/readability-redundant-function-ptr-dereference.cpp
new file mode 100644 (file)
index 0000000..4e324ab
--- /dev/null
@@ -0,0 +1,24 @@
+// RUN: %check_clang_tidy %s readability-redundant-function-ptr-dereference %t
+
+void f(int i);
+
+void positive() {
+  void (*p)(int) = f;
+
+  (**p)(1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:4: warning: redundant repeated dereference of function pointer [readability-redundant-function-ptr-dereference]
+  // CHECK-FIXES: (*p)(1);
+  (*****p)(2);
+  // CHECK-MESSAGES: :[[@LINE-1]]:4: warning: redundant repeated
+  // CHECK-MESSAGES: :[[@LINE-2]]:5: warning: redundant repeated
+  // CHECK-MESSAGES: :[[@LINE-3]]:6: warning: redundant repeated
+  // CHECK-MESSAGES: :[[@LINE-4]]:7: warning: redundant repeated
+  // CHECK-FIXES: (*p)(2);
+}
+
+void negative() {
+  void (*q)(int) = &f;
+
+  q(1);
+  (*q)(2);
+}
diff --git a/test/clang-tidy/readability-redundant-member-init.cpp b/test/clang-tidy/readability-redundant-member-init.cpp
new file mode 100644 (file)
index 0000000..f15cb1b
--- /dev/null
@@ -0,0 +1,199 @@
+// RUN: %check_clang_tidy %s readability-redundant-member-init %t
+
+struct S {
+  S() = default;
+  S(int i) : i(i) {}
+  int i = 1;
+};
+
+struct T {
+  T(int i = 1) : i(i) {}
+  int i;
+};
+
+struct U {
+  int i;
+};
+
+union V {
+  int i;
+  double f;
+};
+
+// Initializer calls default constructor
+struct F1 {
+  F1() : f() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: initializer for member 'f' is redundant
+  // CHECK-FIXES: F1()  {}
+  S f;
+};
+
+// Initializer calls default constructor with default argument
+struct F2 {
+  F2() : f() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: initializer for member 'f' is redundant
+  // CHECK-FIXES: F2()  {}
+  T f;
+};
+
+// Multiple redundant initializers for same constructor
+struct F3 {
+  F3() : f(), g(1), h() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: initializer for member 'f' is redundant
+  // CHECK-MESSAGES: :[[@LINE-2]]:21: warning: initializer for member 'h' is redundant
+  // CHECK-FIXES: F3() :  g(1) {}
+  S f, g, h;
+};
+
+// Templated class independent type
+template <class V>
+struct F4 {
+  F4() : f() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: initializer for member 'f' is redundant
+  // CHECK-FIXES: F4()  {}
+  S f;
+};
+F4<int> f4i;
+F4<S> f4s;
+
+// Base class
+struct F5 : S {
+  F5() : S() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: initializer for base class 'S' is redundant
+  // CHECK-FIXES: F5()  {}
+};
+
+// Constructor call requires cleanup
+struct Cleanup {
+  ~Cleanup() {}
+};
+
+struct UsesCleanup {
+  UsesCleanup(const Cleanup &c = Cleanup()) {}
+};
+
+struct F6 {
+  F6() : uc() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: initializer for member 'uc' is redundant
+  // CHECK-FIXES: F6()  {}
+  UsesCleanup uc;
+};
+
+// Multiple inheritance
+struct F7 : S, T {
+  F7() : S(), T() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: initializer for base class 'S' is redundant
+  // CHECK-MESSAGES: :[[@LINE-2]]:15: warning: initializer for base class 'T' is redundant
+  // CHECK-FIXES: F7()  {}
+};
+
+namespace Foo {
+inline namespace Bar {
+template <int N>
+struct Template {
+  Template() = default;
+  int i = N;
+};
+}
+}
+
+enum { N_THINGS = 5 };
+
+struct F8 : Foo::Template<N_THINGS> {
+  F8() : Template() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: initializer for base class 'Foo::Template<N_THINGS>' is redundant
+  // CHECK-FIXES: F8()  {}
+};
+
+// Initializer not written
+struct NF1 {
+  NF1() {}
+  S f;
+};
+
+// Initializer doesn't call default constructor
+struct NF2 {
+  NF2() : f(1) {}
+  S f;
+};
+
+// Initializer calls default constructor without using default argument
+struct NF3 {
+  NF3() : f(1) {}
+  T f;
+};
+
+// Initializer calls default constructor without using default argument
+struct NF4 {
+  NF4() : f(2) {}
+  T f;
+};
+
+// Initializer is zero-initialization
+struct NF5 {
+  NF5() : i() {}
+  int i;
+};
+
+// Initializer is direct-initialization
+struct NF6 {
+  NF6() : i(1) {}
+  int i;
+};
+
+// Initializer is aggregate initialization of struct
+struct NF7 {
+  NF7() : f{} {}
+  U f;
+};
+
+// Initializer is zero-initialization of struct
+struct NF7b {
+  NF7b() : f() {}
+  U f;
+};
+
+// Initializer is aggregate initialization of array
+struct NF8 {
+  NF8() : f{} {}
+  int f[2];
+};
+
+struct NF9 {
+  NF9() : f{} {}
+  S f[2];
+};
+
+// Initializing member of union
+union NF10 {
+  NF10() : s() {}
+  int i;
+  S s;
+};
+
+// Templated class dependent type
+template <class V>
+struct NF11 {
+  NF11() : f() {}
+  V f;
+};
+NF11<int> nf11i;
+NF11<S> nf11s;
+
+// Delegating constructor
+class NF12 {
+  NF12() = default;
+  NF12(int) : NF12() {}
+};
+
+// Const member
+struct NF13 {
+  NF13() : f() {}
+  const S f;
+};
+
+// Union member
+struct NF14 {
+  NF14() : f() {}
+  V f;
+};
diff --git a/test/clang-tidy/readability-redundant-smartptr-get.cpp b/test/clang-tidy/readability-redundant-smartptr-get.cpp
new file mode 100644 (file)
index 0000000..985efc5
--- /dev/null
@@ -0,0 +1,172 @@
+// RUN: %check_clang_tidy %s readability-redundant-smartptr-get %t
+
+#define NULL __null
+
+namespace std {
+
+template <typename T>
+struct unique_ptr {
+  T& operator*() const;
+  T* operator->() const;
+  T* get() const;
+};
+
+template <typename T>
+struct shared_ptr {
+  T& operator*() const;
+  T* operator->() const;
+  T* get() const;
+};
+
+}  // namespace std
+
+struct Bar {
+  void Do();
+  void ConstDo() const;
+};
+struct BarPtr {
+  Bar* operator->();
+  Bar& operator*();
+  Bar* get();
+};
+struct int_ptr {
+  int* get();
+  int* operator->();
+  int& operator*();
+};
+
+struct Fail1 {
+  Bar* get();
+};
+struct Fail2 {
+  Bar* get();
+  int* operator->();
+  int& operator*();
+};
+
+struct PointerWithOverloadedGet {
+  int* get();
+  template <typename T>
+  T* get();
+  int* operator->();
+  int& operator*();
+};
+
+void Positive() {
+  BarPtr u;
+  // CHECK-FIXES: BarPtr u;
+  BarPtr().get()->Do();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: redundant get() call on smart pointer [readability-redundant-smartptr-get]
+  // CHECK-MESSAGES: BarPtr().get()->Do();
+  // CHECK-FIXES: BarPtr()->Do();
+
+  u.get()->ConstDo();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: redundant get() call
+  // CHECK-MESSAGES: u.get()->ConstDo();
+  // CHECK-FIXES: u->ConstDo();
+
+  Bar& b = *BarPtr().get();
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: redundant get() call
+  // CHECK-MESSAGES: Bar& b = *BarPtr().get();
+  // CHECK-FIXES: Bar& b = *BarPtr();
+
+  Bar& b2 = *std::unique_ptr<Bar>().get();
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: redundant get() call
+  // CHECK-MESSAGES: Bar& b2 = *std::unique_ptr<Bar>().get();
+  // CHECK-FIXES: Bar& b2 = *std::unique_ptr<Bar>();
+
+  (*BarPtr().get()).ConstDo();
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: redundant get() call
+  // CHECK-MESSAGES: (*BarPtr().get()).ConstDo();
+  // CHECK-FIXES: (*BarPtr()).ConstDo();
+
+  (*std::unique_ptr<Bar>().get()).ConstDo();
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: redundant get() call
+  // CHECK-MESSAGES: (*std::unique_ptr<Bar>().get()).ConstDo();
+  // CHECK-FIXES: (*std::unique_ptr<Bar>()).ConstDo();
+
+  std::unique_ptr<Bar>* up;
+  (*up->get()).Do();
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: redundant get() call
+  // CHECK-MESSAGES: (*up->get()).Do();
+  // CHECK-FIXES: (**up).Do();
+
+  int_ptr ip;
+  int i = *ip.get();
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: redundant get() call
+  // CHECK-MESSAGES: int i = *ip.get();
+  // CHECK-FIXES: int i = *ip;
+
+  std::unique_ptr<int> uu;
+  std::shared_ptr<double> *ss;
+  bool bb = uu.get() == nullptr;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: redundant get() call
+  // CHECK-MESSAGES: uu.get() == nullptr;
+  // CHECK-FIXES: bool bb = uu == nullptr;
+
+  bb = nullptr != ss->get();
+  // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: redundant get() call
+  // CHECK-MESSAGES: nullptr != ss->get();
+  // CHECK-FIXES: bb = nullptr != *ss;
+
+  i = *PointerWithOverloadedGet().get();
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: redundant get() call
+  // CHECK-MESSAGES: i = *PointerWithOverloadedGet().get();
+  // CHECK-FIXES: i = *PointerWithOverloadedGet();
+
+  bb = std::unique_ptr<int>().get() == NULL;
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: redundant get() call
+  // CHECK-MESSAGES: bb = std::unique_ptr<int>().get() == NULL;
+  // CHECK-FIXES: bb = std::unique_ptr<int>() == NULL;
+  bb = ss->get() == NULL;
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: redundant get() call
+  // CHECK-MESSAGES: bb = ss->get() == NULL;
+  // CHECK-FIXES: bb = *ss == NULL;
+
+  std::unique_ptr<int> x, y;
+  if (x.get() == nullptr);
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: redundant get() call
+  // CHECK-MESSAGES: if (x.get() == nullptr);
+  // CHECK-FIXES: if (x == nullptr);
+  if (nullptr == y.get());
+  // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: redundant get() call
+  // CHECK-MESSAGES: if (nullptr == y.get());
+  // CHECK-FIXES: if (nullptr == y);
+  if (x.get() == NULL);
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: redundant get() call
+  // CHECK-MESSAGES: if (x.get() == NULL);
+  // CHECK-FIXES: if (x == NULL);
+  if (NULL == x.get());
+  // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: redundant get() call
+  // CHECK-MESSAGES: if (NULL == x.get());
+  // CHECK-FIXES: if (NULL == x);
+  if (x.get());
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: redundant get() call
+  // CHECK-MESSAGES: if (x.get());
+  // CHECK-FIXES: if (x);
+}
+
+void Negative() {
+  struct NegPtr {
+    int* get();
+    int* operator->() {
+      return &*this->get();
+    }
+    int& operator*() {
+      return *get();
+    }
+  };
+
+  long l = *PointerWithOverloadedGet().get<long>();
+
+  std::unique_ptr<Bar>* u;
+  u->get()->Do();
+
+  Fail1().get()->Do();
+  Fail2().get()->Do();
+  const Bar& b = *Fail1().get();
+  (*Fail2().get()).Do();
+
+  int_ptr ip;
+  bool bb = ip.get() == nullptr;
+}
diff --git a/test/clang-tidy/readability-redundant-string-cstr-msvc.cpp b/test/clang-tidy/readability-redundant-string-cstr-msvc.cpp
new file mode 100644 (file)
index 0000000..a6b6b20
--- /dev/null
@@ -0,0 +1,47 @@
+// RUN: %check_clang_tidy %s readability-redundant-string-cstr %t
+
+namespace std {
+template <typename T>
+class allocator {};
+template <typename T>
+class char_traits {};
+template <typename C, typename T, typename A>
+struct basic_string {
+  basic_string();
+  // MSVC headers define two constructors instead of using optional arguments.
+  basic_string(const C *p);
+  basic_string(const C *p, const A &a);
+  const C *c_str() const;
+  const C *data() const;
+};
+typedef basic_string<char, std::char_traits<char>, std::allocator<char>> string;
+}
+namespace llvm {
+struct StringRef {
+  StringRef(const char *p);
+  StringRef(const std::string &);
+};
+}
+
+void f1(const std::string &s) {
+  f1(s.c_str());
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: redundant call to 'c_str' [readability-redundant-string-cstr]
+  // CHECK-FIXES: {{^  }}f1(s);{{$}}
+  f1(s.data());
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: redundant call to 'data' [readability-redundant-string-cstr]
+  // CHECK-FIXES: {{^  }}f1(s);{{$}}
+}
+void f2(const llvm::StringRef r) {
+  std::string s;
+  f2(s.c_str());
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: redundant call {{.*}}
+  // CHECK-FIXES: {{^  }}std::string s;{{$}}
+  // CHECK-FIXES-NEXT: {{^  }}f2(s);{{$}}
+}
+void f3(const llvm::StringRef &r) {
+  std::string s;
+  f3(s.c_str());
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: redundant call {{.*}}
+  // CHECK-FIXES: {{^  }}std::string s;{{$}}
+  // CHECK-FIXES-NEXT: {{^  }}f3(s);{{$}}
+}
diff --git a/test/clang-tidy/readability-redundant-string-cstr.cpp b/test/clang-tidy/readability-redundant-string-cstr.cpp
new file mode 100644 (file)
index 0000000..976a54f
--- /dev/null
@@ -0,0 +1,207 @@
+// RUN: %check_clang_tidy %s readability-redundant-string-cstr %t -- -- -std=c++11
+
+typedef unsigned __INT16_TYPE__ char16;
+typedef unsigned __INT32_TYPE__ char32;
+typedef __SIZE_TYPE__ size;
+
+namespace std {
+template <typename T>
+class allocator {};
+template <typename T>
+class char_traits {};
+template <typename C, typename T, typename A>
+struct basic_string {
+  typedef basic_string<C, T, A> _Type;
+  basic_string();
+  basic_string(const C *p, const A &a = A());
+
+  const C *c_str() const;
+  const C *data() const;
+
+  _Type& append(const C *s);
+  _Type& append(const C *s, size n);
+  _Type& assign(const C *s);
+  _Type& assign(const C *s, size n);
+
+  int compare(const _Type&) const;
+  int compare(const C* s) const;
+  int compare(size pos, size len, const _Type&) const;
+  int compare(size pos, size len, const C* s) const;
+
+  size find(const _Type& str, size pos = 0) const;
+  size find(const C* s, size pos = 0) const;
+  size find(const C* s, size pos, size n) const;
+
+  _Type& insert(size pos, const _Type& str);
+  _Type& insert(size pos, const C* s);
+  _Type& insert(size pos, const C* s, size n);
+
+  _Type& operator+=(const _Type& str);
+  _Type& operator+=(const C* s);
+  _Type& operator=(const _Type& str);
+  _Type& operator=(const C* s);
+};
+
+typedef basic_string<char, std::char_traits<char>, std::allocator<char>> string;
+typedef basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t>> wstring;
+typedef basic_string<char16, std::char_traits<char16>, std::allocator<char16>> u16string;
+typedef basic_string<char32, std::char_traits<char32>, std::allocator<char32>> u32string;
+}
+
+std::string operator+(const std::string&, const std::string&);
+std::string operator+(const std::string&, const char*);
+std::string operator+(const char*, const std::string&);
+
+bool operator==(const std::string&, const std::string&);
+bool operator==(const std::string&, const char*);
+bool operator==(const char*, const std::string&);
+
+namespace llvm {
+struct StringRef {
+  StringRef(const char *p);
+  StringRef(const std::string &);
+};
+}
+
+// Tests for std::string.
+
+void f1(const std::string &s) {
+  f1(s.c_str());
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: redundant call to 'c_str' [readability-redundant-string-cstr]
+  // CHECK-FIXES: {{^  }}f1(s);{{$}}
+  f1(s.data());
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: redundant call to 'data' [readability-redundant-string-cstr]
+  // CHECK-FIXES: {{^  }}f1(s);{{$}}
+}
+void f2(const llvm::StringRef r) {
+  std::string s;
+  f2(s.c_str());
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: redundant call {{.*}}
+  // CHECK-FIXES: {{^  }}std::string s;{{$}}
+  // CHECK-FIXES-NEXT: {{^  }}f2(s);{{$}}
+}
+void f3(const llvm::StringRef &r) {
+  std::string s;
+  f3(s.c_str());
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: redundant call {{.*}}
+  // CHECK-FIXES: {{^  }}std::string s;{{$}}
+  // CHECK-FIXES-NEXT: {{^  }}f3(s);{{$}}
+}
+void f4(const std::string &s) {
+  const std::string* ptr = &s;
+  f1(ptr->c_str());
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: redundant call to 'c_str' [readability-redundant-string-cstr]
+  // CHECK-FIXES: {{^  }}f1(*ptr);{{$}}
+}
+void f5(const std::string &s) {
+  std::string tmp;
+  tmp.append(s.c_str());
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: redundant call {{.*}}
+  // CHECK-FIXES: {{^  }}tmp.append(s);{{$}}
+  tmp.assign(s.c_str());
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: redundant call {{.*}}
+  // CHECK-FIXES: {{^  }}tmp.assign(s);{{$}}
+
+  if (tmp.compare(s.c_str()) == 0) return;
+  // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: redundant call {{.*}}
+  // CHECK-FIXES: {{^  }}if (tmp.compare(s) == 0) return;{{$}}
+
+  if (tmp.compare(1, 2, s.c_str()) == 0) return;
+  // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: redundant call {{.*}}
+  // CHECK-FIXES: {{^  }}if (tmp.compare(1, 2, s) == 0) return;{{$}}
+
+  if (tmp.find(s.c_str()) == 0) return;
+  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: redundant call {{.*}}
+  // CHECK-FIXES: {{^  }}if (tmp.find(s) == 0) return;{{$}}
+
+  if (tmp.find(s.c_str(), 2) == 0) return;
+  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: redundant call {{.*}}
+  // CHECK-FIXES: {{^  }}if (tmp.find(s, 2) == 0) return;{{$}}
+
+  if (tmp.find(s.c_str(), 2) == 0) return;
+  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: redundant call {{.*}}
+  // CHECK-FIXES: {{^  }}if (tmp.find(s, 2) == 0) return;{{$}}
+
+  tmp.insert(1, s.c_str());
+  // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: redundant call {{.*}}
+  // CHECK-FIXES: {{^  }}tmp.insert(1, s);{{$}}
+
+  tmp = s.c_str();
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: redundant call {{.*}}
+  // CHECK-FIXES: {{^  }}tmp = s;{{$}}
+
+  tmp += s.c_str();
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: redundant call {{.*}}
+  // CHECK-FIXES: {{^  }}tmp += s;{{$}}
+
+  if (tmp == s.c_str()) return;
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: redundant call {{.*}}
+  // CHECK-FIXES: {{^  }}if (tmp == s) return;{{$}}
+
+  tmp = s + s.c_str();
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: redundant call {{.*}}
+  // CHECK-FIXES: {{^  }}tmp = s + s;{{$}}
+
+  tmp = s.c_str() + s;
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: redundant call {{.*}}
+  // CHECK-FIXES: {{^  }}tmp = s + s;{{$}}
+}
+void f6(const std::string &s) {
+  std::string tmp;
+  tmp.append(s.c_str(), 2);
+  tmp.assign(s.c_str(), 2);
+
+  if (tmp.compare(s) == 0) return;
+  if (tmp.compare(1, 2, s) == 0) return;
+
+  tmp = s;
+  tmp += s;
+
+  if (tmp == s)
+    return;
+
+  tmp = s + s;
+
+  if (tmp.find(s.c_str(), 2, 4) == 0) return;
+
+  tmp.insert(1, s);
+  tmp.insert(1, s.c_str(), 2);
+}
+
+// Tests for std::wstring.
+
+void g1(const std::wstring &s) {
+  g1(s.c_str());
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: redundant call to 'c_str' [readability-redundant-string-cstr]
+  // CHECK-FIXES: {{^  }}g1(s);{{$}}
+}
+
+// Tests for std::u16string.
+
+void h1(const std::u16string &s) {
+  h1(s.c_str());
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: redundant call to 'c_str' [readability-redundant-string-cstr]
+  // CHECK-FIXES: {{^  }}h1(s);{{$}}
+}
+
+// Tests for std::u32string.
+
+void k1(const std::u32string &s) {
+  k1(s.c_str());
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: redundant call to 'c_str' [readability-redundant-string-cstr]
+  // CHECK-FIXES: {{^  }}k1(s);{{$}}
+}
+
+// Tests on similar classes that aren't good candidates for this checker.
+
+struct NotAString {
+  NotAString();
+  NotAString(const NotAString&);
+  const char *c_str() const;
+};
+
+void dummy(const char*) {}
+
+void invalid(const NotAString &s) {
+  dummy(s.c_str());
+}
diff --git a/test/clang-tidy/readability-redundant-string-init-msvc.cpp b/test/clang-tidy/readability-redundant-string-init-msvc.cpp
new file mode 100644 (file)
index 0000000..0299519
--- /dev/null
@@ -0,0 +1,61 @@
+// RUN: %check_clang_tidy %s readability-redundant-string-init %t
+
+namespace std {
+template <typename T>
+class allocator {};
+template <typename T>
+class char_traits {};
+template <typename C, typename T = std::char_traits<C>, typename A = std::allocator<C>>
+struct basic_string {
+  basic_string();
+  basic_string(const basic_string&);
+  // MSVC headers define two constructors instead of using optional arguments.
+  basic_string(const C *);
+  basic_string(const C *, const A &);
+  ~basic_string();
+};
+typedef basic_string<char> string;
+typedef basic_string<wchar_t> wstring;
+}
+
+void f() {
+  std::string a = "";
+  // CHECK-MESSAGES: [[@LINE-1]]:15: warning: redundant string initialization [readability-redundant-string-init]
+  // CHECK-FIXES: std::string a;
+  std::string b("");
+  // CHECK-MESSAGES: [[@LINE-1]]:15: warning: redundant string initialization
+  // CHECK-FIXES: std::string b;
+  std::string c = R"()";
+  // CHECK-MESSAGES: [[@LINE-1]]:15: warning: redundant string initialization
+  // CHECK-FIXES: std::string c;
+  std::string d(R"()");
+  // CHECK-MESSAGES: [[@LINE-1]]:15: warning: redundant string initialization
+  // CHECK-FIXES: std::string d;
+
+  std::string u = "u";
+  std::string w("w");
+  std::string x = R"(x)";
+  std::string y(R"(y)");
+  std::string z;
+}
+
+void g() {
+  std::wstring a = L"";
+  // CHECK-MESSAGES: [[@LINE-1]]:16: warning: redundant string initialization
+  // CHECK-FIXES: std::wstring a;
+  std::wstring b(L"");
+  // CHECK-MESSAGES: [[@LINE-1]]:16: warning: redundant string initialization
+  // CHECK-FIXES: std::wstring b;
+  std::wstring c = LR"()";
+  // CHECK-MESSAGES: [[@LINE-1]]:16: warning: redundant string initialization
+  // CHECK-FIXES: std::wstring c;
+  std::wstring d(LR"()");
+  // CHECK-MESSAGES: [[@LINE-1]]:16: warning: redundant string initialization
+  // CHECK-FIXES: std::wstring d;
+
+  std::wstring u = L"u";
+  std::wstring w(L"w");
+  std::wstring x = LR"(x)";
+  std::wstring y(LR"(y)");
+  std::wstring z;
+}
diff --git a/test/clang-tidy/readability-redundant-string-init.cpp b/test/clang-tidy/readability-redundant-string-init.cpp
new file mode 100644 (file)
index 0000000..4455ad4
--- /dev/null
@@ -0,0 +1,140 @@
+// RUN: %check_clang_tidy %s readability-redundant-string-init %t
+
+namespace std {
+template <typename T>
+class allocator {};
+template <typename T>
+class char_traits {};
+template <typename C, typename T = std::char_traits<C>, typename A = std::allocator<C>>
+struct basic_string {
+  basic_string();
+  basic_string(const basic_string&);
+  basic_string(const C *, const A &a = A());
+  ~basic_string();
+};
+typedef basic_string<char> string;
+typedef basic_string<wchar_t> wstring;
+}
+
+void f() {
+  std::string a = "";
+  // CHECK-MESSAGES: [[@LINE-1]]:15: warning: redundant string initialization [readability-redundant-string-init]
+  // CHECK-FIXES: std::string a;
+  std::string b("");
+  // CHECK-MESSAGES: [[@LINE-1]]:15: warning: redundant string initialization
+  // CHECK-FIXES: std::string b;
+  std::string c = R"()";
+  // CHECK-MESSAGES: [[@LINE-1]]:15: warning: redundant string initialization
+  // CHECK-FIXES: std::string c;
+  std::string d(R"()");
+  // CHECK-MESSAGES: [[@LINE-1]]:15: warning: redundant string initialization
+  // CHECK-FIXES: std::string d;
+
+  std::string u = "u";
+  std::string w("w");
+  std::string x = R"(x)";
+  std::string y(R"(y)");
+  std::string z;
+}
+
+void g() {
+  std::wstring a = L"";
+  // CHECK-MESSAGES: [[@LINE-1]]:16: warning: redundant string initialization
+  // CHECK-FIXES: std::wstring a;
+  std::wstring b(L"");
+  // CHECK-MESSAGES: [[@LINE-1]]:16: warning: redundant string initialization
+  // CHECK-FIXES: std::wstring b;
+  std::wstring c = LR"()";
+  // CHECK-MESSAGES: [[@LINE-1]]:16: warning: redundant string initialization
+  // CHECK-FIXES: std::wstring c;
+  std::wstring d(LR"()");
+  // CHECK-MESSAGES: [[@LINE-1]]:16: warning: redundant string initialization
+  // CHECK-FIXES: std::wstring d;
+
+  std::wstring u = L"u";
+  std::wstring w(L"w");
+  std::wstring x = LR"(x)";
+  std::wstring y(LR"(y)");
+  std::wstring z;
+}
+
+template <typename T>
+void templ() {
+  std::string s = "";
+  // CHECK-MESSAGES: [[@LINE-1]]:15: warning: redundant string initialization
+  // CHECK-FIXES: std::string s;
+}
+
+#define M(x) x
+#define N { std::string s = ""; }
+// CHECK-FIXES: #define N { std::string s = ""; }
+
+void h() {
+  templ<int>();
+  templ<double>();
+
+  M({ std::string s = ""; })
+  // CHECK-MESSAGES: [[@LINE-1]]:19: warning: redundant string initialization
+  // CHECK-FIXES: M({ std::string s; })
+
+  N
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: redundant string initialization
+  // CHECK-FIXES: N
+  N
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: redundant string initialization
+  // CHECK-FIXES: N
+}
+
+typedef std::string MyString;
+#define STRING MyString
+#define DECL_STRING(name, val) STRING name = val
+
+void i() {
+  MyString a = "";
+  // CHECK-MESSAGES: [[@LINE-1]]:12: warning: redundant string initialization
+  // CHECK-FIXES: MyString a;
+  STRING b = "";
+  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: redundant string initialization
+  // CHECK-FIXES: STRING b;
+  MyString c = "" "" "";
+  // CHECK-MESSAGES: [[@LINE-1]]:12: warning: redundant string initialization
+  // CHECK-FIXES: MyString c;
+  STRING d = "" "" "";
+  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: redundant string initialization
+  // CHECK-FIXES: STRING d;
+  DECL_STRING(e, "");
+  // CHECK-MESSAGES: [[@LINE-1]]:15: warning: redundant string initialization
+
+  MyString f = "u";
+  STRING g = "u";
+  DECL_STRING(h, "u");
+}
+
+#define EMPTY_STR ""
+void j() {
+  std::string a(EMPTY_STR);
+  // CHECK-MESSAGES: [[@LINE-1]]:15: warning: redundant string initialization
+  // CHECK-FIXES: std::string a;
+  std::string b = (EMPTY_STR);
+  // CHECK-MESSAGES: [[@LINE-1]]:15: warning: redundant string initialization
+  // CHECK-FIXES: std::string b;
+
+  std::string c(EMPTY_STR "u" EMPTY_STR);
+}
+
+void k() {
+  std::string a = "", b = "", c = "";
+  // CHECK-MESSAGES: [[@LINE-1]]:15: warning: redundant string initialization
+  // CHECK-MESSAGES: [[@LINE-2]]:23: warning: redundant string initialization
+  // CHECK-MESSAGES: [[@LINE-3]]:31: warning: redundant string initialization
+  // CHECK-FIXES: std::string a, b, c;
+
+  std::string d = "u", e = "u", f = "u";
+}
+
+// These cases should not generate warnings.
+extern void Param1(std::string param = "");
+extern void Param2(const std::string& param = "");
+void Param3(std::string param = "") {}
+void Param4(STRING param = "") {}
+
diff --git a/test/clang-tidy/readability-simplify-bool-expr-chained-conditional-assignment.cpp b/test/clang-tidy/readability-simplify-bool-expr-chained-conditional-assignment.cpp
new file mode 100644 (file)
index 0000000..65b86c3
--- /dev/null
@@ -0,0 +1,34 @@
+// RUN: %check_clang_tidy %s readability-simplify-boolean-expr %t -- -config="{CheckOptions: [{key: "readability-simplify-boolean-expr.ChainedConditionalAssignment", value: 1}]}" --
+
+void chained_conditional_compound_assignment(int i) {
+  bool b;
+  if (i < 0) {
+    b = true;
+  } else if (i < 10) {
+    b = false;
+  } else if (i > 20) {
+    b = true;
+  } else {
+    b = false;
+  }
+  // CHECK-MESSAGES: :[[@LINE-4]]:9: warning: redundant boolean literal in conditional assignment [readability-simplify-boolean-expr]
+  // CHECK-FIXES:      {{^}}  } else if (i < 10) {{{$}}
+  // CHECK-FIXES-NEXT: {{^}}    b = false;{{$}}
+  // CHECK-FIXES-NEXT: {{^}}  } else b = i > 20;{{$}}
+}
+
+void chained_conditional_assignment(int i) {
+  bool b;
+  if (i < 0)
+    b = true;
+  else if (i < 10)
+    b = false;
+  else if (i > 20)
+    b = true;
+  else
+    b = false;
+  // CHECK-MESSAGES: :[[@LINE-3]]:9: warning: {{.*}} in conditional assignment
+  // CHECK-FIXES:      {{^}}  else if (i < 10)
+  // CHECK-FIXES-NEXT: {{^}}    b = false;
+  // CHECK-FIXES-NEXT: {{^}}  else b = i > 20;
+}
diff --git a/test/clang-tidy/readability-simplify-bool-expr-chained-conditional-return.cpp b/test/clang-tidy/readability-simplify-bool-expr-chained-conditional-return.cpp
new file mode 100644 (file)
index 0000000..fb8759c
--- /dev/null
@@ -0,0 +1,94 @@
+// RUN: %check_clang_tidy %s readability-simplify-boolean-expr %t -- -config="{CheckOptions: [{key: "readability-simplify-boolean-expr.ChainedConditionalReturn", value: 1}]}" --
+
+bool chained_conditional_compound_return(int i) {
+  if (i < 0) {
+    return true;
+  } else if (i < 10) {
+    return false;
+  } else if (i > 20) {
+    return true;
+  } else {
+    return false;
+  }
+  // CHECK-MESSAGES: :[[@LINE-4]]:12: warning: redundant boolean literal in conditional return statement [readability-simplify-boolean-expr]
+  // CHECK-FIXES:      {{^}}  } else if (i < 10) {{{$}}
+  // CHECK-FIXES-NEXT: {{^}}    return false;{{$}}
+  // CHECK-FIXES-NEXT: {{^}}  } else return i > 20;{{$}}
+}
+
+bool chained_conditional_return(int i) {
+  if (i < 0)
+    return true;
+  else if (i < 10)
+    return false;
+  else if (i > 20)
+    return true;
+  else
+    return false;
+  // CHECK-MESSAGES: :[[@LINE-3]]:12: warning: {{.*}} in conditional return statement
+  // CHECK-FIXES:      {{^}}  else if (i < 10)
+  // CHECK-FIXES-NEXT: {{^}}    return false;
+  // CHECK-FIXES-NEXT: {{^}}  else return i > 20;
+}
+
+bool chained_simple_if_return(int i) {
+  if (i < 5)
+    return true;
+  if (i > 10)
+    return true;
+  return false;
+}
+// CHECK-MESSAGES: :[[@LINE-3]]:12: warning: {{.*}} in conditional return
+// CHECK-FIXES: {{^}}bool chained_simple_if_return(int i) {{{$}}
+// CHECK-FIXES: {{^}}  if (i < 5){{$}}
+// CHECK-FIXES: {{^    return true;$}}
+// CHECK-FIXES: {{^  return i > 10;$}}
+// CHECK-FIXES: {{^}$}}
+
+bool chained_simple_if_return_negated(int i) {
+  if (i < 5)
+    return false;
+  if (i > 10)
+    return false;
+  return true;
+}
+// CHECK-MESSAGES: :[[@LINE-3]]:12: warning: {{.*}} in conditional return
+// CHECK-FIXES: {{^}}bool chained_simple_if_return_negated(int i) {{{$}}
+// CHECK-FIXES: {{^}}  if (i < 5){{$}}
+// CHECK-FIXES: {{^    return false;$}}
+// CHECK-FIXES: {{^  return i <= 10;$}}
+// CHECK-FIXES: {{^}$}}
+
+bool complex_chained_if_return_return(int i) {
+  if (i < 5) {
+    return true;
+  }
+  if (i > 10) {
+    return true;
+  }
+  return false;
+}
+// CHECK-MESSAGES: :[[@LINE-4]]:12: warning: {{.*}} in conditional return
+// CHECK-FIXES: {{^}}bool complex_chained_if_return_return(int i) {{{$}}
+// CHECK-FIXES: {{^}}  if (i < 5) {{{$}}
+// CHECK-FIXES: {{^}}    return true;{{$}}
+// CHECK-FIXES: {{^}}  }{{$}}
+// CHECK-FIXES: {{^  return i > 10;$}}
+// CHECK-FIXES: {{^}$}}
+
+bool complex_chained_if_return_return_negated(int i) {
+  if (i < 5) {
+    return false;
+  }
+  if (i > 10) {
+    return false;
+  }
+  return true;
+}
+// CHECK-MESSAGES: :[[@LINE-4]]:12: warning: {{.*}} in conditional return
+// CHECK-FIXES: {{^}}bool complex_chained_if_return_return_negated(int i) {{{$}}
+// CHECK-FIXES: {{^}}  if (i < 5) {{{$}}
+// CHECK-FIXES: {{^}}    return false;{{$}}
+// CHECK-FIXES: {{^}}  }{{$}}
+// CHECK-FIXES: {{^  return i <= 10;$}}
+// CHECK-FIXES: {{^}$}}
diff --git a/test/clang-tidy/readability-simplify-bool-expr.cpp b/test/clang-tidy/readability-simplify-bool-expr.cpp
new file mode 100644 (file)
index 0000000..8ae7874
--- /dev/null
@@ -0,0 +1,932 @@
+// RUN: %check_clang_tidy %s readability-simplify-boolean-expr %t
+
+bool a1 = false;
+
+//=-=-=-=-=-=-= operator ==
+bool aa = false == a1;
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: redundant boolean literal supplied to boolean operator [readability-simplify-boolean-expr]
+// CHECK-FIXES: {{^bool aa = !a1;$}}
+bool ab = true == a1;
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: {{.*}} to boolean operator
+// CHECK-FIXES: {{^bool ab = a1;$}}
+bool a2 = a1 == false;
+// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: {{.*}} to boolean operator
+// CHECK-FIXES: {{^bool a2 = !a1;$}}
+bool a3 = a1 == true;
+// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: {{.*}} to boolean operator
+// CHECK-FIXES: {{^bool a3 = a1;$}}
+
+//=-=-=-=-=-=-= operator !=
+bool n1 = a1 != false;
+// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: {{.*}} to boolean operator
+// CHECK-FIXES: {{^bool n1 = a1;$}}
+bool n2 = a1 != true;
+// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: {{.*}} to boolean operator
+// CHECK-FIXES: {{^bool n2 = !a1;$}}
+bool n3 = false != a1;
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: {{.*}} to boolean operator
+// CHECK-FIXES: {{^bool n3 = a1;$}}
+bool n4 = true != a1;
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: {{.*}} to boolean operator
+// CHECK-FIXES: {{^bool n4 = !a1;$}}
+
+//=-=-=-=-=-=-= operator ||
+bool a4 = a1 || false;
+// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: {{.*}} to boolean operator
+// CHECK-FIXES: {{^bool a4 = a1;$}}
+bool a5 = a1 || true;
+// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: {{.*}} to boolean operator
+// CHECK-FIXES: {{^bool a5 = true;$}}
+bool a6 = false || a1;
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: {{.*}} to boolean operator
+// CHECK-FIXES: {{^bool a6 = a1;$}}
+bool a7 = true || a1;
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: {{.*}} to boolean operator
+// CHECK-FIXES: {{^bool a7 = true;$}}
+
+//=-=-=-=-=-=-= operator &&
+bool a8 = a1 && false;
+// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: {{.*}} to boolean operator
+// CHECK-FIXES: {{^bool a8 = false;$}}
+bool a9 = a1 && true;
+// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: {{.*}} to boolean operator
+// CHECK-FIXES: {{^bool a9 = a1;$}}
+bool ac = false && a1;
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: {{.*}} to boolean operator
+// CHECK-FIXES: {{^bool ac = false;$}}
+bool ad = true && a1;
+// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: {{.*}} to boolean operator
+// CHECK-FIXES: {{^bool ad = a1;$}}
+
+void if_with_bool_literal_condition() {
+  int i = 0;
+  if (false) {
+    i = 1;
+  } else {
+    i = 2;
+  }
+  i = 3;
+  // CHECK-MESSAGES: :[[@LINE-6]]:7: warning: {{.*}} in if statement condition
+  // CHECK-FIXES:      {{^  int i = 0;$}}
+  // CHECK-FIXES-NEXT: {{^  {$}}
+  // CHECK-FIXES-NEXT: {{^    i = 2;$}}
+  // CHECK-FIXES-NEXT: {{^  }$}}
+  // CHECK-FIXES-NEXT: {{^  i = 3;$}}
+
+  i = 4;
+  if (true) {
+    i = 5;
+  } else {
+    i = 6;
+  }
+  i = 7;
+  // CHECK-MESSAGES: :[[@LINE-6]]:7: warning: {{.*}} in if statement condition
+  // CHECK-FIXES:      {{^  i = 4;$}}
+  // CHECK-FIXES-NEXT: {{^  {$}}
+  // CHECK-FIXES-NEXT: {{^    i = 5;$}}
+  // CHECK-FIXES-NEXT: {{^  }$}}
+  // CHECK-FIXES-NEXT: {{^  i = 7;$}}
+
+  i = 8;
+  if (false) {
+    i = 9;
+  }
+  i = 11;
+  // CHECK-MESSAGES: :[[@LINE-4]]:7: warning: {{.*}} in if statement condition
+  // CHECK-FIXES:      {{^  i = 8;$}}
+  // CHECK-FIXES-NEXT: {{^  $}}
+  // CHECK-FIXES-NEXT: {{^  i = 11;$}}
+}
+
+void operator_equals() {
+  int i = 0;
+  bool b1 = (i > 2);
+  if (b1 == true) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: {{.*}} to boolean operator
+    // CHECK-FIXES: {{^  if \(b1\) {$}}
+    i = 5;
+  } else {
+    i = 6;
+  }
+  bool b2 = (i > 4);
+  if (b2 == false) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: {{.*}} to boolean operator
+    // CHECK-FIXES: {{^  if \(!b2\) {$}}
+    i = 7;
+  } else {
+    i = 9;
+  }
+  bool b3 = (i > 6);
+  if (true == b3) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: {{.*}} to boolean operator
+    // CHECK-FIXES: {{^  if \(b3\) {$}}
+    i = 10;
+  } else {
+    i = 11;
+  }
+  bool b4 = (i > 8);
+  if (false == b4) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: {{.*}} to boolean operator
+    // CHECK-FIXES: {{^  if \(!b4\) {$}}
+    i = 12;
+  } else {
+    i = 13;
+  }
+}
+
+void operator_or() {
+  int i = 0;
+  bool b5 = (i > 10);
+  if (b5 || false) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: {{.*}} to boolean operator
+    // CHECK-FIXES: {{^  if \(b5\) {$}}
+    i = 14;
+  } else {
+    i = 15;
+  }
+  bool b6 = (i > 10);
+  if (b6 || true) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: {{.*}} to boolean operator
+    // CHECK-FIXES: {{^  if \(true\) {$}}
+    i = 16;
+  } else {
+    i = 17;
+  }
+  bool b7 = (i > 10);
+  if (false || b7) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: {{.*}} to boolean operator
+    // CHECK-FIXES: {{^  if \(b7\) {$}}
+    i = 18;
+  } else {
+    i = 19;
+  }
+  bool b8 = (i > 10);
+  if (true || b8) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: {{.*}} to boolean operator
+    // CHECK-FIXES: {{^  if \(true\) {$}}
+    i = 20;
+  } else {
+    i = 21;
+  }
+}
+
+void operator_and() {
+  int i = 0;
+  bool b9 = (i > 20);
+  if (b9 && false) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: {{.*}} to boolean operator
+    // CHECK-FIXES: {{^  if \(false\) {$}}
+    i = 22;
+  } else {
+    i = 23;
+  }
+  bool ba = (i > 20);
+  if (ba && true) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: {{.*}} to boolean operator
+    // CHECK-FIXES: {{^  if \(ba\) {$}}
+    i = 24;
+  } else {
+    i = 25;
+  }
+  bool bb = (i > 20);
+  if (false && bb) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: {{.*}} to boolean operator
+    // CHECK-FIXES: {{^  if \(false\) {$}}
+    i = 26;
+  } else {
+    i = 27;
+  }
+  bool bc = (i > 20);
+  if (true && bc) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: {{.*}} to boolean operator
+    // CHECK-FIXES: {{^  if \(bc\) {$}}
+    i = 28;
+  } else {
+    i = 29;
+  }
+}
+
+void ternary_operator() {
+  int i = 0;
+  bool bd = (i > 20) ? true : false;
+  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: {{.*}} in ternary expression result
+  // CHECK-FIXES: {{^  bool bd = i > 20;$}}
+
+  bool be = (i > 20) ? false : true;
+  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: {{.*}} in ternary expression result
+  // CHECK-FIXES: {{^  bool be = i <= 20;$}}
+
+  bool bf = ((i > 20)) ? false : true;
+  // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: {{.*}} in ternary expression result
+  // CHECK-FIXES: {{^  bool bf = i <= 20;$}}
+}
+
+void operator_not_equal() {
+  int i = 0;
+  bool bf = (i > 20);
+  if (false != bf) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: {{.*}} to boolean operator
+    // CHECK-FIXES: {{^  if \(bf\) {$}}
+    i = 30;
+  } else {
+    i = 31;
+  }
+  bool bg = (i > 20);
+  if (true != bg) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: {{.*}} to boolean operator
+    // CHECK-FIXES: {{^  if \(!bg\) {$}}
+    i = 32;
+  } else {
+    i = 33;
+  }
+  bool bh = (i > 20);
+  if (bh != false) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: {{.*}} to boolean operator
+    // CHECK-FIXES: {{^  if \(bh\) {$}}
+    i = 34;
+  } else {
+    i = 35;
+  }
+  bool bi = (i > 20);
+  if (bi != true) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: {{.*}} to boolean operator
+    // CHECK-FIXES: {{^  if \(!bi\) {$}}
+    i = 36;
+  } else {
+    i = 37;
+  }
+}
+
+void nested_booleans() {
+  if (false || (true || false)) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: {{.*}} to boolean operator
+    // CHECK-FIXES: {{^  if \(false \|\| \(true\)\) {$}}
+  }
+  if (true && (true || false)) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: {{.*}} to boolean operator
+    // CHECK-FIXES: {{^  if \(true && \(true\)\) {$}}
+  }
+  if (false || (true && false)) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: {{.*}} to boolean operator
+    // CHECK-FIXES: {{^  if \(false \|\| \(false\)\) {$}}
+  }
+  if (true && (true && false)) {
+    // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: {{.*}} to boolean operator
+    // CHECK-FIXES: {{^  if \(true && \(false\)\) {$}}
+  }
+}
+
+static constexpr bool truthy() {
+  return true;
+}
+
+#define HAS_XYZ_FEATURE true
+
+void macros_and_constexprs(int i = 0) {
+  bool b = (i == 1);
+  if (b && truthy()) {
+    // leave this alone; if you want it simplified, then you should
+    // inline the constexpr function first.
+    i = 1;
+  }
+  i = 2;
+  if (b && HAS_XYZ_FEATURE) {
+    // leave this alone; if you want it simplified, then you should
+    // inline the macro first.
+    i = 3;
+  }
+  i = 4;
+}
+
+bool conditional_return_statements(int i) {
+  if (i == 0) return true; else return false;
+}
+// CHECK-MESSAGES: :[[@LINE-2]]:22: warning: {{.*}} in conditional return statement
+// CHECK-FIXES:      {{^}}  return i == 0;{{$}}
+// CHECK-FIXES-NEXT: {{^}$}}
+
+bool conditional_return_statements_then_expr(int i, int j) {
+  if (i == j) return (i == 0); else return false;
+}
+
+bool conditional_return_statements_else_expr(int i, int j) {
+  if (i == j) return true; else return (i == 0);
+}
+
+bool negated_conditional_return_statements(int i) {
+  if (i == 0) return false; else return true;
+}
+// CHECK-MESSAGES: :[[@LINE-2]]:22: warning: {{.*}} in conditional return statement
+// CHECK-FIXES:      {{^}}  return i != 0;{{$}}
+// CHECK-FIXES-NEXT: {{^}$}}
+
+bool negative_condition_conditional_return_statement(int i) {
+  if (!(i == 0)) return false; else return true;
+}
+// CHECK-MESSAGES: :[[@LINE-2]]:25: warning: {{.*}} in conditional return statement
+// CHECK-FIXES:      {{^}}  return i == 0;{{$}}
+// CHECK-FIXES-NEXT: {{^}$}}
+
+bool conditional_compound_return_statements(int i) {
+  if (i == 1) {
+    return true;
+  } else {
+    return false;
+  }
+}
+// CHECK-MESSAGES: :[[@LINE-5]]:12: warning: {{.*}} in conditional return statement
+// CHECK-FIXES:      {{^}}bool conditional_compound_return_statements(int i) {{{$}}
+// CHECK-FIXES-NEXT: {{^}}  return i == 1;{{$}}
+// CHECK-FIXES-NEXT: {{^}$}}
+
+bool negated_conditional_compound_return_statements(int i) {
+  if (i == 1) {
+    return false;
+  } else {
+    return true;
+  }
+}
+// CHECK-MESSAGES: :[[@LINE-5]]:12: warning: {{.*}} in conditional return statement
+// CHECK-FIXES:      {{^}}bool negated_conditional_compound_return_statements(int i) {{{$}}
+// CHECK-FIXES-NEXT: {{^}}  return i != 1;{{$}}
+// CHECK-FIXES-NEXT: {{^}$}}
+
+bool conditional_return_statements_side_effects_then(int i) {
+  if (i == 2) {
+    macros_and_constexprs();
+    return true;
+  } else
+    return false;
+}
+
+bool negated_conditional_return_statements_side_effects_then(int i) {
+  if (i == 2) {
+    macros_and_constexprs();
+    return false;
+  } else
+    return true;
+}
+
+bool conditional_return_statements_side_effects_else(int i) {
+  if (i == 2)
+    return true;
+  else {
+    macros_and_constexprs();
+    return false;
+  }
+}
+
+bool negated_conditional_return_statements_side_effects_else(int i) {
+  if (i == 2)
+    return false;
+  else {
+    macros_and_constexprs();
+    return true;
+  }
+}
+
+void lambda_conditional_return_statements() {
+  auto lambda = [](int n) -> bool { if (n > 0) return true; else return false; };
+  // CHECK-MESSAGES: :[[@LINE-1]]:55: warning: {{.*}} in conditional return statement
+  // CHECK-FIXES: {{^}}  auto lambda = [](int n) -> bool { return n > 0; };{{$}}
+
+  auto lambda2 = [](int n) -> bool {
+    if (n > 0) {
+        return true;
+    } else {
+        return false;
+    }
+  };
+  // CHECK-MESSAGES: :[[@LINE-5]]:16: warning: {{.*}} in conditional return statement
+  // CHECK-FIXES:      {{^}}  auto lambda2 = [](int n) -> bool {{{$}}
+  // CHECK-FIXES-NEXT: {{^}}    return n > 0;{{$}}
+  // CHECK-FIXES-NEXT: {{^}}  };{{$}}
+
+  auto lambda3 = [](int n) -> bool { if (n > 0) {macros_and_constexprs(); return true; } else return false; };
+
+  auto lambda4 = [](int n) -> bool {
+    if (n > 0)
+        return true;
+    else {
+        macros_and_constexprs();
+        return false;
+    }
+  };
+
+  auto lambda5 = [](int n) -> bool { if (n > 0) return false; else return true; };
+  // CHECK-MESSAGES: :[[@LINE-1]]:56: warning: {{.*}} in conditional return statement
+  // CHECK-FIXES: {{^}}  auto lambda5 = [](int n) -> bool { return n <= 0; };{{$}}
+
+  auto lambda6 = [](int n) -> bool {
+    if (n > 0) {
+        return false;
+    } else {
+        return true;
+    }
+  };
+  // CHECK-MESSAGES: :[[@LINE-5]]:16: warning: {{.*}} in conditional return statement
+  // CHECK-FIXES:      {{^}}  auto lambda6 = [](int n) -> bool {{{$}}
+  // CHECK-FIXES-NEXT: {{^}}    return n <= 0;{{$}}
+  // CHECK-FIXES-NEXT: {{^}}  };{{$}}
+}
+
+void simple_conditional_assignment_statements(int i) {
+  bool b;
+  if (i > 10)
+    b = true;
+  else
+    b = false;
+  bool bb = false;
+  // CHECK-MESSAGES: :[[@LINE-4]]:9: warning: {{.*}} in conditional assignment
+  // CHECK-FIXES: bool b;
+  // CHECK-FIXES: {{^  }}b = i > 10;{{$}}
+  // CHECK-FIXES: bool bb = false;
+
+  bool c;
+  if (i > 20)
+    c = false;
+  else
+    c = true;
+  bool c2 = false;
+  // CHECK-MESSAGES: :[[@LINE-4]]:9: warning: {{.*}} in conditional assignment
+  // CHECK-FIXES: bool c;
+  // CHECK-FIXES: {{^  }}c = i <= 20;{{$}}
+  // CHECK-FIXES: bool c2 = false;
+
+  // Unchanged: different variables.
+  bool b2;
+  if (i > 12)
+    b = true;
+  else
+    b2 = false;
+
+  // Unchanged: no else statement.
+  bool b3;
+  if (i > 15)
+    b3 = true;
+
+  // Unchanged: not boolean assignment.
+  int j;
+  if (i > 17)
+    j = 10;
+  else
+    j = 20;
+
+  // Unchanged: different variables assigned.
+  int k = 0;
+  bool b4 = false;
+  if (i > 10)
+    b4 = true;
+  else
+    k = 10;
+}
+
+void complex_conditional_assignment_statements(int i) {
+  bool d;
+  if (i > 30) {
+    d = true;
+  } else {
+    d = false;
+  }
+  d = false;
+  // CHECK-MESSAGES: :[[@LINE-5]]:9: warning: {{.*}} in conditional assignment
+  // CHECK-FIXES: bool d;
+  // CHECK-FIXES: {{^  }}d = i > 30;{{$}}
+  // CHECK-FIXES: d = false;
+
+  bool e;
+  if (i > 40) {
+    e = false;
+  } else {
+    e = true;
+  }
+  e = false;
+  // CHECK-MESSAGES: :[[@LINE-5]]:9: warning: {{.*}} in conditional assignment
+  // CHECK-FIXES: bool e;
+  // CHECK-FIXES: {{^  }}e = i <= 40;{{$}}
+  // CHECK-FIXES: e = false;
+
+  // Unchanged: no else statement.
+  bool b3;
+  if (i > 15) {
+    b3 = true;
+  }
+
+  // Unchanged: not a boolean assignment.
+  int j;
+  if (i > 17) {
+    j = 10;
+  } else {
+    j = 20;
+  }
+
+  // Unchanged: multiple statements.
+  bool f;
+  if (j > 10) {
+    j = 10;
+    f = true;
+  } else {
+    j = 20;
+    f = false;
+  }
+
+  // Unchanged: multiple statements.
+  bool g;
+  if (j > 10)
+    f = true;
+  else {
+    j = 20;
+    f = false;
+  }
+
+  // Unchanged: multiple statements.
+  bool h;
+  if (j > 10) {
+    j = 10;
+    f = true;
+  } else
+    f = false;
+}
+
+// Unchanged: chained return statements, but ChainedConditionalReturn not set.
+bool chained_conditional_compound_return(int i) {
+  if (i < 0) {
+    return true;
+  } else if (i < 10) {
+    return false;
+  } else if (i > 20) {
+    return true;
+  } else {
+    return false;
+  }
+}
+
+// Unchanged: chained return statements, but ChainedConditionalReturn not set.
+bool chained_conditional_return(int i) {
+  if (i < 0)
+    return true;
+  else if (i < 10)
+    return false;
+  else if (i > 20)
+    return true;
+  else
+    return false;
+}
+
+// Unchanged: chained assignments, but ChainedConditionalAssignment not set.
+void chained_conditional_compound_assignment(int i) {
+  bool b;
+  if (i < 0) {
+    b = true;
+  } else if (i < 10) {
+    b = false;
+  } else if (i > 20) {
+    b = true;
+  } else {
+    b = false;
+  }
+}
+
+// Unchanged: chained return statements, but ChainedConditionalReturn not set.
+void chained_conditional_assignment(int i) {
+  bool b;
+  if (i < 0)
+    b = true;
+  else if (i < 10)
+    b = false;
+  else if (i > 20)
+    b = true;
+  else
+    b = false;
+}
+
+// Unchanged: chained return statements, but ChainedConditionalReturn not set.
+bool chained_simple_if_return_negated(int i) {
+  if (i < 5)
+    return false;
+  if (i > 10)
+    return false;
+  return true;
+}
+
+// Unchanged: chained return statements, but ChainedConditionalReturn not set.
+bool complex_chained_if_return_return(int i) {
+  if (i < 5) {
+    return true;
+  }
+  if (i > 10) {
+    return true;
+  }
+  return false;
+}
+
+// Unchanged: chained return statements, but ChainedConditionalReturn not set.
+bool complex_chained_if_return_return_negated(int i) {
+  if (i < 5) {
+    return false;
+  }
+  if (i > 10) {
+    return false;
+  }
+  return true;
+}
+
+// Unchanged: chained return statements, but ChainedConditionalReturn not set.
+bool chained_simple_if_return(int i) {
+  if (i < 5)
+    return true;
+  if (i > 10)
+    return true;
+  return false;
+}
+
+bool simple_if_return_return(int i) {
+  if (i > 10)
+    return true;
+  return false;
+}
+// CHECK-MESSAGES: :[[@LINE-3]]:12: warning: {{.*}} in conditional return
+// CHECK-FIXES: {{^}}bool simple_if_return_return(int i) {{{$}}
+// CHECK-FIXES: {{^  return i > 10;$}}
+// CHECK-FIXES: {{^}$}}
+
+bool simple_if_return_return_negated(int i) {
+  if (i > 10)
+    return false;
+  return true;
+}
+// CHECK-MESSAGES: :[[@LINE-3]]:12: warning: {{.*}} in conditional return
+// CHECK-FIXES: {{^}}bool simple_if_return_return_negated(int i) {{{$}}
+// CHECK-FIXES: {{^  return i <= 10;$}}
+// CHECK-FIXES: {{^}$}}
+
+bool complex_if_return_return(int i) {
+  if (i > 10) {
+    return true;
+  }
+  return false;
+}
+// CHECK-MESSAGES: :[[@LINE-4]]:12: warning: {{.*}} in conditional return
+// CHECK-FIXES: {{^}}bool complex_if_return_return(int i) {{{$}}
+// CHECK-FIXES: {{^  return i > 10;$}}
+// CHECK-FIXES: {{^}$}}
+
+bool complex_if_return_return_negated(int i) {
+  if (i > 10) {
+    return false;
+  }
+  return true;
+}
+// CHECK-MESSAGES: :[[@LINE-4]]:12: warning: {{.*}} in conditional return
+// CHECK-FIXES: {{^}}bool complex_if_return_return_negated(int i) {{{$}}
+// CHECK-FIXES: {{^  return i <= 10;$}}
+// CHECK-FIXES: {{^}$}}
+
+bool if_implicit_bool_expr(int i) {
+  if (i & 1) {
+    return true;
+  } else {
+    return false;
+  }
+}
+// CHECK-MESSAGES: :[[@LINE-5]]:12: warning: {{.*}} in conditional return
+// CHECK-FIXES: {{^}}  return (i & 1) != 0;{{$}}
+
+bool negated_if_implicit_bool_expr(int i) {
+  if (i - 1) {
+    return false;
+  } else {
+    return true;
+  }
+}
+// CHECK-MESSAGES: :[[@LINE-5]]:12: warning: {{.*}} in conditional return
+// CHECK-FIXES: {{^}}  return (i - 1) == 0;{{$}}
+
+bool implicit_int(int i) {
+  if (i) {
+    return true;
+  } else {
+    return false;
+  }
+}
+// CHECK-MESSAGES: :[[@LINE-5]]:12: warning: {{.*}} in conditional return
+// CHECK-FIXES: {{^}}  return i != 0;{{$}}
+
+bool explicit_bool(bool b) {
+  if (b) {
+    return true;
+  } else {
+    return false;
+  }
+}
+// CHECK-MESSAGES: :[[@LINE-5]]:12: warning: {{.*}} in conditional return
+// CHECK-FIXES: {{^}}  return b;{{$}}
+
+class Implicit {
+public:
+  operator bool() {
+    return true;
+  }
+};
+
+bool object_bool_implicit_conversion(Implicit O) {
+  if (O) {
+    return true;
+  } else {
+    return false;
+  }
+}
+// CHECK-MESSAGES: :[[@LINE-5]]:12: warning: {{.*}} in conditional return
+// CHECK-FIXES: {{^}}  return O;{{$}}
+
+bool negated_explicit_bool(bool b) {
+  if (!b) {
+    return true;
+  } else {
+    return false;
+  }
+}
+// CHECK-MESSAGES: :[[@LINE-5]]:12: warning: {{.*}} in conditional return
+// CHECK-FIXES: {{^}}  return !b;{{$}}
+
+bool bitwise_complement_conversion(int i) {
+  if (~i) {
+    return true;
+  } else {
+    return false;
+  }
+}
+// CHECK-MESSAGES: :[[@LINE-5]]:12: warning: {{.*}} in conditional return
+// CHECK-FIXES: {{^}}  return ~i != 0;{{$}}
+
+bool logical_or(bool a, bool b) {
+  if (a || b) {
+    return true;
+  } else {
+    return false;
+  }
+}
+// CHECK-MESSAGES: :[[@LINE-5]]:12: warning: {{.*}} in conditional return
+// CHECK-FIXES: {{^}}  return a || b;{{$}}
+
+bool logical_and(bool a, bool b) {
+  if (a && b) {
+    return true;
+  } else {
+    return false;
+  }
+}
+// CHECK-MESSAGES: :[[@LINE-5]]:12: warning: {{.*}} in conditional return
+// CHECK-FIXES: {{^}}  return a && b;{{$}}
+
+class Comparable
+{
+public:
+  bool operator==(Comparable const &rhs) { return true; }
+  bool operator!=(Comparable const &rhs) { return false; }
+};
+
+bool comparable_objects() {
+  Comparable c;
+  Comparable d;
+  if (c == d) {
+    return true;
+  } else {
+    return false;
+  }
+}
+// CHECK-MESSAGES: :[[@LINE-5]]:12: warning: {{.*}} in conditional return
+// CHECK-FIXES: {{^}}  return c == d;{{$}}
+
+bool negated_comparable_objects() {
+  Comparable c;
+  Comparable d;
+  if (c == d) {
+    return false;
+  } else {
+    return true;
+  }
+}
+// CHECK-MESSAGES: :[[@LINE-5]]:12: warning: {{.*}} in conditional return
+// CHECK-FIXES: {{^}}  return c != d;{{$}}
+
+struct X {
+  explicit operator bool();
+};
+
+void explicit_conversion_assignment(X x) {
+  bool y;
+  if (x) {
+    y = true;
+  } else {
+    y = false;
+  }
+}
+// CHECK-MESSAGES: :[[@LINE-5]]:9: warning: {{.*}} in conditional assignment
+// CHECK-FIXES: {{^  bool y;$}}
+// CHECK-FIXES: {{^}}  y = static_cast<bool>(x);{{$}}
+
+void ternary_integer_condition(int i) {
+  bool b = i ? true : false;
+}
+// CHECK-MESSAGES: :[[@LINE-2]]:16: warning: {{.*}} in ternary expression result
+// CHECK-FIXES: bool b = i != 0;{{$}}
+
+bool non_null_pointer_condition(int *p1) {
+  if (p1) {
+    return true;
+  } else {
+    return false;
+  }
+}
+// CHECK-MESSAGES: :[[@LINE-5]]:12: warning: {{.*}} in conditional return
+// CHECK-FIXES: return p1 != nullptr;{{$}}
+
+bool null_pointer_condition(int *p2) {
+  if (!p2) {
+    return true;
+  } else {
+    return false;
+  }
+}
+// CHECK-MESSAGES: :[[@LINE-5]]:12: warning: {{.*}} in conditional return
+// CHECK-FIXES: return p2 == nullptr;{{$}}
+
+bool negated_non_null_pointer_condition(int *p3) {
+  if (p3) {
+    return false;
+  } else {
+    return true;
+  }
+}
+// CHECK-MESSAGES: :[[@LINE-5]]:12: warning: {{.*}} in conditional return
+// CHECK-FIXES: return p3 == nullptr;{{$}}
+
+bool negated_null_pointer_condition(int *p4) {
+  if (!p4) {
+    return false;
+  } else {
+    return true;
+  }
+}
+// CHECK-MESSAGES: :[[@LINE-5]]:12: warning: {{.*}} in conditional return
+// CHECK-FIXES: return p4 != nullptr;{{$}}
+
+bool comments_in_the_middle(bool b) {
+  if (b) {
+    return true;
+  } else {
+    // something wicked this way comes
+    return false;
+  }
+}
+// CHECK-MESSAGES: :[[@LINE-6]]:12: warning: {{.*}} in conditional return
+// CHECK-FIXES: {{^}}  if (b) {
+// CHECK-FIXES: // something wicked this way comes{{$}}
+
+bool preprocessor_in_the_middle(bool b) {
+  if (b) {
+    return true;
+  } else {
+#define SOMETHING_WICKED false
+    return false;
+  }
+}
+// CHECK-MESSAGES: :[[@LINE-6]]:12: warning: {{.*}} in conditional return
+// CHECK-FIXES: {{^}}  if (b) {
+// CHECK-FIXES: {{^}}#define SOMETHING_WICKED false
+
+bool integer_not_zero(int i) {
+  if (i) {
+    return false;
+  } else {
+    return true;
+  }
+}
+// CHECK-MESSAGES: :[[@LINE-5]]:12: warning: {{.*}} in conditional return
+// CHECK-FIXES: {{^}}  return i == 0;{{$}}
+
+class A {
+public:
+    int m;
+};
+
+bool member_pointer_nullptr(int A::*p) {
+  if (p) {
+    return true;
+  } else {
+    return false;
+  }
+}
+// CHECK-MESSAGES: :[[@LINE-5]]:12: warning: {{.*}} in conditional return
+// CHECK-FIXES: return p != nullptr;{{$}}
+
+bool integer_member_implicit_cast(A *p) {
+  if (p->m) {
+    return true;
+  } else {
+    return false;
+  }
+}
+// CHECK-MESSAGES: :[[@LINE-5]]:12: warning: {{.*}} in conditional return
+// CHECK-FIXES: return p->m != 0;{{$}}
diff --git a/test/clang-tidy/readability-static-definition-in-anonymous-namespace.cpp b/test/clang-tidy/readability-static-definition-in-anonymous-namespace.cpp
new file mode 100644 (file)
index 0000000..5c3c8c1
--- /dev/null
@@ -0,0 +1,49 @@
+// RUN: %check_clang_tidy %s readability-static-definition-in-anonymous-namespace %t
+
+namespace {
+
+int a = 1;
+const int b = 1;
+static int c = 1;
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: 'c' is a static definition in anonymous namespace; static is redundant here [readability-static-definition-in-anonymous-namespace]
+// CHECK-FIXES: {{^}}int c = 1;
+static const int d = 1;
+// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: 'd' is a static definition in anonymous namespace
+// CHECK-FIXES: {{^}}const int d = 1;
+const static int e = 1;
+// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: 'e' is a static definition in anonymous namespace
+// CHECK-FIXES: {{^}}const int e = 1;
+
+void f() {
+  int a = 1;
+  static int b = 1;
+}
+
+static int g() {
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: 'g' is a static definition in anonymous namespace
+// CHECK-FIXES: {{^}}int g() {
+  return 1;
+}
+
+#define DEFINE_STATIC static
+// CHECK-FIXES: {{^}}#define DEFINE_STATIC static
+DEFINE_STATIC int h = 1;
+// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: 'h' is a static definition in anonymous namespace
+// CHECK-FIXES: {{^}}DEFINE_STATIC int h = 1;
+
+#define DEFINE_STATIC_VAR(x) static int x = 2
+// CHECK-FIXES: {{^}}#define DEFINE_STATIC_VAR(x) static int x = 2
+DEFINE_STATIC_VAR(i);
+// CHECK-FIXES: {{^}}DEFINE_STATIC_VAR(i);
+
+} // namespace
+
+namespace N {
+
+int a = 1;
+const int b = 1;
+static int c = 1;
+static const int d = 1;
+const static int e = 1;
+
+} // namespace N
diff --git a/test/clang-tidy/readability-uniqueptr-delete-release.cpp b/test/clang-tidy/readability-uniqueptr-delete-release.cpp
new file mode 100644 (file)
index 0000000..aa8ae25
--- /dev/null
@@ -0,0 +1,71 @@
+// RUN: %check_clang_tidy %s readability-uniqueptr-delete-release %t
+
+namespace std {
+template <typename T>
+struct default_delete {};
+
+template <typename T, typename D = default_delete<T>>
+class unique_ptr {
+ public:
+  unique_ptr();
+  ~unique_ptr();
+  explicit unique_ptr(T*);
+  template <typename U, typename E>
+  unique_ptr(unique_ptr<U, E>&&);
+  T* release();
+};
+}  // namespace std
+
+std::unique_ptr<int>& ReturnsAUnique();
+
+void Positives() {
+  std::unique_ptr<int> P;
+  delete P.release();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: prefer '= nullptr' to 'delete x.release()' to reset unique_ptr<> objects [readability-uniqueptr-delete-release]
+  // CHECK-FIXES: {{^}}  P = nullptr;
+
+  std::unique_ptr<int> Array[20];
+  delete Array[4].release();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: prefer '= nullptr' to 'delete
+  // CHECK-FIXES: {{^}}  Array[4] = nullptr;
+
+  delete ReturnsAUnique().release();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: prefer '= nullptr' to 'delete
+  // CHECK-FIXES: {{^}}  ReturnsAUnique() = nullptr;
+}
+
+struct NotDefaultDeleter {};
+
+struct NotUniquePtr {
+  int* release();
+};
+
+void Negatives() {
+  std::unique_ptr<int, NotDefaultDeleter> P;
+  delete P.release();
+
+  NotUniquePtr P2;
+  delete P2.release();
+}
+
+template <typename T, typename D>
+void NegativeDeleterT() {
+  // Ideally this would trigger a warning, but we have all dependent types
+  // disabled for now.
+  std::unique_ptr<T> P;
+  delete P.release();
+
+  // We ignore this one because the deleter is a template argument.
+  // Not all instantiations will use the default deleter.
+  std::unique_ptr<int, D> P2;
+  delete P2.release();
+}
+template void NegativeDeleterT<int, std::default_delete<int>>();
+
+// Test some macros
+
+#define DELETE_RELEASE(x) delete (x).release()
+void NegativesWithTemplate() {
+  std::unique_ptr<int> P;
+  DELETE_RELEASE(P);
+}
diff --git a/test/clang-tidy/select-checks.cpp b/test/clang-tidy/select-checks.cpp
new file mode 100644 (file)
index 0000000..12d05b4
--- /dev/null
@@ -0,0 +1,11 @@
+// RUN: clang-tidy %s -checks='-*,llvm-namespace-*' -- 2>&1 | FileCheck -implicit-check-not='{{warning:|error:}}' %s
+// RUN: clang-tidy %s -checks='-*,an-unknown-check' -- 2>&1 | FileCheck -implicit-check-not='{{warning:|error:}}' -check-prefix=CHECK2 %s
+
+// CHECK2: Error: no checks enabled.
+
+namespace i {
+}
+// CHECK: :[[@LINE-1]]:2: warning: namespace 'i' not terminated with a closing comment [llvm-namespace-comment]
+
+// Expect no warnings from the google-explicit-constructor check:
+class A { A(int i); };
diff --git a/test/clang-tidy/serialize-diagnostics.cpp b/test/clang-tidy/serialize-diagnostics.cpp
new file mode 100644 (file)
index 0000000..804253c
--- /dev/null
@@ -0,0 +1,3 @@
+// RUN: clang-tidy -checks=-*,llvm-namespace-comment %s -- -serialize-diagnostics %t | FileCheck %s
+// CHECK: :[[@LINE+1]]:12: error: expected ';' after struct [clang-diagnostic-error]
+struct A {}
diff --git a/test/clang-tidy/static-analyzer-config.cpp b/test/clang-tidy/static-analyzer-config.cpp
new file mode 100644 (file)
index 0000000..cee0a4c
--- /dev/null
@@ -0,0 +1,19 @@
+// RUN: clang-tidy %s -checks='-*,clang-analyzer-unix.Malloc' -config='{CheckOptions: [{ key: "clang-analyzer-unix.Malloc:Optimistic", value: true}]}' -- | FileCheck %s
+typedef __typeof(sizeof(int)) size_t;
+void *malloc(size_t);
+void free(void *);
+void __attribute((ownership_returns(malloc))) *my_malloc(size_t);
+void __attribute((ownership_takes(malloc, 1))) my_free(void *);
+
+void f1() {
+  void *p = malloc(12);
+  return;
+  // CHECK: warning: Potential leak of memory pointed to by 'p' [clang-analyzer-unix.Malloc]
+}
+
+void af2() {
+  void *p = my_malloc(12);
+  my_free(p);
+  free(p);
+  // CHECK: warning: Attempt to free released memory [clang-analyzer-unix.Malloc]
+}
diff --git a/test/clang-tidy/static-analyzer.cpp b/test/clang-tidy/static-analyzer.cpp
new file mode 100644 (file)
index 0000000..b0015aa
--- /dev/null
@@ -0,0 +1,17 @@
+// RUN: clang-tidy %s -checks='-*,clang-analyzer-*' -- | FileCheck %s
+extern void *malloc(unsigned long);
+extern void free(void *);
+
+void f() {
+  int *p = new int(42);
+  delete p;
+  delete p;
+  // CHECK: warning: Attempt to free released memory [clang-analyzer-cplusplus.NewDelete]
+}
+
+void g() {
+  void *q = malloc(132);
+  free(q);
+  free(q);
+  // CHECK: warning: Attempt to free released memory [clang-analyzer-unix.Malloc]
+}
diff --git a/test/clang-tidy/temporaries.cpp b/test/clang-tidy/temporaries.cpp
new file mode 100644 (file)
index 0000000..a32474d
--- /dev/null
@@ -0,0 +1,23 @@
+// RUN: clang-tidy -checks='-*,clang-analyzer-core.NullDereference' -analyze-temporary-dtors %s -- | FileCheck %s
+
+struct NoReturnDtor {
+  ~NoReturnDtor() __attribute__((noreturn));
+};
+
+extern bool check(const NoReturnDtor &);
+
+// CHECK-NOT: warning
+void testNullPointerDereferencePositive() {
+  int *value = 0;
+  // CHECK: [[@LINE+1]]:10: warning: Dereference of null pointer (loaded from variable 'value') [clang-analyzer-core.NullDereference]
+  *value = 1;
+}
+
+// CHECK-NOT: warning
+void testNullPointerDereference() {
+  int *value = 0;
+  if (check(NoReturnDtor())) {
+    // This unreachable code causes a warning if we don't run with -analyze-temporary-dtors
+    *value = 1;
+  }
+}
diff --git a/test/clang-tidy/validate-check-names.cpp b/test/clang-tidy/validate-check-names.cpp
new file mode 100644 (file)
index 0000000..35bfb09
--- /dev/null
@@ -0,0 +1,2 @@
+// Check names may only contain alphanumeric characters, '-', '_', and '.'.
+// RUN: clang-tidy -checks=* -list-checks | grep '^    ' | cut -b5- | not grep -v '^[a-zA-Z0-9_.\-]\+$'
diff --git a/test/clang-tidy/werrors-diagnostics.cpp b/test/clang-tidy/werrors-diagnostics.cpp
new file mode 100644 (file)
index 0000000..9f90b62
--- /dev/null
@@ -0,0 +1,18 @@
+// RUN: clang-tidy %s -checks='-*,llvm-namespace-comment,clang-diagnostic*' \
+// RUN:   -- -Wunused-variable 2>&1 \
+// RUN:   | FileCheck %s --check-prefix=CHECK-WARN -implicit-check-not='{{warning|error}}:'
+// RUN: not clang-tidy %s -checks='-*,llvm-namespace-comment,clang-diagnostic*' \
+// RUN:   -warnings-as-errors='clang-diagnostic*' -- -Wunused-variable 2>&1 \
+// RUN:   | FileCheck %s --check-prefix=CHECK-WERR -implicit-check-not='{{warning|error}}:'
+// RUN: not clang-tidy %s -checks='-*,llvm-namespace-comment,clang-diagnostic*' \
+// RUN:   -warnings-as-errors='clang-diagnostic*' -quiet -- -Wunused-variable 2>&1 \
+// RUN:   | FileCheck %s --check-prefix=CHECK-WERR-QUIET -implicit-check-not='{{warning|error}}:'
+
+void f() { int i; }
+// CHECK-WARN: warning: unused variable 'i' [clang-diagnostic-unused-variable]
+// CHECK-WERR: error: unused variable 'i' [clang-diagnostic-unused-variable,-warnings-as-errors]
+// CHECK-WERR-QUIET: error: unused variable 'i' [clang-diagnostic-unused-variable,-warnings-as-errors]
+
+// CHECK-WARN-NOT: treated as
+// CHECK-WERR: 1 warning treated as error
+// CHECK-WERR-QUIET-NOT: treated as
diff --git a/test/clang-tidy/werrors-plural.cpp b/test/clang-tidy/werrors-plural.cpp
new file mode 100644 (file)
index 0000000..a14759d
--- /dev/null
@@ -0,0 +1,24 @@
+// RUN: clang-tidy %s -checks='-*,llvm-namespace-comment,clang-diagnostic*' -- 2>&1 \
+// RUN:   | FileCheck %s --check-prefix=CHECK-WARN -implicit-check-not='{{warning|error}}:'
+// RUN: not clang-tidy %s -checks='-*,llvm-namespace-comment,clang-diagnostic*' \
+// RUN:   -warnings-as-errors='llvm-namespace-comment' -- 2>&1 \
+// RUN:   | FileCheck %s --check-prefix=CHECK-WERR -implicit-check-not='{{warning|error}}:'
+// RUN: not clang-tidy %s -checks='-*,llvm-namespace-comment,clang-diagnostic*' \
+// RUN:   -warnings-as-errors='llvm-namespace-comment' -quiet -- 2>&1 \
+// RUN:   | FileCheck %s --check-prefix=CHECK-WERR-QUIET -implicit-check-not='{{warning|error}}:'
+
+namespace j {
+}
+// CHECK-WARN: warning: namespace 'j' not terminated with a closing comment [llvm-namespace-comment]
+// CHECK-WERR: error: namespace 'j' not terminated with a closing comment [llvm-namespace-comment,-warnings-as-errors]
+// CHECK-WERR-QUIET: error: namespace 'j' not terminated with a closing comment [llvm-namespace-comment,-warnings-as-errors]
+
+namespace k {
+}
+// CHECK-WARN: warning: namespace 'k' not terminated with a closing comment [llvm-namespace-comment]
+// CHECK-WERR: error: namespace 'k' not terminated with a closing comment [llvm-namespace-comment,-warnings-as-errors]
+// CHECK-WERR-QUIET: error: namespace 'k' not terminated with a closing comment [llvm-namespace-comment,-warnings-as-errors]
+
+// CHECK-WARN-NOT: treated as
+// CHECK-WERR: 2 warnings treated as errors
+// CHECK-WERR-QUIET-NOT: treated as
diff --git a/test/clang-tidy/werrors.cpp b/test/clang-tidy/werrors.cpp
new file mode 100644 (file)
index 0000000..44afb83
--- /dev/null
@@ -0,0 +1,13 @@
+// RUN: clang-tidy %s -checks='-*,llvm-namespace-comment' -- 2>&1 | FileCheck %s --check-prefix=CHECK-WARN -implicit-check-not='{{warning|error}}:'
+// RUN: not clang-tidy %s -checks='-*,llvm-namespace-comment' -warnings-as-errors='llvm-namespace-comment' -- 2>&1 | FileCheck %s --check-prefix=CHECK-WERR -implicit-check-not='{{warning|error}}:'
+// RUN: not clang-tidy %s -checks='-*,llvm-namespace-comment' -warnings-as-errors='llvm-namespace-comment' -quiet -- 2>&1 | FileCheck %s --check-prefix=CHECK-WERR-QUIET -implicit-check-not='{{warning|error}}:'
+
+namespace i {
+}
+// CHECK-WARN: warning: namespace 'i' not terminated with a closing comment [llvm-namespace-comment]
+// CHECK-WERR: error: namespace 'i' not terminated with a closing comment [llvm-namespace-comment,-warnings-as-errors]
+// CHECK-WERR-QUIET: error: namespace 'i' not terminated with a closing comment [llvm-namespace-comment,-warnings-as-errors]
+
+// CHECK-WARN-NOT: treated as
+// CHECK-WERR: 1 warning treated as error
+// CHECK-WERR-QUIET-NOT: treated as
diff --git a/test/clangd/authority-less-uri.test b/test/clangd/authority-less-uri.test
new file mode 100644 (file)
index 0000000..f52f0ea
--- /dev/null
@@ -0,0 +1,32 @@
+# RUN: clangd -run-synchronously < %s | FileCheck %s\r
+# It is absolutely vital that this file has CRLF line endings.\r
+#\r
+# Test authority-less URI\r
+Content-Length: 125\r
+\r
+{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}}\r
+\r
+Content-Length: 246\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file:///main.cpp","languageId":"cpp","version":1,"text":"struct fake { int a, bb, ccc; int f(int i, const float f) const; };\nint main() {\n  fake f;\n  f.\n}\n"}}}\r
+\r
+Content-Length: 146\r
+\r
+{"jsonrpc":"2.0","id":1,"method":"textDocument/completion","params":{"textDocument":{"uri":"file:/main.cpp"},"position":{"line":3,"character":5}}}\r
+# Test authority-less URI\r
+#\r
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[\r
+# CHECK-DAG: {"label":"a","kind":5,"detail":"int","sortText":"99964a","filterText":"a","insertText":"a"}\r
+# CHECK: ]}\r
+\r
+Content-Length: 172\r
+\r
+{"jsonrpc":"2.0","id":2,"method":"textDocument/completion","params":{"textDocument":{"uri":"file:///main.cpp"},"uri":"file:///main.cpp","position":{"line":3,"character":5}}}\r
+# Test params parsing in the presence of a 1.x-compatible client (inlined "uri")\r
+#\r
+# CHECK: {"jsonrpc":"2.0","id":2,"result":[\r
+# CHECK-DAG: {"label":"a","kind":5,"detail":"int","sortText":"99964a","filterText":"a","insertText":"a"}\r
+# CHECK: ]}\r
+Content-Length: 44\r
+\r
+{"jsonrpc":"2.0","id":3,"method":"shutdown"}\r
diff --git a/test/clangd/completion.test b/test/clangd/completion.test
new file mode 100644 (file)
index 0000000..f99c33b
--- /dev/null
@@ -0,0 +1,54 @@
+# RUN: clangd -run-synchronously < %s | FileCheck %s\r
+# It is absolutely vital that this file has CRLF line endings.\r
+#\r
+Content-Length: 125\r
+\r
+{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}}\r
+\r
+Content-Length: 246\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file:///main.cpp","languageId":"cpp","version":1,"text":"struct fake { int a, bb, ccc; int f(int i, const float f) const; };\nint main() {\n  fake f;\n  f.\n}\n"}}}\r
+\r
+Content-Length: 148\r
+\r
+{"jsonrpc":"2.0","id":1,"method":"textDocument/completion","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":3,"character":5}}}\r
+# The order of results returned by ASTUnit CodeComplete seems to be\r
+# nondeterministic, so we check regardless of order.\r
+#\r
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[\r
+# CHECK-DAG: {"label":"a","kind":5,"detail":"int","sortText":"99964a","filterText":"a","insertText":"a"}\r
+# CHECK-DAG: {"label":"bb","kind":5,"detail":"int","sortText":"99964bb","filterText":"bb","insertText":"bb"}\r
+# CHECK-DAG: {"label":"ccc","kind":5,"detail":"int","sortText":"99964ccc","filterText":"ccc","insertText":"ccc"}\r
+# CHECK-DAG: {"label":"operator=(const fake &)","kind":2,"detail":"fake &","sortText":"99965operator=","filterText":"operator=","insertText":"operator="}\r
+# CHECK-DAG: {"label":"~fake()","kind":4,"detail":"void","sortText":"99965~fake","filterText":"~fake","insertText":"~fake"}\r
+# CHECK-DAG: {"label":"f(int i, const float f) const","kind":2,"detail":"int","sortText":"99964f","filterText":"f","insertText":"f"}\r
+# CHECK: ]}\r
+Content-Length: 148\r
+\r
+{"jsonrpc":"2.0","id":2,"method":"textDocument/completion","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":3,"character":5}}}\r
+# Repeat the completion request, expect the same results.\r
+#\r
+# CHECK: {"jsonrpc":"2.0","id":2,"result":[\r
+# CHECK-DAG: {"label":"a","kind":5,"detail":"int","sortText":"99964a","filterText":"a","insertText":"a"}\r
+# CHECK-DAG: {"label":"bb","kind":5,"detail":"int","sortText":"99964bb","filterText":"bb","insertText":"bb"}\r
+# CHECK-DAG: {"label":"ccc","kind":5,"detail":"int","sortText":"99964ccc","filterText":"ccc","insertText":"ccc"}\r
+# CHECK-DAG: {"label":"operator=(const fake &)","kind":2,"detail":"fake &","sortText":"99965operator=","filterText":"operator=","insertText":"operator="}\r
+# CHECK-DAG: {"label":"~fake()","kind":4,"detail":"void","sortText":"99965~fake","filterText":"~fake","insertText":"~fake"}\r
+# CHECK-DAG: {"label":"f(int i, const float f) const","kind":2,"detail":"int","sortText":"99964f","filterText":"f","insertText":"f"}\r
+# CHECK: ]}\r
+# Update the source file and check for completions again.\r
+Content-Length: 226\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":2},"contentChanges":[{"text":"struct fancy { int (*func())(int, int); };\nint main() {\n  fancy f;\n  f.\n}\n"}]}}\r
+\r
+Content-Length: 148\r
+\r
+{"jsonrpc":"2.0","id":3,"method":"textDocument/completion","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":3,"character":5}}}\r
+# Repeat the completion request, expect the same results.\r
+#\r
+# CHECK: {"jsonrpc":"2.0","id":3,"result":[\r
+# CHECK-DAG: {"label":"func()","kind":2,"detail":"int (*)(int, int)","sortText":"99965func","filterText":"func","insertText":"func"}\r
+# CHECK: ]}\r
+Content-Length: 44\r
+\r
+{"jsonrpc":"2.0","id":4,"method":"shutdown"}\r
diff --git a/test/clangd/definitions.test b/test/clangd/definitions.test
new file mode 100644 (file)
index 0000000..57e0dce
--- /dev/null
@@ -0,0 +1,168 @@
+# RUN: clangd -run-synchronously < %s | FileCheck %s\r
+# It is absolutely vital that this file has CRLF line endings.\r
+#\r
+Content-Length: 125\r
+\r
+{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}}\r
+\r
+Content-Length: 172\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file:///main.cpp","languageId":"cpp","version":1,"text":"int main() {\nint a;\na;\n}\n"}}}\r
+\r
+Content-Length: 148\r
+\r
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":2,"character":0}}}\r
+# Go to local variable\r
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 1, "character": 0}, "end": {"line": 1, "character": 5}}}]}\r
+\r
+Content-Length: 148\r
+\r
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":2,"character":1}}}\r
+# Go to local variable, end of token\r
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 1, "character": 0}, "end": {"line": 1, "character": 5}}}]}\r
+\r
+Content-Length: 214\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":2},"contentChanges":[{"text":"struct Foo {\nint x;\n};\nint main() {\n  Foo bar = { x : 1 };\n}\n"}]}}\r
+\r
+Content-Length: 149\r
+\r
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":4,"character":14}}}\r
+# Go to field, GNU old-style field designator \r
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 1, "character": 0}, "end": {"line": 1, "character": 5}}}]}\r
+\r
+Content-Length: 215\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":3},"contentChanges":[{"text":"struct Foo {\nint x;\n};\nint main() {\n  Foo baz = { .x = 2 };\n}\n"}]}}\r
+\r
+Content-Length: 149\r
+\r
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":4,"character":15}}}\r
+# Go to field, field designator \r
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 1, "character": 0}, "end": {"line": 1, "character": 5}}}]}\r
+\r
+Content-Length: 187\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":4},"contentChanges":[{"text":"int main() {\n   main();\n   return 0;\n}"}]}}\r
+\r
+Content-Length: 148\r
+\r
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":1,"character":3}}}\r
+# Go to function declaration, function call \r
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 0, "character": 0}, "end": {"line": 3, "character": 1}}}]}\r
+\r
+Content-Length: 208\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":5},"contentChanges":[{"text":"struct Foo {\n};\nint main() {\n   Foo bar;\n   return 0;\n}\n"}]}}\r
+\r
+Content-Length: 148\r
+\r
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":3,"character":3}}}\r
+# Go to struct declaration, new struct instance \r
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 0, "character": 0}, "end": {"line": 1, "character": 1}}}]}\r
+\r
+Content-Length: 231\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":5},"contentChanges":[{"text":"namespace n1 {\nstruct Foo {\n};\n}\nint main() {\n   n1::Foo bar;\n   return 0;\n}\n"}]}}\r
+\r
+Content-Length: 148\r
+\r
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":5,"character":4}}}\r
+# Go to struct declaration, new struct instance, qualified name \r
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 0, "character": 0}, "end": {"line": 3, "character": 1}}}]}\r
+\r
+Content-Length: 215\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":6},"contentChanges":[{"text":"struct Foo {\n  int x;\n};\nint main() {\n   Foo bar;\n   bar.x;\n}\n"}]}}\r
+\r
+Content-Length: 148\r
+\r
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":5,"character":7}}}\r
+# Go to field declaration, field reference \r
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 1, "character": 2}, "end": {"line": 1, "character": 7}}}]}\r
+\r
+Content-Length: 220\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":7},"contentChanges":[{"text":"struct Foo {\n  void x();\n};\nint main() {\n   Foo bar;\n   bar.x();\n}\n"}]}}\r
+\r
+Content-Length: 148\r
+\r
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":5,"character":7}}}\r
+# Go to method declaration, method call \r
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 1, "character": 2}, "end": {"line": 1, "character": 10}}}]}\r
+\r
+Content-Length: 240\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":7},"contentChanges":[{"text":"struct Foo {\n};\ntypedef Foo TypedefFoo;\nint main() {\n   TypedefFoo bar;\n   return 0;\n}\n"}]}}\r
+\r
+Content-Length: 149\r
+\r
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":4,"character":10}}}\r
+# Go to typedef \r
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 2, "character": 0}, "end": {"line": 2, "character": 22}}}]}\r
+\r
+Content-Length: 254\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":7},"contentChanges":[{"text":"template <typename MyTemplateParam>\nvoid foo() {\n MyTemplateParam a;\n}\nint main() {\n   return 0;\n}\n"}]}}\r
+\r
+Content-Length: 149\r
+\r
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":2,"character":13}}}\r
+# Go to template type parameter. Fails until clangIndex is modified to handle those.\r
+# no-CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 0, "character": 10}, "end": {"line": 0, "character": 34}}}]}\r
+\r
+Content-Length: 256\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":7},"contentChanges":[{"text":"namespace ns {\nstruct Foo {\nstatic void bar() {}\n};\n}\nint main() {\n   ns::Foo::bar();\n   return 0;\n}\n"}]}}\r
+\r
+Content-Length: 148\r
+\r
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":6,"character":4}}}\r
+# Go to namespace, static method call \r
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 0, "character": 0}, "end": {"line": 4, "character": 1}}}]}\r
+\r
+Content-Length: 265\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":7},"contentChanges":[{"text":"namespace ns {\nstruct Foo {\n  int field;\n  Foo(int param) : field(param) {}\n};\n}\nint main() {\n   return 0;\n}\n"}]}}\r
+\r
+Content-Length: 149\r
+\r
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":3,"character":21}}}\r
+# Go to field, member initializer \r
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 2, "character": 2}, "end": {"line": 2, "character": 11}}}]}\r
+\r
+Content-Length: 204\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":7},"contentChanges":[{"text":"#define MY_MACRO 0\nint main() {\n  return MY_MACRO;\n}\n"}]}}\r
+\r
+Content-Length: 148\r
+\r
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":2,"character":9}}}\r
+# Go to macro\r
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 0, "character": 8}, "end": {"line": 0, "character": 18}}}]}\r
+\r
+Content-Length: 217\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":7},"contentChanges":[{"text":"#define FOO 1\nint a = FOO;\n#define FOO 2\nint b = FOO;\n#undef FOO\n"}]}}\r
+\r
+Content-Length: 148\r
+\r
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":1,"character":8}}}\r
+# Go to macro, re-defined later\r
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 0, "character": 8}, "end": {"line": 0, "character": 13}}}]}\r
+\r
+Content-Length: 148\r
+\r
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":3,"character":8}}}\r
+# Go to macro, undefined later\r
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 2, "character": 8}, "end": {"line": 2, "character": 13}}}]}\r
+\r
+Content-Length: 148\r
+\r
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":4,"character":7}}}\r
+# Go to macro, being undefined\r
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"uri": "file:///main.cpp", "range": {"start": {"line": 2, "character": 8}, "end": {"line": 2, "character": 13}}}]}\r
+\r
+Content-Length: 44\r
+\r
+{"jsonrpc":"2.0","id":3,"method":"shutdown"}\r
diff --git a/test/clangd/diagnostics.test b/test/clangd/diagnostics.test
new file mode 100644 (file)
index 0000000..f5ef394
--- /dev/null
@@ -0,0 +1,17 @@
+# RUN: clangd -run-synchronously < %s | FileCheck %s\r
+# It is absolutely vital that this file has CRLF line endings.\r
+#\r
+Content-Length: 125\r
+\r
+{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}}\r
+#\r
+Content-Length: 152\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file:///foo.c","languageId":"c","version":1,"text":"void main() {}"}}}\r
+#\r
+# CHECK: {"jsonrpc":"2.0","method":"textDocument/publishDiagnostics","params":{"uri":"file:///foo.c","diagnostics":[{"range":{"start": {"line": 0, "character": 1}, "end": {"line": 0, "character": 1}},"severity":2,"message":"return type of 'main' is not 'int'"},{"range":{"start": {"line": 0, "character": 1}, "end": {"line": 0, "character": 1}},"severity":3,"message":"change return type to 'int'"}]}}\r
+#\r
+#\r
+Content-Length: 44\r
+\r
+{"jsonrpc":"2.0","id":5,"method":"shutdown"}\r
diff --git a/test/clangd/extra-flags.test b/test/clangd/extra-flags.test
new file mode 100644 (file)
index 0000000..4e5fcc9
--- /dev/null
@@ -0,0 +1,22 @@
+# RUN: clangd -run-synchronously < %s | FileCheck %s\r
+# It is absolutely vital that this file has CRLF line endings.\r
+#\r
+Content-Length: 125\r
+\r
+{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}}\r
+#\r
+Content-Length: 205\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file:///foo.c","languageId":"c","version":1,"text":"int main() { int i; return i; }"},"metadata":{"extraFlags":["-Wall"]}}}\r
+# CHECK: {"jsonrpc":"2.0","method":"textDocument/publishDiagnostics","params":{"uri":"file:///foo.c","diagnostics":[{"range":{"start": {"line": 0, "character": 28}, "end": {"line": 0, "character": 28}},"severity":2,"message":"variable 'i' is uninitialized when used here"},{"range":{"start": {"line": 0, "character": 19}, "end": {"line": 0, "character": 19}},"severity":3,"message":"initialize the variable 'i' to silence this warning"}]}}\r
+#\r
+Content-Length: 175\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///foo.c","version":2},"contentChanges":[{"text":"int main() { int i; return i; }"}]}}\r
+# CHECK: {"jsonrpc":"2.0","method":"textDocument/publishDiagnostics","params":{"uri":"file:///foo.c","diagnostics":[{"range":{"start": {"line": 0, "character": 28}, "end": {"line": 0, "character": 28}},"severity":2,"message":"variable 'i' is uninitialized when used here"},{"range":{"start": {"line": 0, "character": 19}, "end": {"line": 0, "character": 19}},"severity":3,"message":"initialize the variable 'i' to silence this warning"}]}}\r
+#\r
+Content-Length: 44\r
+\r
+{"jsonrpc":"2.0","id":5,"method":"shutdown"}\r
+\r
+\r
diff --git a/test/clangd/fixits.test b/test/clangd/fixits.test
new file mode 100644 (file)
index 0000000..7272c3b
--- /dev/null
@@ -0,0 +1,22 @@
+# RUN: clangd -run-synchronously < %s | FileCheck %s\r
+# It is absolutely vital that this file has CRLF line endings.\r
+#\r
+Content-Length: 125\r
+\r
+{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}}\r
+#\r
+Content-Length: 180\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file:///foo.c","languageId":"c","version":1,"text":"int main(int i, char **a) { if (i = 2) {}}"}}}\r
+#\r
+# CHECK: {"jsonrpc":"2.0","method":"textDocument/publishDiagnostics","params":{"uri":"file:///foo.c","diagnostics":[{"range":{"start": {"line": 0, "character": 35}, "end": {"line": 0, "character": 35}},"severity":2,"message":"using the result of an assignment as a condition without parentheses"},{"range":{"start": {"line": 0, "character": 35}, "end": {"line": 0, "character": 35}},"severity":3,"message":"place parentheses around the assignment to silence this warning"},{"range":{"start": {"line": 0, "character": 35}, "end": {"line": 0, "character": 35}},"severity":3,"message":"use '==' to turn this assignment into an equality comparison"}]}}\r
+#\r
+Content-Length: 746\r
+\r
+ {"jsonrpc":"2.0","id":2,"method":"textDocument/codeAction","params":{"textDocument":{"uri":"file:///foo.c"},"range":{"start":{"line":104,"character":13},"end":{"line":0,"character":35}},"context":{"diagnostics":[{"range":{"start": {"line": 0, "character": 35}, "end": {"line": 0, "character": 35}},"severity":2,"message":"using the result of an assignment as a condition without parentheses"},{"range":{"start": {"line": 0, "character": 35}, "end": {"line": 0, "character": 35}},"severity":3,"message":"place parentheses around the assignment to silence this warning"},{"range":{"start": {"line": 0, "character": 35}, "end": {"line": 0, "character": 35}},"severity":3,"message":"use '==' to turn this assignment into an equality comparison"}]}}}\r
+#\r
+# CHECK: {"jsonrpc":"2.0","id":2, "result": [{"title":"Apply FixIt 'place parentheses around the assignment to silence this warning'", "command": "clangd.applyFix", "arguments": ["file:///foo.c", [{"range": {"start": {"line": 0, "character": 32}, "end": {"line": 0, "character": 32}}, "newText": "("},{"range": {"start": {"line": 0, "character": 37}, "end": {"line": 0, "character": 37}}, "newText": ")"}]]},{"title":"Apply FixIt 'use '==' to turn this assignment into an equality comparison'", "command": "clangd.applyFix", "arguments": ["file:///foo.c", [{"range": {"start": {"line": 0, "character": 34}, "end": {"line": 0, "character": 35}}, "newText": "=="}]]}]\r
+#\r
+Content-Length: 44\r
+\r
+{"jsonrpc":"2.0","id":3,"method":"shutdown"}\r
diff --git a/test/clangd/formatting.test b/test/clangd/formatting.test
new file mode 100644 (file)
index 0000000..08be629
--- /dev/null
@@ -0,0 +1,68 @@
+# RUN: clangd < %s | FileCheck %s\r
+# It is absolutely vital that this file has CRLF line endings.\r
+#\r
+Content-Length: 125\r
+\r
+{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}}\r
+# CHECK: Content-Length: 462\r
+# CHECK: {"jsonrpc":"2.0","id":0,"result":{"capabilities":{\r
+# CHECK:   "textDocumentSync": 1,\r
+# CHECK:   "documentFormattingProvider": true,\r
+# CHECK:   "documentRangeFormattingProvider": true,\r
+# CHECK:   "documentOnTypeFormattingProvider": {"firstTriggerCharacter":"}","moreTriggerCharacter":[]},\r
+# CHECK:   "codeActionProvider": true,\r
+# CHECK:   "completionProvider": {"resolveProvider": false, "triggerCharacters": [".",">"]},\r
+# CHECK:   "definitionProvider": true\r
+# CHECK: }}}\r
+#\r
+Content-Length: 193\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file:///foo.c","languageId":"c","version":1,"text":"int foo ( int x ) {\n    x = x+1;\n    return x;\n    }"}}}\r
+#\r
+#\r
+Content-Length: 233\r
+\r
+{"jsonrpc":"2.0","id":1,"method":"textDocument/rangeFormatting","params":{"textDocument":{"uri":"file:///foo.c"},"range":{"start":{"line":1,"character":4},"end":{"line":1,"character":12}},"options":{"tabSize":4,"insertSpaces":true}}}\r
+# CHECK: {"jsonrpc":"2.0","id":1,"result":[{"range": {"start": {"line": 0, "character": 19}, "end": {"line": 1, "character": 4}}, "newText": "\n  "},{"range": {"start": {"line": 1, "character": 9}, "end": {"line": 1, "character": 9}}, "newText": " "},{"range": {"start": {"line": 1, "character": 10}, "end": {"line": 1, "character": 10}}, "newText": " "},{"range": {"start": {"line": 1, "character": 12}, "end": {"line": 2, "character": 4}}, "newText": "\n  "}]}\r
+#\r
+#\r
+Content-Length: 197\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///foo.c","version":5},"contentChanges":[{"text":"int foo ( int x ) {\n  x = x + 1;\n  return x;\n    }"}]}}\r
+#\r
+#\r
+Content-Length: 233\r
+\r
+{"jsonrpc":"2.0","id":2,"method":"textDocument/rangeFormatting","params":{"textDocument":{"uri":"file:///foo.c"},"range":{"start":{"line":1,"character":2},"end":{"line":1,"character":12}},"options":{"tabSize":4,"insertSpaces":true}}}\r
+# CHECK: {"jsonrpc":"2.0","id":2,"result":[]}\r
+#\r
+Content-Length: 153\r
+\r
+{"jsonrpc":"2.0","id":3,"method":"textDocument/formatting","params":{"textDocument":{"uri":"file:///foo.c"},"options":{"tabSize":4,"insertSpaces":true}}}\r
+# CHECK: {"jsonrpc":"2.0","id":3,"result":[{"range": {"start": {"line": 0, "character": 7}, "end": {"line": 0, "character": 8}}, "newText": ""},{"range": {"start": {"line": 0, "character": 9}, "end": {"line": 0, "character": 10}}, "newText": ""},{"range": {"start": {"line": 0, "character": 15}, "end": {"line": 0, "character": 16}}, "newText": ""},{"range": {"start": {"line": 2, "character": 11}, "end": {"line": 3, "character": 4}}, "newText": "\n"}]}\r
+#\r
+#\r
+Content-Length: 190\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///foo.c","version":9},"contentChanges":[{"text":"int foo(int x) {\n  x = x + 1;\n  return x;\n}"}]}}\r
+#\r
+#\r
+Content-Length: 153\r
+\r
+{"jsonrpc":"2.0","id":4,"method":"textDocument/formatting","params":{"textDocument":{"uri":"file:///foo.c"},"options":{"tabSize":4,"insertSpaces":true}}}\r
+# CHECK: {"jsonrpc":"2.0","id":4,"result":[]}\r
+#\r
+Content-Length: 193\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///foo.c","version":5},"contentChanges":[{"text":"int foo ( int x ) {\n  x = x + 1;\n  return x;\n}"}]}}\r
+#\r
+#\r
+Content-Length: 204\r
+\r
+{"jsonrpc":"2.0","id":5,"method":"textDocument/onTypeFormatting","params":{"textDocument":{"uri":"file:///foo.c"},"position":{"line":3,"character":1},"ch":"}","options":{"tabSize":4,"insertSpaces":true}}}\r
+# CHECK: {"jsonrpc":"2.0","id":5,"result":[{"range": {"start": {"line": 0, "character": 7}, "end": {"line": 0, "character": 8}}, "newText": ""},{"range": {"start": {"line": 0, "character": 9}, "end": {"line": 0, "character": 10}}, "newText": ""},{"range": {"start": {"line": 0, "character": 15}, "end": {"line": 0, "character": 16}}, "newText": ""}]}\r
+#\r
+\r
+Content-Length: 44\r
+\r
+{"jsonrpc":"2.0","id":6,"method":"shutdown"}\r
diff --git a/test/include-fixer/Inputs/database_template.json b/test/include-fixer/Inputs/database_template.json
new file mode 100644 (file)
index 0000000..ec71c56
--- /dev/null
@@ -0,0 +1,7 @@
+[
+{
+  "directory": "test_dir/build",
+  "command": "clang++ -I../include -o bar.o test_dir/src/bar.cpp",
+  "file": "test_dir/src/bar.cpp"
+}
+]
diff --git a/test/include-fixer/Inputs/fake_yaml_db.yaml b/test/include-fixer/Inputs/fake_yaml_db.yaml
new file mode 100644 (file)
index 0000000..b599005
--- /dev/null
@@ -0,0 +1,71 @@
+---
+Name:           foo
+Contexts:
+  - ContextType:     Namespace
+    ContextName:     a
+  - ContextType:     Namespace
+    ContextName:     b
+FilePath:        foo.h
+Type:            Class
+Seen:            1
+Used:            0
+---
+Name:           foo_bar
+Contexts:
+  - ContextType:     Namespace
+    ContextName:     a
+  - ContextType:     Namespace
+    ContextName:     b
+FilePath:        foobar.h
+Type:            Class
+Seen:            0
+Used:            0
+---
+Name:           bar
+Contexts:
+  - ContextType:     Namespace
+    ContextName:     a
+  - ContextType:     Namespace
+    ContextName:     b
+FilePath:        ../include/bar.h
+Type:            Class
+Seen:            1
+Used:            0
+---
+Name:           bar
+Contexts:
+  - ContextType:     Namespace
+    ContextName:     a
+  - ContextType:     Namespace
+    ContextName:     b
+FilePath:        ../include/bar.h
+Type:            Class
+Seen:            3
+Used:            0
+---
+Name:           bar
+Contexts:
+  - ContextType:     Namespace
+    ContextName:     a
+  - ContextType:     Namespace
+    ContextName:     b
+FilePath:        ../include/zbar.h
+Type:            Class
+Seen:            3
+Used:            0
+---
+Name:           b
+Contexts:
+FilePath:        var.h
+Type:            Variable
+Seen:            1
+Used:            0
+---
+Name:            bar
+Contexts:
+  - ContextType:    Namespace
+    ContextName:    c
+FilePath:        test/include-fixer/baz.h
+Type:            Class
+Seen:            1
+Used:            0
diff --git a/test/include-fixer/Inputs/merge/a.yaml b/test/include-fixer/Inputs/merge/a.yaml
new file mode 100644 (file)
index 0000000..174db0e
--- /dev/null
@@ -0,0 +1,20 @@
+---
+Name:           foo
+Contexts:
+  - ContextType:     Namespace
+    ContextName:     a
+FilePath:        foo.h
+Type:            Class
+Seen:            1
+Used:            1
+...
+---
+Name:           bar
+Contexts:
+  - ContextType:     Namespace
+    ContextName:     a
+FilePath:        ../include/bar.h
+Type:            Class
+Seen:            1
+Used:            2
+...
diff --git a/test/include-fixer/Inputs/merge/b.yaml b/test/include-fixer/Inputs/merge/b.yaml
new file mode 100644 (file)
index 0000000..8689320
--- /dev/null
@@ -0,0 +1,20 @@
+---
+Name:           foo
+Contexts:
+  - ContextType:     Namespace
+    ContextName:     a
+FilePath:        foo.h
+Type:            Class
+Seen:            1
+Used:            2
+...
+---
+Name:           bar
+Contexts:
+  - ContextType:     Namespace
+    ContextName:     a
+FilePath:        ../include/barbar.h
+Type:            Class
+Seen:            1
+Used:            0
+...
diff --git a/test/include-fixer/commandline_options.cpp b/test/include-fixer/commandline_options.cpp
new file mode 100644 (file)
index 0000000..3cc77b4
--- /dev/null
@@ -0,0 +1,15 @@
+// RUN: echo "foo f;" > %t.cpp
+// RUN: clang-include-fixer -db=fixed -input='foo= "foo.h","bar.h"' -output-headers %t.cpp -- | FileCheck %s
+// RUN: cat %t.cpp | clang-include-fixer -stdin -insert-header='{FilePath: "%/t.cpp", QuerySymbolInfos: [{RawIdentifier: foo, Range: {Offset: 0, Length: 3}}], HeaderInfos: [{Header: "\"foo.h\"", QualifiedName: "foo"}]}' %t.cpp | FileCheck %s -check-prefix=CHECK-CODE
+// RUN: cat %t.cpp | not clang-include-fixer -stdin -insert-header='{FilePath: "%/t.cpp", QuerySymbolInfos: [{RawIdentifier: foo, Range: {Offset: 0, Length: 3}}], HeaderInfos: [{Header: "\"foo.h\"", QualifiedName: "foo"},{Header: "\"foo2.h\"", QualifiedName: "foo"}]}' %t.cpp
+// RUN: cat %t.cpp | clang-include-fixer -stdin -insert-header='{FilePath: "%/t.cpp", QuerySymbolInfos: [{RawIdentifier: foo, Range: {Offset: 0, Length: 3}}], HeaderInfos: [{Header: "\"foo.h\"", QualifiedName: "a:foo"},{Header: "\"foo.h\"", QualifiedName: "b:foo"}]}' %t.cpp
+//
+// CHECK:     "HeaderInfos": [
+// CHECK-NEXT:  {"Header": "\"foo.h\"",
+// CHECK-NEXT:   "QualifiedName": "foo"},
+// CHECK-NEXT:  {"Header": "\"bar.h\"",
+// CHECK-NEXT:   "QualifiedName": "foo"}
+// CHECK-NEXT:]
+//
+// CHECK-CODE: #include "foo.h"
+// CHECK-CODE: foo f;
diff --git a/test/include-fixer/exit_on_fatal.cpp b/test/include-fixer/exit_on_fatal.cpp
new file mode 100644 (file)
index 0000000..fc8c95a
--- /dev/null
@@ -0,0 +1,10 @@
+// RUN: sed -e 's#//.*$##' %s > %t.cpp
+// RUN: not clang-include-fixer -db=fixed -input='foo= "foo.h"' %t.cpp --
+// RUN: FileCheck %s -input-file=%t.cpp
+
+// CHECK-NOT: #include
+// CHECK: #include "doesnotexist.h"
+// CHECK-NEXT: foo f;
+
+#include "doesnotexist.h"
+foo f;
diff --git a/test/include-fixer/fixeddb.cpp b/test/include-fixer/fixeddb.cpp
new file mode 100644 (file)
index 0000000..90068db
--- /dev/null
@@ -0,0 +1,8 @@
+// RUN: sed -e 's#//.*$##' %s > %t.cpp
+// RUN: clang-include-fixer -db=fixed -input='foo= "foo.h","bar.h"' %t.cpp --
+// RUN: FileCheck %s -input-file=%t.cpp
+
+// CHECK: #include "foo.h"
+// CHECK: foo f;
+
+foo f;
diff --git a/test/include-fixer/include_path.cpp b/test/include-fixer/include_path.cpp
new file mode 100644 (file)
index 0000000..28b6ace
--- /dev/null
@@ -0,0 +1,19 @@
+// RUN: mkdir -p %T/include-fixer/include
+// RUN: mkdir -p %T/include-fixer/symbols
+// RUN: mkdir -p %T/include-fixer/build
+// RUN: mkdir -p %T/include-fixer/src
+// RUN: sed 's|test_dir|%/T/include-fixer|g' %S/Inputs/database_template.json > %T/include-fixer/build/compile_commands.json
+// RUN: echo -e '#include "bar.h"\nb::a::bar f;' > %T/include-fixer/src/bar.cpp
+// RUN: echo 'namespace b { namespace a { class bar {}; } }' > %T/include-fixer/include/bar.h
+// RUN: cd %T/include-fixer/build
+// RUN: find-all-symbols -output-dir=%T/include-fixer/symbols -p=. %T/include-fixer/src/bar.cpp
+// RUN: find-all-symbols -merge-dir=%T/include-fixer/symbols %T/include-fixer/build/find_all_symbols.yaml
+// RUN: FileCheck -input-file=%T/include-fixer/build/find_all_symbols.yaml -check-prefix=CHECK-YAML %s
+//
+// RUN: echo 'b::a::bar f;' > %T/include-fixer/src/bar.cpp
+// RUN: clang-include-fixer -db=yaml -input=%T/include-fixer/build/find_all_symbols.yaml -minimize-paths=true -p=. %T/include-fixer/src/bar.cpp
+// RUN: FileCheck -input-file=%T/include-fixer/src/bar.cpp %s
+
+// CHECK-YAML: ..{{[/\\]}}include{{[/\\]}}bar.h
+// CHECK: #include "bar.h"
+// CHECK: b::a::bar f;
diff --git a/test/include-fixer/merge.test b/test/include-fixer/merge.test
new file mode 100644 (file)
index 0000000..cee751b
--- /dev/null
@@ -0,0 +1,33 @@
+# RUN: find-all-symbols -merge-dir=%S/Inputs/merge %t.merged
+# RUN: sed '/^#/d' %s > %t.golden
+# RUN: diff -u %t.golden %t.merged
+---
+Name:            bar
+Contexts:        
+  - ContextType:     Namespace
+    ContextName:     a
+FilePath:        ../include/bar.h
+Type:            Class
+Seen:            1
+Used:            1
+...
+---
+Name:            bar
+Contexts:        
+  - ContextType:     Namespace
+    ContextName:     a
+FilePath:        ../include/barbar.h
+Type:            Class
+Seen:            1
+Used:            0
+...
+---
+Name:            foo
+Contexts:        
+  - ContextType:     Namespace
+    ContextName:     a
+FilePath:        foo.h
+Type:            Class
+Seen:            2
+Used:            2
+...
diff --git a/test/include-fixer/multiple_fixes.cpp b/test/include-fixer/multiple_fixes.cpp
new file mode 100644 (file)
index 0000000..35a73b4
--- /dev/null
@@ -0,0 +1,13 @@
+// REQUIRES: shell
+// RUN: sed -e 's#//.*$##' %s > %t.cpp
+// RUN: mkdir -p %T/include-fixer/multiple-fixes
+// RUN: echo 'foo f;' > %T/include-fixer/multiple-fixes/foo.cpp
+// RUN: echo 'bar b;' > %T/include-fixer/multiple-fixes/bar.cpp
+// RUN: clang-include-fixer -db=fixed -input='foo= "foo.h";bar= "bar.h"' %T/include-fixer/multiple-fixes/*.cpp --
+// RUN: FileCheck -input-file=%T/include-fixer/multiple-fixes/bar.cpp %s -check-prefix=CHECK-BAR
+// RUN: FileCheck -input-file=%T/include-fixer/multiple-fixes/foo.cpp %s -check-prefix=CHECK-FOO
+//
+// CHECK-FOO: #include "foo.h"
+// CHECK-FOO: foo f;
+// CHECK-BAR: #include "bar.h"
+// CHECK-BAR: bar b;
diff --git a/test/include-fixer/prefix_variable.cpp b/test/include-fixer/prefix_variable.cpp
new file mode 100644 (file)
index 0000000..b39cdb8
--- /dev/null
@@ -0,0 +1,10 @@
+// RUN: sed -e 's#//.*$##' %s > %t.cpp
+// RUN: clang-include-fixer -db=yaml -input=%p/Inputs/fake_yaml_db.yaml %t.cpp --
+// RUN: FileCheck %s -input-file=%t.cpp
+
+// CHECK-NOT: #include
+// CHECK: doesnotexist f;
+
+namespace b {
+doesnotexist f;
+}
diff --git a/test/include-fixer/query_symbol.cpp b/test/include-fixer/query_symbol.cpp
new file mode 100644 (file)
index 0000000..84c3a23
--- /dev/null
@@ -0,0 +1,13 @@
+// RUN: clang-include-fixer -db=fixed -input='foo= "foo.h","bar.h"' -query-symbol="foo" test.cpp -- | FileCheck %s
+
+// CHECK:     "FilePath": "test.cpp",
+// CHECK-NEXT:"QuerySymbolInfos": [
+// CHECK-NEXT:   {"RawIdentifier": "foo",
+// CHECK-NEXT:    "Range":{"Offset":0,"Length":0}}
+// CHECK-NEXT:],
+// CHECK-NEXT:"HeaderInfos": [
+// CHECK-NEXT:  {"Header": "\"foo.h\"",
+// CHECK-NEXT:   "QualifiedName": "foo"},
+// CHECK-NEXT:  {"Header": "\"bar.h\"",
+// CHECK-NEXT:   "QualifiedName": "foo"}
+// CHECK-NEXT:]
diff --git a/test/include-fixer/ranking.cpp b/test/include-fixer/ranking.cpp
new file mode 100644 (file)
index 0000000..2dabe16
--- /dev/null
@@ -0,0 +1,13 @@
+// RUN: clang-include-fixer -db=yaml -input=%S/Inputs/fake_yaml_db.yaml -output-headers %s -- | FileCheck %s
+// RUN: clang-include-fixer -query-symbol bar -db=yaml -input=%S/Inputs/fake_yaml_db.yaml -output-headers %s -- | FileCheck %s
+
+// CHECK:     "HeaderInfos": [
+// CHECK-NEXT:  {"Header": "\"test/include-fixer/baz.h\"",
+// CHECK-NEXT:   "QualifiedName": "c::bar"},
+// CHECK-NEXT:  {"Header": "\"../include/bar.h\"",
+// CHECK-NEXT:   "QualifiedName": "b::a::bar"},
+// CHECK-NEXT:  {"Header": "\"../include/zbar.h\"",
+// CHECK-NEXT:   "QualifiedName": "b::a::bar"}
+// CHECK-NEXT:]
+
+bar b;
diff --git a/test/include-fixer/yaml_fuzzy.cpp b/test/include-fixer/yaml_fuzzy.cpp
new file mode 100644 (file)
index 0000000..705542d
--- /dev/null
@@ -0,0 +1,9 @@
+// RUN: sed -e 's#//.*$##' %s > %t.cpp
+// RUN: clang-include-fixer -db=fuzzyYaml -input=%p/Inputs/fake_yaml_db.yaml %t.cpp --
+// RUN: FileCheck %s -input-file=%t.cpp
+
+// include-fixer will add the include, but doesn't complete the symbol.
+// CHECK: #include "foobar.h"
+// CHECK: fba f;
+
+b::a::fba f;
diff --git a/test/include-fixer/yamldb.cpp b/test/include-fixer/yamldb.cpp
new file mode 100644 (file)
index 0000000..d17b414
--- /dev/null
@@ -0,0 +1,8 @@
+// RUN: sed -e 's#//.*$##' %s > %t.cpp
+// RUN: clang-include-fixer -db=yaml -input=%p/Inputs/fake_yaml_db.yaml %t.cpp --
+// RUN: FileCheck %s -input-file=%t.cpp
+
+// CHECK: #include "foo.h"
+// CHECK: b::a::foo f;
+
+b::a::foo f;
diff --git a/test/include-fixer/yamldb_autodetect.cpp b/test/include-fixer/yamldb_autodetect.cpp
new file mode 100644 (file)
index 0000000..1997390
--- /dev/null
@@ -0,0 +1,11 @@
+// RUN: mkdir -p %T/foo/bar
+// RUN: cp %p/Inputs/fake_yaml_db.yaml %T/find_all_symbols_db.yaml
+// RUN: cd %T/foo
+// RUN: sed -e 's#//.*$##' %s > bar/test.cpp
+// RUN: clang-include-fixer -db=yaml bar/test.cpp --
+// RUN: FileCheck %s -input-file=bar/test.cpp
+
+// CHECK: #include "foo.h"
+// CHECK: b::a::foo f;
+
+b::a::foo f;
diff --git a/test/include-fixer/yamldb_plugin.cpp b/test/include-fixer/yamldb_plugin.cpp
new file mode 100644 (file)
index 0000000..8de643e
--- /dev/null
@@ -0,0 +1,24 @@
+// RUN: c-index-test -test-load-source-reparse 2 all %s -Xclang -add-plugin -Xclang clang-include-fixer -fspell-checking -Xclang -plugin-arg-clang-include-fixer -Xclang -input=%p/Inputs/fake_yaml_db.yaml 2>&1 | FileCheck %s
+
+foo f;
+foo g;
+unknown u;
+
+// CHECK: yamldb_plugin.cpp:3:1: error: unknown type name 'foo'; did you mean 'foo'?
+// CHECK: Number FIX-ITs = 1
+// CHECK: FIX-IT: Replace [3:1 - 3:4] with "foo"
+// CHECK: yamldb_plugin.cpp:3:1: note: Add '#include "foo.h"' to provide the missing declaration [clang-include-fixer]
+// CHECK: Number FIX-ITs = 1
+// CHECK: FIX-IT: Insert "#include "foo.h"
+// CHECK: yamldb_plugin.cpp:4:1: error: unknown type name 'foo'; did you mean 'foo'?
+// CHECK: Number FIX-ITs = 1
+// CHECK: FIX-IT: Replace [4:1 - 4:4] with "foo"
+// CHECK: yamldb_plugin.cpp:4:1: note: Add '#include "foo.h"' to provide the missing declaration [clang-include-fixer]
+// CHECK: Number FIX-ITs = 1
+// CHECK: FIX-IT: Insert "#include "foo.h"
+// CHECK: " at 3:1
+// CHECK: yamldb_plugin.cpp:5:1:
+// CHECK: error: unknown type name 'unknown'
+// CHECK: Number FIX-ITs = 0
+// CHECK-NOT: error
+// CHECK-NOT: FIX-IT
diff --git a/test/lit.cfg b/test/lit.cfg
new file mode 100644 (file)
index 0000000..bb59293
--- /dev/null
@@ -0,0 +1,201 @@
+# -*- Python -*-
+
+import os
+import platform
+import re
+import subprocess
+
+import lit.formats
+import lit.util
+
+# Configuration file for the 'lit' test runner.
+
+# name: The name of this test suite.
+config.name = 'Clang Tools'
+
+# Tweak PATH for Win32
+if platform.system() == 'Windows':
+    # Seek sane tools in directories and set to $PATH.
+    path = getattr(config, 'lit_tools_dir', None)
+    path = lit_config.getToolsPath(path,
+                                   config.environment['PATH'],
+                                   ['cmp.exe', 'grep.exe', 'sed.exe'])
+    if path is not None:
+        path = os.path.pathsep.join((path,
+                                     config.environment['PATH']))
+        config.environment['PATH'] = path
+
+# Choose between lit's internal shell pipeline runner and a real shell.  If
+# LIT_USE_INTERNAL_SHELL is in the environment, we use that as an override.
+use_lit_shell = os.environ.get("LIT_USE_INTERNAL_SHELL")
+if use_lit_shell:
+    # 0 is external, "" is default, and everything else is internal.
+    execute_external = (use_lit_shell == "0")
+else:
+    # Otherwise we default to internal on Windows and external elsewhere, as
+    # bash on Windows is usually very slow.
+    execute_external = (not sys.platform in ['win32'])
+
+# testFormat: The test format to use to interpret tests.
+#
+# For now we require '&&' between commands, until they get globally killed and
+# the test runner updated.
+config.test_format = lit.formats.ShTest(execute_external)
+
+# suffixes: A list of file extensions to treat as test files.
+config.suffixes = ['.c', '.cpp', '.hpp', '.m', '.mm', '.cu', '.ll', '.cl', '.s',
+  '.modularize', '.module-map-checker', '.test']
+
+# Test-time dependencies located in directories called 'Inputs' are excluded
+# from test suites; there won't be any lit tests within them.
+config.excludes = ['Inputs']
+
+# test_source_root: The root path where tests are located.
+config.test_source_root = os.path.dirname(__file__)
+
+# test_exec_root: The root path where tests should be run.
+clang_tools_binary_dir = getattr(config, 'clang_tools_binary_dir', None)
+if clang_tools_binary_dir is not None:
+    config.test_exec_root = os.path.join(clang_tools_binary_dir, 'test')
+
+# Clear some environment variables that might affect Clang.
+#
+# This first set of vars are read by Clang, but shouldn't affect tests
+# that aren't specifically looking for these features, or are required
+# simply to run the tests at all.
+#
+# FIXME: Should we have a tool that enforces this?
+
+# safe_env_vars = ('TMPDIR', 'TEMP', 'TMP', 'USERPROFILE', 'PWD',
+#                  'MACOSX_DEPLOYMENT_TARGET', 'IPHONEOS_DEPLOYMENT_TARGET',
+#                  'IOS_SIMULATOR_DEPLOYMENT_TARGET',
+#                  'VCINSTALLDIR', 'VC100COMNTOOLS', 'VC90COMNTOOLS',
+#                  'VC80COMNTOOLS')
+possibly_dangerous_env_vars = ['COMPILER_PATH', 'RC_DEBUG_OPTIONS',
+                               'CINDEXTEST_PREAMBLE_FILE', 'LIBRARY_PATH',
+                               'CPATH', 'C_INCLUDE_PATH', 'CPLUS_INCLUDE_PATH',
+                               'OBJC_INCLUDE_PATH', 'OBJCPLUS_INCLUDE_PATH',
+                               'LIBCLANG_TIMING', 'LIBCLANG_OBJTRACKING',
+                               'LIBCLANG_LOGGING', 'LIBCLANG_BGPRIO_INDEX',
+                               'LIBCLANG_BGPRIO_EDIT', 'LIBCLANG_NOTHREADS',
+                               'LIBCLANG_RESOURCE_USAGE',
+                               'LIBCLANG_CODE_COMPLETION_LOGGING']
+# Clang/Win32 may refer to %INCLUDE%. vsvarsall.bat sets it.
+if platform.system() != 'Windows':
+    possibly_dangerous_env_vars.append('INCLUDE')
+for name in possibly_dangerous_env_vars:
+  if name in config.environment:
+    del config.environment[name]
+
+# Tweak the PATH to include the tools dir and the scripts dir.
+if clang_tools_binary_dir is not None:
+    clang_tools_dir = getattr(config, 'clang_tools_dir', None)
+    if not clang_tools_dir:
+        lit_config.fatal('No Clang tools dir set!')
+    llvm_tools_dir = getattr(config, 'llvm_tools_dir', None)
+    if not llvm_tools_dir:
+        lit_config.fatal('No LLVM tools dir set!')
+    path = os.path.pathsep.join((
+            clang_tools_dir, llvm_tools_dir, config.environment['PATH']))
+    config.environment['PATH'] = path
+
+    llvm_libs_dir = getattr(config, 'llvm_libs_dir', None)
+    if not llvm_libs_dir:
+        lit_config.fatal('No LLVM libs dir set!')
+    path = os.path.pathsep.join((llvm_libs_dir,
+                                 config.environment.get('LD_LIBRARY_PATH','')))
+    config.environment['LD_LIBRARY_PATH'] = path
+
+###
+
+# Check that the object root is known.
+if config.test_exec_root is None:
+    # Otherwise, we haven't loaded the site specific configuration (the user is
+    # probably trying to run on a test file directly, and either the site
+    # configuration hasn't been created by the build system, or we are in an
+    # out-of-tree build situation).
+
+    # Check for 'clang_site_config' user parameter, and use that if available.
+    site_cfg = lit_config.params.get('clang_tools_extra_site_config', None)
+    if site_cfg and os.path.exists(site_cfg):
+        lit_config.load_config(config, site_cfg)
+        raise SystemExit
+
+    # Try to detect the situation where we are using an out-of-tree build by
+    # looking for 'llvm-config'.
+    #
+    # FIXME: I debated (i.e., wrote and threw away) adding logic to
+    # automagically generate the lit.site.cfg if we are in some kind of fresh
+    # build situation. This means knowing how to invoke the build system though,
+    # and I decided it was too much magic. We should solve this by just having
+    # the .cfg files generated during the configuration step.
+
+    llvm_config = lit.util.which('llvm-config', config.environment['PATH'])
+    if not llvm_config:
+        lit_config.fatal('No site specific configuration available!')
+
+    # Get the source and object roots.
+    llvm_src_root = lit.util.capture(['llvm-config', '--src-root']).strip()
+    llvm_obj_root = lit.util.capture(['llvm-config', '--obj-root']).strip()
+    clang_src_root = os.path.join(llvm_src_root, "tools", "clang")
+    clang_obj_root = os.path.join(llvm_obj_root, "tools", "clang")
+
+    clang_tools_extra_src_root = os.path.join(clang_src_root, "tools", "extra")
+    clang_tools_extra_obj_root = os.path.join(clang_obj_root, "tools", "extra")
+    # Validate that we got a tree which points to here, using the standard
+    # tools/clang layout.
+    this_src_root = os.path.dirname(config.test_source_root)
+    if os.path.realpath(clang_tools_extra_src_root) != os.path.realpath(this_src_root):
+        lit_config.fatal('No site specific configuration available!')
+
+    # Check that the site specific configuration exists.
+    site_cfg = os.path.join(clang_tools_extra_obj_root, 'test', 'lit.site.cfg')
+    if not os.path.exists(site_cfg):
+        lit_config.fatal(
+            'No site specific configuration available! You may need to '
+            'run "make test" in your Clang build directory.')
+
+    # Okay, that worked. Notify the user of the automagic, and reconfigure.
+    lit_config.note('using out-of-tree build at %r' % clang_obj_root)
+    lit_config.load_config(config, site_cfg)
+    raise SystemExit
+
+###
+
+import os
+
+# When running under valgrind, we mangle '-vg' onto the end of the triple so we
+# can check it with XFAIL and XTARGET.
+if lit_config.useValgrind:
+    config.target_triple += '-vg'
+
+###
+
+# Set available features we allow tests to conditionalize on.
+#
+# As of 2011.08, crash-recovery tests still do not pass on FreeBSD.
+if platform.system() not in ['FreeBSD']:
+    config.available_features.add('crash-recovery')
+
+# Shell execution
+if execute_external:
+    config.available_features.add('shell')
+
+# Exclude MSYS due to transforming '/' to 'X:/mingwroot/'.
+if not platform.system() in ['Windows'] or not execute_external:
+    config.available_features.add('shell-preserves-root')
+
+# ANSI escape sequences in non-dumb terminal
+if platform.system() not in ['Windows']:
+    config.available_features.add('ansi-escape-sequences')
+
+check_clang_tidy = os.path.join(
+    config.test_source_root, "clang-tidy", "check_clang_tidy.py")
+config.substitutions.append(
+    ('%check_clang_tidy',
+     '%s %s' % (config.python_executable, check_clang_tidy)) )
+clang_tidy_diff = os.path.join(
+    config.test_source_root, "..", "clang-tidy", "tool", "clang-tidy-diff.py")
+config.substitutions.append(
+    ('%clang_tidy_diff',
+     '%s %s' % (config.python_executable, clang_tidy_diff)) )
diff --git a/test/lit.site.cfg.in b/test/lit.site.cfg.in
new file mode 100644 (file)
index 0000000..dfd0164
--- /dev/null
@@ -0,0 +1,25 @@
+@LIT_SITE_CFG_IN_HEADER@
+
+import sys
+
+config.llvm_tools_dir = "@LLVM_TOOLS_DIR@"
+config.llvm_libs_dir = "@LLVM_LIBS_DIR@"
+config.lit_tools_dir = "@LLVM_LIT_TOOLS_DIR@"
+config.clang_tools_binary_dir = "@CLANG_TOOLS_BINARY_DIR@"
+config.clang_tools_dir = "@CLANG_TOOLS_DIR@"
+config.python_executable = "@PYTHON_EXECUTABLE@"
+config.target_triple = "@TARGET_TRIPLE@"
+
+# Support substitution of the tools and libs dirs with user parameters. This is
+# used when we can't determine the tool dir at configuration time.
+try:
+    config.clang_tools_dir = config.clang_tools_dir % lit_config.params
+    config.llvm_tools_dir = config.llvm_tools_dir % lit_config.params
+    config.llvm_libs_dir = config.llvm_libs_dir % lit_config.params
+except KeyError:
+    e = sys.exc_info()[1]
+    key, = e.args
+    lit_config.fatal("unable to find %r parameter, use '--param=%s=VALUE'" % (key,key))
+
+# Let the main config do the real work.
+lit_config.load_config(config, "@CLANG_TOOLS_SOURCE_DIR@/test/lit.cfg")
diff --git a/test/modularize/Inputs/Anonymous.h b/test/modularize/Inputs/Anonymous.h
new file mode 100644 (file)
index 0000000..8388eca
--- /dev/null
@@ -0,0 +1,11 @@
+// Exercise some anonymous type issues.
+
+// Anonymous enum.
+enum {
+  Tag1
+};
+
+// Anonymous enum typedef.
+typedef enum {
+  Tag2
+} AnonymousEnum;
diff --git a/test/modularize/Inputs/CompileError/HasError.h b/test/modularize/Inputs/CompileError/HasError.h
new file mode 100644 (file)
index 0000000..4395cb1
--- /dev/null
@@ -0,0 +1,2 @@
+typedef WithoutDep BadType;
+
diff --git a/test/modularize/Inputs/CompileError/Level1A.h b/test/modularize/Inputs/CompileError/Level1A.h
new file mode 100644 (file)
index 0000000..10eef67
--- /dev/null
@@ -0,0 +1 @@
+#define MACRO_1A 1
diff --git a/test/modularize/Inputs/CompileError/module.modulemap b/test/modularize/Inputs/CompileError/module.modulemap
new file mode 100644 (file)
index 0000000..fbb8e7d
--- /dev/null
@@ -0,0 +1,10 @@
+// module.map\r
+\r
+module Level1A {\r
+  header "Level1A.h"\r
+  export *\r
+}\r
+module HasError {\r
+  header "HasError.h"\r
+  export *\r
+}\r
diff --git a/test/modularize/Inputs/CoverageNoProblems/Includes1/.hidden/DontFindMe.h b/test/modularize/Inputs/CoverageNoProblems/Includes1/.hidden/DontFindMe.h
new file mode 100644 (file)
index 0000000..f60b66c
--- /dev/null
@@ -0,0 +1,3 @@
+#error DontFindMe.h shouldn't be found.
+
+
diff --git a/test/modularize/Inputs/CoverageNoProblems/Includes1/Level1A.h b/test/modularize/Inputs/CoverageNoProblems/Includes1/Level1A.h
new file mode 100644 (file)
index 0000000..10eef67
--- /dev/null
@@ -0,0 +1 @@
+#define MACRO_1A 1
diff --git a/test/modularize/Inputs/CoverageNoProblems/Includes2/Level2A.h b/test/modularize/Inputs/CoverageNoProblems/Includes2/Level2A.h
new file mode 100644 (file)
index 0000000..9a355df
--- /dev/null
@@ -0,0 +1 @@
+#define MACRO_2A 1
diff --git a/test/modularize/Inputs/CoverageNoProblems/NonIncludes/Level3A.h b/test/modularize/Inputs/CoverageNoProblems/NonIncludes/Level3A.h
new file mode 100644 (file)
index 0000000..594e284
--- /dev/null
@@ -0,0 +1 @@
+#define MACRO_3A 1
diff --git a/test/modularize/Inputs/CoverageNoProblems/module.modulemap b/test/modularize/Inputs/CoverageNoProblems/module.modulemap
new file mode 100644 (file)
index 0000000..d2b0957
--- /dev/null
@@ -0,0 +1,10 @@
+// module.map
+
+module Level1A {
+  header "Includes1/Level1A.h"
+  export *
+}
+module Level2A {
+  header "Includes2/Level2A.h"
+  export *
+}
diff --git a/test/modularize/Inputs/CoverageProblems/Level1A.h b/test/modularize/Inputs/CoverageProblems/Level1A.h
new file mode 100644 (file)
index 0000000..165efc7
--- /dev/null
@@ -0,0 +1,2 @@
+#include "Level2A.h"
+#define MACRO_1A 1
diff --git a/test/modularize/Inputs/CoverageProblems/Level1B.h b/test/modularize/Inputs/CoverageProblems/Level1B.h
new file mode 100644 (file)
index 0000000..1e60798
--- /dev/null
@@ -0,0 +1,2 @@
+#include "Level2B.h"
+#define MACRO_1B 1
diff --git a/test/modularize/Inputs/CoverageProblems/Level2A.h b/test/modularize/Inputs/CoverageProblems/Level2A.h
new file mode 100644 (file)
index 0000000..9a355df
--- /dev/null
@@ -0,0 +1 @@
+#define MACRO_2A 1
diff --git a/test/modularize/Inputs/CoverageProblems/Level2B.h b/test/modularize/Inputs/CoverageProblems/Level2B.h
new file mode 100644 (file)
index 0000000..25b7017
--- /dev/null
@@ -0,0 +1 @@
+#define MACRO_2B 1
diff --git a/test/modularize/Inputs/CoverageProblems/Level3A.h b/test/modularize/Inputs/CoverageProblems/Level3A.h
new file mode 100644 (file)
index 0000000..1699061
--- /dev/null
@@ -0,0 +1,2 @@
+#include "Sub/Level3B.h"
+#define MACRO_3A 1
diff --git a/test/modularize/Inputs/CoverageProblems/Level3B b/test/modularize/Inputs/CoverageProblems/Level3B
new file mode 100644 (file)
index 0000000..e1eee7e
--- /dev/null
@@ -0,0 +1 @@
+#define MACRO_3B 1
diff --git a/test/modularize/Inputs/CoverageProblems/Sub/Level3B.h b/test/modularize/Inputs/CoverageProblems/Sub/Level3B.h
new file mode 100644 (file)
index 0000000..e1eee7e
--- /dev/null
@@ -0,0 +1 @@
+#define MACRO_3B 1
diff --git a/test/modularize/Inputs/CoverageProblems/UmbrellaFile.h b/test/modularize/Inputs/CoverageProblems/UmbrellaFile.h
new file mode 100644 (file)
index 0000000..5417a42
--- /dev/null
@@ -0,0 +1,3 @@
+#define UMBRELLA_HEADER 1
+#include "UmbrellaInclude1.h"
+#include "UmbrellaInclude2.h"
diff --git a/test/modularize/Inputs/CoverageProblems/UmbrellaInclude1.h b/test/modularize/Inputs/CoverageProblems/UmbrellaInclude1.h
new file mode 100644 (file)
index 0000000..adf82cb
--- /dev/null
@@ -0,0 +1 @@
+#define UMBRELLA_INCLUDE_1 1
diff --git a/test/modularize/Inputs/CoverageProblems/UmbrellaInclude2.h b/test/modularize/Inputs/CoverageProblems/UmbrellaInclude2.h
new file mode 100644 (file)
index 0000000..a3875ab
--- /dev/null
@@ -0,0 +1 @@
+#define UMBRELLA_INCLUDE_2 1
diff --git a/test/modularize/Inputs/CoverageProblems/UmbrellaSub/Umbrell1.h b/test/modularize/Inputs/CoverageProblems/UmbrellaSub/Umbrell1.h
new file mode 100644 (file)
index 0000000..719c223
--- /dev/null
@@ -0,0 +1 @@
+#define UMBRELLA_1 1
diff --git a/test/modularize/Inputs/CoverageProblems/UmbrellaSub/Umbrell2.h b/test/modularize/Inputs/CoverageProblems/UmbrellaSub/Umbrell2.h
new file mode 100644 (file)
index 0000000..8efec99
--- /dev/null
@@ -0,0 +1 @@
+#define UMBRELLA_2 1
diff --git a/test/modularize/Inputs/CoverageProblems/module.modulemap b/test/modularize/Inputs/CoverageProblems/module.modulemap
new file mode 100644 (file)
index 0000000..dbe0bb8
--- /dev/null
@@ -0,0 +1,30 @@
+// module.map
+
+module Level1A {
+  header "Level1A.h"
+  export *
+}
+module Level1B {
+  header "Level1B.h"
+  export *
+  module Level2B {
+    header "Level2B.h"
+    export *
+  }
+}
+module Level2A {
+  header "Level2A.h"
+  export *
+}
+module UmbrellaDirectoryModule {
+  umbrella "UmbrellaSub"
+}
+module UmbrellaHeaderModule {
+  umbrella header "UmbrellaFile.h"
+}
+/*
+module NoHeader {
+  header "NoHeader.h"
+  export *
+}
+*/
diff --git a/test/modularize/Inputs/DuplicateHeader1.h b/test/modularize/Inputs/DuplicateHeader1.h
new file mode 100644 (file)
index 0000000..2936b08
--- /dev/null
@@ -0,0 +1,2 @@
+// Same decl as in DuplicateHeader2.h.
+typedef int TypeInt;
diff --git a/test/modularize/Inputs/DuplicateHeader2.h b/test/modularize/Inputs/DuplicateHeader2.h
new file mode 100644 (file)
index 0000000..cef0ffe
--- /dev/null
@@ -0,0 +1,2 @@
+// Same decl as in DuplicateHeader1.h.
+typedef int TypeInt;
diff --git a/test/modularize/Inputs/Empty.h b/test/modularize/Inputs/Empty.h
new file mode 100644 (file)
index 0000000..d74d27d
--- /dev/null
@@ -0,0 +1 @@
+// Empty header for testing #include directives in blocks.
diff --git a/test/modularize/Inputs/HeaderGuard.h b/test/modularize/Inputs/HeaderGuard.h
new file mode 100644 (file)
index 0000000..64d7476
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef _HEADERGUARD_H_
+#define _HEADERGUARD_H_
+#include "HeaderGuardSub1.h"
+#include "HeaderGuardSub2.h"
+#include "HeaderGuardSubSub.h"
+#endif // _HEADERGUARD_H_
diff --git a/test/modularize/Inputs/HeaderGuardSub1.h b/test/modularize/Inputs/HeaderGuardSub1.h
new file mode 100644 (file)
index 0000000..36ad80c
--- /dev/null
@@ -0,0 +1,5 @@
+#ifndef _HEADERGUARDSUB1_H_
+#define _HEADERGUARDSUB1_H_
+#include "HeaderGuardSubSub.h"
+#include "HeaderGuardSubSubDefined.h"
+#endif // _HEADERGUARDSUB1_H_
diff --git a/test/modularize/Inputs/HeaderGuardSub2.h b/test/modularize/Inputs/HeaderGuardSub2.h
new file mode 100644 (file)
index 0000000..4098c8e
--- /dev/null
@@ -0,0 +1,5 @@
+#ifndef _HEADERGUARDSUB2_H_
+#define _HEADERGUARDSUB2_H_
+#include "HeaderGuardSubSub.h"
+#include "HeaderGuardSubSubDefined.h"
+#endif // _HEADERGUARDSUB2_H_
diff --git a/test/modularize/Inputs/HeaderGuardSubSub.h b/test/modularize/Inputs/HeaderGuardSubSub.h
new file mode 100644 (file)
index 0000000..3f230d2
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef _HEADERGUARDSUBSUB_H_
+#define _HEADERGUARDSUBSUB_H_
+
+#define SOMETHING 1
+
+// Nest include.  Header guard should not confuse modularize.
+#include "HeaderGuard.h"
+
+#endif // _HEADERGUARDSUBSUB_H_
diff --git a/test/modularize/Inputs/HeaderGuardSubSubDefined.h b/test/modularize/Inputs/HeaderGuardSubSubDefined.h
new file mode 100644 (file)
index 0000000..5f0cc62
--- /dev/null
@@ -0,0 +1,9 @@
+#if !defined(_HEADERGUARDSUBSUBDEFINED_H_)
+#define _HEADERGUARDSUBSUBDEFINED_H_
+
+#define SOMETHING_OTHER 1
+
+// Nest include.  Header guard should not confuse modularize.
+#include "HeaderGuard.h"
+
+#endif // _HEADERGUARDSUBSUBDEFINED_H_
diff --git a/test/modularize/Inputs/IncludeInExtern.h b/test/modularize/Inputs/IncludeInExtern.h
new file mode 100644 (file)
index 0000000..da7de8f
--- /dev/null
@@ -0,0 +1,3 @@
+extern "C" {
+  #include "Empty.h"
+}
diff --git a/test/modularize/Inputs/IncludeInNamespace.h b/test/modularize/Inputs/IncludeInNamespace.h
new file mode 100644 (file)
index 0000000..2122528
--- /dev/null
@@ -0,0 +1,3 @@
+namespace MyNamespace {
+  #include "Empty.h"
+}
diff --git a/test/modularize/Inputs/InconsistentHeader1.h b/test/modularize/Inputs/InconsistentHeader1.h
new file mode 100644 (file)
index 0000000..7dfcca9
--- /dev/null
@@ -0,0 +1,4 @@
+// Define symbol such that a declaration exists when this header
+// is included, but not when InconsistentHeader2.h is included.
+#define SYMBOL1 1
+#include "InconsistentSubHeader.h"
diff --git a/test/modularize/Inputs/InconsistentHeader2.h b/test/modularize/Inputs/InconsistentHeader2.h
new file mode 100644 (file)
index 0000000..a2bcae1
--- /dev/null
@@ -0,0 +1,3 @@
+// Set up so the declaration in InconsistentSubHeader.h is not defined.
+#define SYMBOL2 1
+#include "InconsistentSubHeader.h"
diff --git a/test/modularize/Inputs/InconsistentSubHeader.h b/test/modularize/Inputs/InconsistentSubHeader.h
new file mode 100644 (file)
index 0000000..f87ea65
--- /dev/null
@@ -0,0 +1,18 @@
+// Set up so TypeInt only defined during InconsistentHeader1.h include.
+#ifdef SYMBOL1
+#define SYMBOL 1
+#define FUNC_STYLE(a, b) a||b
+#endif
+#ifdef SYMBOL2
+#define SYMBOL 2
+#define FUNC_STYLE(a, b) a&&b
+#endif
+
+#if SYMBOL == 1
+typedef int TypeInt;
+#endif
+
+int var = FUNC_STYLE(1, 0);
+
+#if defined(SYMBOL1)
+#endif
diff --git a/test/modularize/Inputs/IsDependent.h b/test/modularize/Inputs/IsDependent.h
new file mode 100644 (file)
index 0000000..4928110
--- /dev/null
@@ -0,0 +1,4 @@
+// This header depends on SomeTypes.h for the TypeInt typedef.
+
+typedef TypeInt NewTypeInt;
+typedef OtherTypeInt OtherNewTypeInt;
diff --git a/test/modularize/Inputs/MissingHeader/Level1A.h b/test/modularize/Inputs/MissingHeader/Level1A.h
new file mode 100644 (file)
index 0000000..10eef67
--- /dev/null
@@ -0,0 +1 @@
+#define MACRO_1A 1
diff --git a/test/modularize/Inputs/MissingHeader/module.modulemap b/test/modularize/Inputs/MissingHeader/module.modulemap
new file mode 100644 (file)
index 0000000..daa06fc
--- /dev/null
@@ -0,0 +1,10 @@
+// module.map\r
+\r
+module Level1A {\r
+  header "Level1A.h"\r
+  export *\r
+}\r
+module Missing {\r
+  header "Missing.h"\r
+  export *\r
+}\r
diff --git a/test/modularize/Inputs/NamespaceClasses.h b/test/modularize/Inputs/NamespaceClasses.h
new file mode 100644 (file)
index 0000000..57e6091
--- /dev/null
@@ -0,0 +1,20 @@
+// Define same class name in different namespaces.
+
+namespace Namespace1 {
+  class NamespaceClass {
+  public:
+    NamespaceClass() : Member(0) {}
+  private:
+    int Member;
+  };
+}
+
+namespace Namespace2 {
+  class NamespaceClass {
+  public:
+    NamespaceClass() : Member(0) {}
+  private:
+    int Member;
+  };
+}
+
diff --git a/test/modularize/Inputs/NestedMacro.h b/test/modularize/Inputs/NestedMacro.h
new file mode 100644 (file)
index 0000000..2cfc92c
--- /dev/null
@@ -0,0 +1,5 @@
+// Verification of fix for nested macro.
+
+#define FUNCMACROINNER(a) a
+#define FUNCMACROOUTER(b, c) FUNCMACROINNER(b) + FUNCMACROINNER(c)
+int FuncMacroValue = FUNCMACROOUTER(1, 2);
diff --git a/test/modularize/Inputs/NoProblems.modulemap b/test/modularize/Inputs/NoProblems.modulemap
new file mode 100644 (file)
index 0000000..82029c1
--- /dev/null
@@ -0,0 +1,9 @@
+// NoProblems.modulemap
+module SomeTypes {
+  header "SomeTypes.h"
+  export *
+}
+module SomeDecls {
+  header "SomeDecls.h"
+  export *
+}
diff --git a/test/modularize/Inputs/ProblemsDuplicate.modulemap b/test/modularize/Inputs/ProblemsDuplicate.modulemap
new file mode 100644 (file)
index 0000000..ff1d2f5
--- /dev/null
@@ -0,0 +1,9 @@
+// ProblemsDuplicate.modulemap
+module DuplicateHeader1 {
+  header "DuplicateHeader1.h"
+  export *
+}
+module DuplicateHeader2 {
+  header "DuplicateHeader2.h"
+  export *
+}
diff --git a/test/modularize/Inputs/SomeDecls.h b/test/modularize/Inputs/SomeDecls.h
new file mode 100644 (file)
index 0000000..8892b65
--- /dev/null
@@ -0,0 +1,16 @@
+// Declare a couple of functions - no modules problems.
+
+void FuncOne();
+
+int FuncTwo(int arg);
+
+void FuncOverload(int arg) {}
+void FuncOverload(char *arg) {}
+
+namespace Namespace1 {
+  void FuncNameSpace() {}
+}
+
+namespace Namespace2 {
+  void FuncNameSpace() {}
+}
diff --git a/test/modularize/Inputs/SomeOtherTypes.h b/test/modularize/Inputs/SomeOtherTypes.h
new file mode 100644 (file)
index 0000000..288faff
--- /dev/null
@@ -0,0 +1,4 @@
+// Declare another type for the dependency check.
+// This file dependent on SomeTypes.h being included first.
+
+typedef TypeInt OtherTypeInt;
diff --git a/test/modularize/Inputs/SomeTypes.h b/test/modularize/Inputs/SomeTypes.h
new file mode 100644 (file)
index 0000000..46c4316
--- /dev/null
@@ -0,0 +1,16 @@
+// Define a few different kinds of types - no modules problems.
+
+typedef int TypeInt;
+
+typedef TypeInt NestedTypeInt;
+
+struct TypeStruct {
+  int Member;
+};
+
+class TypeClass {
+public:
+  TypeClass() : Member(0) {}
+private:
+  int Member;
+};
diff --git a/test/modularize/Inputs/SubModule1/Header1.h b/test/modularize/Inputs/SubModule1/Header1.h
new file mode 100644 (file)
index 0000000..ea89f0f
--- /dev/null
@@ -0,0 +1 @@
+// Header1.h - Empty.
diff --git a/test/modularize/Inputs/SubModule1/Header2.h b/test/modularize/Inputs/SubModule1/Header2.h
new file mode 100644 (file)
index 0000000..7c71e98
--- /dev/null
@@ -0,0 +1 @@
+// Header2.h - Empty.
diff --git a/test/modularize/Inputs/SubModule2/Header3.h b/test/modularize/Inputs/SubModule2/Header3.h
new file mode 100644 (file)
index 0000000..bb56afa
--- /dev/null
@@ -0,0 +1 @@
+// Header3.h - Empty.
diff --git a/test/modularize/Inputs/SubModule2/Header4.h b/test/modularize/Inputs/SubModule2/Header4.h
new file mode 100644 (file)
index 0000000..07ec951
--- /dev/null
@@ -0,0 +1 @@
+// Header4.h - Empty.
diff --git a/test/modularize/Inputs/TemplateClasses.h b/test/modularize/Inputs/TemplateClasses.h
new file mode 100644 (file)
index 0000000..3eab6d9
--- /dev/null
@@ -0,0 +1,15 @@
+// Exercise some template issues.  Should not produce errors.
+
+// Forward declaration.
+template<class T> class TemplateClass;
+
+// Full declaration.
+template<class T>class TemplateClass {
+public:
+  TemplateClass() {}
+private:
+  T Member;
+};
+
+// Template alias.
+template<class T> using TemplateClassAlias = TemplateClass<T>;
diff --git a/test/modularize/NoProblems.modularize b/test/modularize/NoProblems.modularize
new file mode 100644 (file)
index 0000000..b3cc36b
--- /dev/null
@@ -0,0 +1,6 @@
+# RUN: modularize %s -x c++
+# RUN: modularize -prefix=%p %s -x c++
+# RUN: modularize -no-coverage-check %S/Inputs/NoProblems.modulemap -x c++
+
+Inputs/SomeTypes.h
+Inputs/SomeDecls.h
diff --git a/test/modularize/NoProblemsAnonymous.modularize b/test/modularize/NoProblemsAnonymous.modularize
new file mode 100644 (file)
index 0000000..20b1898
--- /dev/null
@@ -0,0 +1,3 @@
+# RUN: modularize %s -x c++
+
+Inputs/Anonymous.h
diff --git a/test/modularize/NoProblemsAssistant.modularize b/test/modularize/NoProblemsAssistant.modularize
new file mode 100644 (file)
index 0000000..7ddc726
--- /dev/null
@@ -0,0 +1,50 @@
+# RUN: modularize -module-map-path=Output/NoProblemsAssistant.txt -root-module=Root -prefix=%S/Input %s
+# RUN: FileCheck --input-file=%T/NoProblemsAssistant.txt %s
+
+SomeTypes.h
+SomeDecls.h
+SubModule1/Header1.h
+SubModule1/Header2.h
+SubModule2/Header3.h
+SubModule2/Header4.h
+SubModule2/Header5-dash.dot.h
+SubModule2.h
+
+# CHECK: // Output/NoProblemsAssistant.txt
+# CHECK-NEXT: // Generated by: modularize -module-map-path=Output/NoProblemsAssistant.txt -root-module=Root -prefix={{.*}}{{[/\\]}}{{.*}} {{.*}}{{[/\\]}}NoProblemsAssistant.modularize
+# CHECK: module Root {
+# CHECK-NEXT:   module SomeTypes {
+# CHECK-NEXT:     header "SomeTypes.h"
+# CHECK-NEXT:     export *
+# CHECK-NEXT:   }
+# CHECK-NEXT:   module SomeDecls {
+# CHECK-NEXT:     header "SomeDecls.h"
+# CHECK-NEXT:     export *
+# CHECK-NEXT:   }
+# CHECK-NEXT:   module SubModule1 {
+# CHECK-NEXT:     module Header1 {
+# CHECK-NEXT:       header "SubModule1/Header1.h"
+# CHECK-NEXT:       export *
+# CHECK-NEXT:     }
+# CHECK-NEXT:     module Header2 {
+# CHECK-NEXT:       header "SubModule1/Header2.h"
+# CHECK-NEXT:       export *
+# CHECK-NEXT:     }
+# CHECK-NEXT:   }
+# CHECK-NEXT:   module SubModule2 {
+# CHECK-NEXT:     module Header3 {
+# CHECK-NEXT:       header "SubModule2/Header3.h"
+# CHECK-NEXT:       export *
+# CHECK-NEXT:     }
+# CHECK-NEXT:     module Header4 {
+# CHECK-NEXT:       header "SubModule2/Header4.h"
+# CHECK-NEXT:       export *
+# CHECK-NEXT:     }
+# CHECK-NEXT:     module Header5_dash_dot {
+# CHECK-NEXT:       header "SubModule2/Header5-dash.dot.h"
+# CHECK-NEXT:       export *
+# CHECK-NEXT:     }
+# CHECK-NEXT:     header "SubModule2.h"
+# CHECK-NEXT:     export *
+# CHECK-NEXT:   }
+# CHECK-NEXT: }
diff --git a/test/modularize/NoProblemsCoverage.modularize b/test/modularize/NoProblemsCoverage.modularize
new file mode 100644 (file)
index 0000000..bcb06f8
--- /dev/null
@@ -0,0 +1 @@
+# RUN: modularize -I Includes1 -I Includes2 %S/Inputs/CoverageNoProblems/module.modulemap
diff --git a/test/modularize/NoProblemsDependencies.modularize b/test/modularize/NoProblemsDependencies.modularize
new file mode 100644 (file)
index 0000000..bd5292b
--- /dev/null
@@ -0,0 +1,3 @@
+# RUN: modularize %s -x c++
+
+Inputs/IsDependent.h: Inputs/SomeTypes.h Inputs/SomeOtherTypes.h
diff --git a/test/modularize/NoProblemsGuard.modularize b/test/modularize/NoProblemsGuard.modularize
new file mode 100644 (file)
index 0000000..a5a80f0
--- /dev/null
@@ -0,0 +1,5 @@
+# RUN: modularize %s -x c++
+
+Inputs/HeaderGuardSub1.h
+Inputs/HeaderGuardSub2.h
+Inputs/HeaderGuard.h
diff --git a/test/modularize/NoProblemsList.modularize b/test/modularize/NoProblemsList.modularize
new file mode 100644 (file)
index 0000000..39261ce
--- /dev/null
@@ -0,0 +1,2 @@
+# RUN: modularize %S/NoProblems.modularize,%S/NoProblemsAnonymous.modularize -x c++
+# RUN: modularize -prefix=%p %S/NoProblems.modularize,%S/NoProblemsAnonymous.modularize -x c++
diff --git a/test/modularize/NoProblemsNamespace.modularize b/test/modularize/NoProblemsNamespace.modularize
new file mode 100644 (file)
index 0000000..1c3f78d
--- /dev/null
@@ -0,0 +1,3 @@
+# RUN: modularize -block-check-header-list-only
+
+Inputs/IncludeInNamespace.h
diff --git a/test/modularize/NoProblemsNamespaceClasses.modularize b/test/modularize/NoProblemsNamespaceClasses.modularize
new file mode 100644 (file)
index 0000000..1d02c78
--- /dev/null
@@ -0,0 +1,3 @@
+# RUN: modularize %s -x c++
+
+Inputs/NamespaceClasses.h
diff --git a/test/modularize/NoProblemsNestedMacro.modularize b/test/modularize/NoProblemsNestedMacro.modularize
new file mode 100644 (file)
index 0000000..a1c627d
--- /dev/null
@@ -0,0 +1,3 @@
+# RUN: modularize %s -x c++
+
+Inputs/NestedMacro.h
diff --git a/test/modularize/NoProblemsTemplateClasses.modularize b/test/modularize/NoProblemsTemplateClasses.modularize
new file mode 100644 (file)
index 0000000..d41c43c
--- /dev/null
@@ -0,0 +1,3 @@
+# RUN: modularize %s -x c++
+
+Inputs/TemplateClasses.h
diff --git a/test/modularize/ProblemsCompileError.modularize b/test/modularize/ProblemsCompileError.modularize
new file mode 100644 (file)
index 0000000..a7ad298
--- /dev/null
@@ -0,0 +1,3 @@
+# RUN: not modularize %S/Inputs/CompileError/module.modulemap 2>&1 | FileCheck %s
+
+# CHECK: {{.*}}{{[/\\]}}Inputs{{[/\\]}}CompileError{{[/\\]}}HasError.h:1:9: error: unknown type name 'WithoutDep'
diff --git a/test/modularize/ProblemsCoverage.modularize b/test/modularize/ProblemsCoverage.modularize
new file mode 100644 (file)
index 0000000..5a1c348
--- /dev/null
@@ -0,0 +1,5 @@
+# RUN: not modularize %S/Inputs/CoverageProblems/module.modulemap 2>&1 | FileCheck %s
+
+# CHECK: warning: {{.*}}{{[/\\]}}Inputs/CoverageProblems/module.modulemap does not account for file: {{.*}}{{[/\\]}}Inputs/CoverageProblems/Level3A.h
+# CHECK-NEXT: warning: {{.*}}{{[/\\]}}Inputs/CoverageProblems/module.modulemap does not account for file: {{.*}}{{[/\\]}}Inputs/CoverageProblems/Level3B
+# CHECK-NEXT: warning: {{.*}}{{[/\\]}}Inputs/CoverageProblems/module.modulemap does not account for file: {{.*}}{{[/\\]}}Inputs/CoverageProblems/Sub/Level3B.h
diff --git a/test/modularize/ProblemsDisplayLists.modularize b/test/modularize/ProblemsDisplayLists.modularize
new file mode 100644 (file)
index 0000000..31be95c
--- /dev/null
@@ -0,0 +1,16 @@
+# RUN: not modularize -display-file-lists %S/Inputs/CompileError/module.modulemap 2>&1 | FileCheck %s
+
+# CHECK: {{.*}}{{[/\\]}}Inputs{{[/\\]}}CompileError{{[/\\]}}HasError.h:1:9: error: unknown type name 'WithoutDep'
+
+# CHECK: These are the files with possible errors:
+
+# CHECK: Inputs/CompileError/HasError.h
+
+# CHECK: These are the files with no detected errors:
+
+# CHECK: Inputs/CompileError/Level1A.h
+
+# CHECK: These are the combined files, with problem files preceded by #:
+
+# CHECK: {{.*}}Inputs/CompileError/HasError.h
+# CHECK: Inputs/CompileError/Level1A.h
diff --git a/test/modularize/ProblemsDuplicate.modularize b/test/modularize/ProblemsDuplicate.modularize
new file mode 100644 (file)
index 0000000..301c9cc
--- /dev/null
@@ -0,0 +1,9 @@
+# RUN: not modularize %s -x c++ 2>&1 | FileCheck %s
+# RUN: not modularize %S/Inputs/ProblemsDuplicate.modulemap -x c++ 2>&1 | FileCheck %s
+
+Inputs/DuplicateHeader1.h
+Inputs/DuplicateHeader2.h
+
+# CHECK: error: value 'TypeInt' defined at multiple locations:
+# CHECK-NEXT:    {{.*}}{{[/\\]}}Inputs{{[/\\]}}DuplicateHeader1.h:2:13
+# CHECK-NEXT:    {{.*}}{{[/\\]}}Inputs{{[/\\]}}DuplicateHeader2.h:2:13
\ No newline at end of file
diff --git a/test/modularize/ProblemsExternC.modularize b/test/modularize/ProblemsExternC.modularize
new file mode 100644 (file)
index 0000000..b91d7e5
--- /dev/null
@@ -0,0 +1,12 @@
+# RUN: not modularize %s -x c++ 2>&1 | FileCheck %s
+
+Inputs/IncludeInExtern.h
+
+# CHECK: {{.*}}{{[/\\]}}Inputs{{[/\\]}}IncludeInExtern.h:2:3:
+# CHECK-NEXT:   #include "Empty.h"
+# CHECK-NEXT:   ^
+# CHECK-NEXT: error: Include directive within extern "C" {}.
+# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}IncludeInExtern.h:1:1:
+# CHECK-NEXT: extern "C" {
+# CHECK-NEXT: ^
+# CHECK-NEXT: The "extern "C" {}" block is here.
diff --git a/test/modularize/ProblemsInconsistent.modularize b/test/modularize/ProblemsInconsistent.modularize
new file mode 100644 (file)
index 0000000..04d0b01
--- /dev/null
@@ -0,0 +1,108 @@
+# RUN: not modularize %s -x c++ 2>&1 | FileCheck %s
+
+Inputs/InconsistentHeader1.h
+Inputs/InconsistentHeader2.h
+
+# CHECK: error: macro 'SYMBOL' defined at multiple locations:
+# CHECK-NEXT:     {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:3:9
+# CHECK-NEXT:     {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:7:9
+# CHECK-NEXT: error: macro 'FUNC_STYLE' defined at multiple locations:
+# CHECK-NEXT:     {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:4:9
+# CHECK-NEXT:     {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:8:9
+# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:15:11:
+# CHECK-NEXT: int var = FUNC_STYLE(1, 0);
+# CHECK-NEXT:           ^
+# CHECK-NEXT: error: Macro instance 'FUNC_STYLE(1, 0);' has different values in this header, depending on how it was included.
+# CHECK-NEXT:   'FUNC_STYLE(1, 0);' expanded to: '1||0' with respect to these inclusion paths:
+# CHECK-NEXT:     {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentHeader1.h
+# CHECK-NEXT:       {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h
+# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:4:9:
+# CHECK-NEXT: #define FUNC_STYLE(a, b) a||b
+# CHECK-NEXT:         ^
+# CHECK-NEXT: Macro defined here.
+# CHECK-NEXT:   'FUNC_STYLE(1, 0);' expanded to: '1&&0' with respect to these inclusion paths:
+# CHECK-NEXT:     {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentHeader2.h
+# CHECK-NEXT:       {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h
+# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:8:9:
+# CHECK-NEXT: #define FUNC_STYLE(a, b) a&&b
+# CHECK-NEXT:         ^
+# CHECK-NEXT: Macro defined here.
+# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:11:5:
+# CHECK-NEXT: #if SYMBOL == 1
+# CHECK-NEXT:     ^
+# CHECK-NEXT: error: Macro instance 'SYMBOL' has different values in this header, depending on how it was included.
+# CHECK-NEXT:   'SYMBOL' expanded to: '1' with respect to these inclusion paths:
+# CHECK-NEXT:     {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentHeader1.h
+# CHECK-NEXT:       {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h
+# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:3:9:
+# CHECK-NEXT: #define SYMBOL 1
+# CHECK-NEXT:         ^
+# CHECK-NEXT: Macro defined here.
+# CHECK-NEXT:   'SYMBOL' expanded to: '2' with respect to these inclusion paths:
+# CHECK-NEXT:     {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentHeader2.h
+# CHECK-NEXT:       {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h
+# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:7:9:
+# CHECK-NEXT: #define SYMBOL 2
+# CHECK-NEXT:         ^
+# CHECK-NEXT: Macro defined here.
+# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:17:5:
+# CHECK-NEXT: #if defined(SYMBOL1)
+# CHECK-NEXT:     ^
+# CHECK-NEXT: error: Macro instance 'defined(SYMBOL1)' has different values in this header, depending on how it was included.
+# CHECK-NEXT:   'defined(SYMBOL1)' expanded to: 'true' with respect to these inclusion paths:
+# CHECK-NEXT:     {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentHeader1.h
+# CHECK-NEXT:       {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h
+# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentHeader1.h:3:9:
+# CHECK-NEXT: #define SYMBOL1 1
+# CHECK-NEXT:         ^
+# CHECK-NEXT: Macro defined here.
+# CHECK-NEXT:   'defined(SYMBOL1)' expanded to: 'false' with respect to these inclusion paths:
+# CHECK-NEXT:     {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentHeader2.h
+# CHECK-NEXT:       {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h
+# CHECK-NEXT: (no macro definition)
+# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:11:2
+# CHECK-NEXT: #if SYMBOL == 1
+# CHECK-NEXT: ^
+# CHECK-NEXT: error: Conditional expression instance 'SYMBOL == 1' has different values in this header, depending on how it was included.
+# CHECK-NEXT:   'SYMBOL == 1' expanded to: 'true' with respect to these inclusion paths:
+# CHECK-NEXT:     {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentHeader1.h
+# CHECK-NEXT:       {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h
+# CHECK-NEXT:   'SYMBOL == 1' expanded to: 'false' with respect to these inclusion paths:
+# CHECK-NEXT:     {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentHeader2.h
+# CHECK-NEXT:       {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h
+# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:2:2
+# CHECK-NEXT: #ifdef SYMBOL1
+# CHECK-NEXT: ^
+# CHECK-NEXT: error: Conditional expression instance 'SYMBOL1' has different values in this header, depending on how it was included.
+# CHECK-NEXT:   'SYMBOL1' expanded to: 'true' with respect to these inclusion paths:
+# CHECK-NEXT:     {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentHeader1.h
+# CHECK-NEXT:       {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h
+# CHECK-NEXT:   'SYMBOL1' expanded to: 'false' with respect to these inclusion paths:
+# CHECK-NEXT:     {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentHeader2.h
+# CHECK-NEXT:       {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h
+# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:6:2
+# CHECK-NEXT: #ifdef SYMBOL2
+# CHECK-NEXT: ^
+# CHECK-NEXT: error: Conditional expression instance 'SYMBOL2' has different values in this header, depending on how it was included.
+# CHECK-NEXT:   'SYMBOL2' expanded to: 'false' with respect to these inclusion paths:
+# CHECK-NEXT:     {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentHeader1.h
+# CHECK-NEXT:       {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h
+# CHECK-NEXT:   'SYMBOL2' expanded to: 'true' with respect to these inclusion paths:
+# CHECK-NEXT:     {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentHeader2.h
+# CHECK-NEXT:       {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h
+# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:17:2
+# CHECK-NEXT: #if defined(SYMBOL1)
+# CHECK-NEXT: ^
+# CHECK-NEXT: error: Conditional expression instance 'defined(SYMBOL1)' has different values in this header, depending on how it was included.
+# CHECK-NEXT:   'defined(SYMBOL1)' expanded to: 'true' with respect to these inclusion paths:
+# CHECK-NEXT:     {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentHeader1.h
+# CHECK-NEXT:       {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h
+# CHECK-NEXT:   'defined(SYMBOL1)' expanded to: 'false' with respect to these inclusion paths:
+# CHECK-NEXT:     {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentHeader2.h
+# CHECK-NEXT:       {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h
+# CHECK-NEXT: error: header '{{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h' has different contents depending on how it was included.
+# CHECK-NEXT: note: 'SYMBOL' in {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h at 3:9 not always provided
+# CHECK-NEXT: note: 'FUNC_STYLE' in {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h at 4:9 not always provided
+# CHECK-NEXT: note: 'SYMBOL' in {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h at 7:9 not always provided
+# CHECK-NEXT: note: 'FUNC_STYLE' in {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h at 8:9 not always provided
+# CHECK-NEXT: note: 'TypeInt' in {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h at 12:13 not always provided
diff --git a/test/modularize/ProblemsMissingHeader.modularize b/test/modularize/ProblemsMissingHeader.modularize
new file mode 100644 (file)
index 0000000..bc731fb
--- /dev/null
@@ -0,0 +1,3 @@
+# RUN: not modularize %S/Inputs/MissingHeader/module.modulemap 2>&1 | FileCheck %s
+
+# CHECK: {{.*}}{{[/\\]}}Inputs/MissingHeader/module.modulemap:8:10: error : Header not found: Missing.h
diff --git a/test/modularize/ProblemsNamespace.modularize b/test/modularize/ProblemsNamespace.modularize
new file mode 100644 (file)
index 0000000..d4bc353
--- /dev/null
@@ -0,0 +1,12 @@
+# RUN: not modularize %s -x c++ 2>&1 | FileCheck %s
+
+Inputs/IncludeInNamespace.h
+
+# CHECK: {{.*}}{{[/\\]}}Inputs{{[/\\]}}IncludeInNamespace.h:2:3:
+# CHECK-NEXT:   #include "Empty.h"
+# CHECK-NEXT:   ^
+# CHECK-NEXT: error: Include directive within namespace MyNamespace {}.
+# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}IncludeInNamespace.h:1:1:
+# CHECK-NEXT: namespace MyNamespace {
+# CHECK-NEXT: ^
+# CHECK-NEXT: The "namespace MyNamespace {}" block is here.
diff --git a/test/modularize/SubModule2.h b/test/modularize/SubModule2.h
new file mode 100644 (file)
index 0000000..70d711b
--- /dev/null
@@ -0,0 +1,3 @@
+// SubModule2.h - Master header with same name as directory.
+#include "SubModule2/Header3.h"
+#include "SubModule2/Header4.h"
diff --git a/test/pp-trace/Inputs/Level1A.h b/test/pp-trace/Inputs/Level1A.h
new file mode 100644 (file)
index 0000000..165efc7
--- /dev/null
@@ -0,0 +1,2 @@
+#include "Level2A.h"
+#define MACRO_1A 1
diff --git a/test/pp-trace/Inputs/Level1B.h b/test/pp-trace/Inputs/Level1B.h
new file mode 100644 (file)
index 0000000..1e60798
--- /dev/null
@@ -0,0 +1,2 @@
+#include "Level2B.h"
+#define MACRO_1B 1
diff --git a/test/pp-trace/Inputs/Level2A.h b/test/pp-trace/Inputs/Level2A.h
new file mode 100644 (file)
index 0000000..9a355df
--- /dev/null
@@ -0,0 +1 @@
+#define MACRO_2A 1
diff --git a/test/pp-trace/Inputs/Level2B.h b/test/pp-trace/Inputs/Level2B.h
new file mode 100644 (file)
index 0000000..25b7017
--- /dev/null
@@ -0,0 +1 @@
+#define MACRO_2B 1
diff --git a/test/pp-trace/Inputs/ModularizeList.txt b/test/pp-trace/Inputs/ModularizeList.txt
new file mode 100644 (file)
index 0000000..8193db6
--- /dev/null
@@ -0,0 +1,4 @@
+Level1A.h
+Level1B.h
+Level2A.h
+Level2B.h
diff --git a/test/pp-trace/Inputs/module.map b/test/pp-trace/Inputs/module.map
new file mode 100644 (file)
index 0000000..31f33c5
--- /dev/null
@@ -0,0 +1,18 @@
+// module.map\r
+\r
+module Level1A {\r
+  header "Level1A.h"\r
+  export *\r
+}\r
+module Level1B {\r
+  header "Level1B.h"\r
+  export *\r
+  module Level2B {\r
+    header "Level2B.h"\r
+    export *\r
+  }\r
+}\r
+module Level2A {\r
+  header "Level2A.h"\r
+  export *\r
+}\r
diff --git a/test/pp-trace/pp-trace-conditional.cpp b/test/pp-trace/pp-trace-conditional.cpp
new file mode 100644 (file)
index 0000000..e3e225f
--- /dev/null
@@ -0,0 +1,304 @@
+// RUN: pp-trace -ignore FileChanged %s -undef -target x86_64 -std=c++11 | FileCheck --strict-whitespace %s
+
+#if 1
+#endif
+
+#if 0
+#endif
+
+#if 1
+#else
+#endif
+
+#if 0
+#else
+#endif
+
+#if 1
+#elif 1
+#endif
+#if 1
+#elif 0
+#endif
+
+#if 0
+#elif 1
+#endif
+#if 0
+#elif 0
+#endif
+#if 1
+#elif 1
+#endif
+#if 1
+#elif 0
+#endif
+
+#if 0
+#elif 1
+#else
+#endif
+#if 0
+#elif 0
+#else
+#endif
+#if 1
+#elif 1
+#else
+#endif
+#if 1
+#elif 0
+#else
+#endif
+
+#define MACRO 1
+#ifdef MACRO
+#endif
+#ifdef NO_MACRO
+#endif
+#ifndef MACRO
+#endif
+#ifndef NO_MACRO
+#endif
+
+// CHECK: ---
+// CHECK-NEXT: - Callback: MacroDefined
+// CHECK:        MacroNameTok: __STDC__
+// CHECK-NEXT:   MacroDirective: MD_Define
+// CHECK-NEXT: - Callback: MacroDefined
+// CHECK:        MacroNameTok: __STDC_HOSTED__
+// CHECK-NEXT:   MacroDirective: MD_Define
+// CHECK-NEXT: - Callback: MacroDefined
+// CHECK:        MacroNameTok: __cplusplus
+// CHECK-NEXT:   MacroDirective: MD_Define
+// CHECK-NEXT: - Callback: MacroDefined
+// CHECK:        MacroNameTok: __STDC_UTF_16__
+// CHECK-NEXT:   MacroDirective: MD_Define
+// CHECK-NEXT: - Callback: MacroDefined
+// CHECK:        MacroNameTok: __STDC_UTF_32__
+// CHECK-NEXT:   MacroDirective: MD_Define
+// CHECK:      - Callback: If
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:3:2"
+// CHECK-NEXT:   ConditionRange: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:3:4", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:4:1"]
+// CHECK-NEXT:   ConditionValue: CVK_True
+// CHECK-NEXT: - Callback: Endif
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:4:2"
+// CHECK-NEXT:   IfLoc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:3:2"
+// CHECK-NEXT: - Callback: If
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:6:2"
+// CHECK-NEXT:   ConditionRange: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:6:4", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:7:1"]
+// CHECK-NEXT:   ConditionValue: CVK_False
+// CHECK-NEXT: - Callback: Endif
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:7:2"
+// CHECK-NEXT:   IfLoc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:6:2"
+// CHECK-NEXT: - Callback: SourceRangeSkipped
+// CHECK-NEXT:   Range: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:6:2", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:7:2"]
+// CHECK-NEXT: - Callback: If
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:9:2"
+// CHECK-NEXT:   ConditionRange: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:9:4", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:10:1"]
+// CHECK-NEXT:   ConditionValue: CVK_True
+// CHECK-NEXT: - Callback: Else
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:10:2"
+// CHECK-NEXT:   IfLoc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:9:2"
+// CHECK-NEXT: - Callback: Endif
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:11:2"
+// CHECK-NEXT:   IfLoc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:9:2"
+// CHECK-NEXT: - Callback: SourceRangeSkipped
+// CHECK-NEXT:   Range: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:10:2", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:11:2"]
+// CHECK-NEXT: - Callback: If
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:13:2"
+// CHECK-NEXT:   ConditionRange: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:13:4", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:14:1"]
+// CHECK-NEXT:   ConditionValue: CVK_False
+// CHECK-NEXT: - Callback: Else
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:14:2"
+// CHECK-NEXT:   IfLoc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:13:2"
+// CHECK-NEXT: - Callback: SourceRangeSkipped
+// CHECK-NEXT:   Range: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:13:2", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:14:2"]
+// CHECK-NEXT: - Callback: Endif
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:15:2"
+// CHECK-NEXT:   IfLoc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:13:2"
+// CHECK-NEXT: - Callback: If
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:17:2"
+// CHECK-NEXT:   ConditionRange: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:17:4", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:18:1"]
+// CHECK-NEXT:   ConditionValue: CVK_True
+// CHECK-NEXT: - Callback: Elif
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:18:2"
+// CHECK-NEXT:   ConditionRange: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:18:6", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:19:1"]
+// CHECK-NEXT:   ConditionValue: CVK_NotEvaluated
+// CHECK-NEXT:   IfLoc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:17:2"
+// CHECK-NEXT: - Callback: Endif
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:19:2"
+// CHECK-NEXT:   IfLoc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:17:2"
+// CHECK-NEXT: - Callback: SourceRangeSkipped
+// CHECK-NEXT:   Range: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:18:2", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:19:2"]
+// CHECK-NEXT: - Callback: If
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:20:2"
+// CHECK-NEXT:   ConditionRange: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:20:4", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:21:1"]
+// CHECK-NEXT:   ConditionValue: CVK_True
+// CHECK-NEXT: - Callback: Elif
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:21:2"
+// CHECK-NEXT:   ConditionRange: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:21:6", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:22:1"]
+// CHECK-NEXT:   ConditionValue: CVK_NotEvaluated
+// CHECK-NEXT:   IfLoc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:20:2"
+// CHECK-NEXT: - Callback: Endif
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:22:2"
+// CHECK-NEXT:   IfLoc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:20:2"
+// CHECK-NEXT: - Callback: SourceRangeSkipped
+// CHECK-NEXT:   Range: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:21:2", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:22:2"]
+// CHECK-NEXT: - Callback: If
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:24:2"
+// CHECK-NEXT:   ConditionRange: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:24:4", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:25:1"]
+// CHECK-NEXT:   ConditionValue: CVK_False
+// CHECK-NEXT: - Callback: Elif
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:25:2"
+// CHECK-NEXT:   ConditionRange: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:25:6", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:26:1"]
+// CHECK-NEXT:   ConditionValue: CVK_True
+// CHECK-NEXT:   IfLoc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:24:2"
+// CHECK-NEXT: - Callback: SourceRangeSkipped
+// CHECK-NEXT:   Range: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:24:2", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:25:2"]
+// CHECK-NEXT: - Callback: Endif
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:26:2"
+// CHECK-NEXT:   IfLoc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:24:2"
+// CHECK-NEXT: - Callback: If
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:27:2"
+// CHECK-NEXT:   ConditionRange: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:27:4", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:28:1"]
+// CHECK-NEXT:   ConditionValue: CVK_False
+// CHECK-NEXT: - Callback: Elif
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:28:2"
+// CHECK-NEXT:   ConditionRange: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:28:6", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:29:1"]
+// CHECK-NEXT:   ConditionValue: CVK_False
+// CHECK-NEXT:   IfLoc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:27:2"
+// CHECK-NEXT: - Callback: Endif
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:29:2"
+// CHECK-NEXT:   IfLoc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:27:2"
+// CHECK-NEXT: - Callback: SourceRangeSkipped
+// CHECK-NEXT:   Range: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:27:2", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:29:2"]
+// CHECK-NEXT: - Callback: If
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:30:2"
+// CHECK-NEXT:   ConditionRange: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:30:4", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:31:1"]
+// CHECK-NEXT:   ConditionValue: CVK_True
+// CHECK-NEXT: - Callback: Elif
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:31:2"
+// CHECK-NEXT:   ConditionRange: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:31:6", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:32:1"]
+// CHECK-NEXT:   ConditionValue: CVK_NotEvaluated
+// CHECK-NEXT:   IfLoc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:30:2"
+// CHECK-NEXT: - Callback: Endif
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:32:2"
+// CHECK-NEXT:   IfLoc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:30:2"
+// CHECK-NEXT: - Callback: SourceRangeSkipped
+// CHECK-NEXT:   Range: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:31:2", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:32:2"]
+// CHECK-NEXT: - Callback: If
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:33:2"
+// CHECK-NEXT:   ConditionRange: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:33:4", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:34:1"]
+// CHECK-NEXT:   ConditionValue: CVK_True
+// CHECK-NEXT: - Callback: Elif
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:34:2"
+// CHECK-NEXT:   ConditionRange: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:34:6", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:35:1"]
+// CHECK-NEXT:   ConditionValue: CVK_NotEvaluated
+// CHECK-NEXT:   IfLoc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:33:2"
+// CHECK-NEXT: - Callback: Endif
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:35:2"
+// CHECK-NEXT:   IfLoc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:33:2"
+// CHECK-NEXT: - Callback: SourceRangeSkipped
+// CHECK-NEXT:   Range: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:34:2", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:35:2"]
+// CHECK-NEXT: - Callback: If
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:37:2"
+// CHECK-NEXT:   ConditionRange: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:37:4", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:38:1"]
+// CHECK-NEXT:   ConditionValue: CVK_False
+// CHECK-NEXT: - Callback: Elif
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:38:2"
+// CHECK-NEXT:   ConditionRange: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:38:6", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:39:1"]
+// CHECK-NEXT:   ConditionValue: CVK_True
+// CHECK-NEXT:   IfLoc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:37:2"
+// CHECK-NEXT: - Callback: SourceRangeSkipped
+// CHECK-NEXT:   Range: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:37:2", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:38:2"]
+// CHECK-NEXT: - Callback: Else
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:39:2"
+// CHECK-NEXT:   IfLoc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:37:2"
+// CHECK-NEXT: - Callback: Endif
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:40:2"
+// CHECK-NEXT:   IfLoc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:37:2"
+// CHECK-NEXT: - Callback: SourceRangeSkipped
+// CHECK-NEXT:   Range: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:39:2", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:40:2"]
+// CHECK-NEXT: - Callback: If
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:41:2"
+// CHECK-NEXT:   ConditionRange: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:41:4", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:42:1"]
+// CHECK-NEXT:   ConditionValue: CVK_False
+// CHECK-NEXT: - Callback: Elif
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:42:2"
+// CHECK-NEXT:   ConditionRange: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:42:6", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:43:1"]
+// CHECK-NEXT:   ConditionValue: CVK_False
+// CHECK-NEXT:   IfLoc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:41:2"
+// CHECK-NEXT: - Callback: Else
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:43:2"
+// CHECK-NEXT:   IfLoc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:41:2"
+// CHECK-NEXT: - Callback: SourceRangeSkipped
+// CHECK-NEXT:   Range: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:41:2", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:43:2"]
+// CHECK-NEXT: - Callback: Endif
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:44:2"
+// CHECK-NEXT:   IfLoc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:41:2"
+// CHECK-NEXT: - Callback: If
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:45:2"
+// CHECK-NEXT:   ConditionRange: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:45:4", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:46:1"]
+// CHECK-NEXT:   ConditionValue: CVK_True
+// CHECK-NEXT: - Callback: Elif
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:46:2"
+// CHECK-NEXT:   ConditionRange: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:46:6", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:47:1"]
+// CHECK-NEXT:   ConditionValue: CVK_NotEvaluated
+// CHECK-NEXT:   IfLoc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:45:2"
+// CHECK-NEXT: - Callback: Endif
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:48:2"
+// CHECK-NEXT:   IfLoc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:45:2"
+// CHECK-NEXT: - Callback: SourceRangeSkipped
+// CHECK-NEXT:   Range: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:46:2", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:48:2"]
+// CHECK-NEXT: - Callback: If
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:49:2"
+// CHECK-NEXT:   ConditionRange: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:49:4", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:50:1"]
+// CHECK-NEXT:   ConditionValue: CVK_True
+// CHECK-NEXT: - Callback: Elif
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:50:2"
+// CHECK-NEXT:   ConditionRange: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:50:6", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:51:1"]
+// CHECK-NEXT:   ConditionValue: CVK_NotEvaluated
+// CHECK-NEXT:   IfLoc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:49:2"
+// CHECK-NEXT: - Callback: Endif
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:52:2"
+// CHECK-NEXT:   IfLoc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:49:2"
+// CHECK-NEXT: - Callback: SourceRangeSkipped
+// CHECK-NEXT:   Range: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:50:2", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:52:2"]
+// CHECK-NEXT: - Callback: MacroDefined
+// CHECK-NEXT:   MacroNameTok: MACRO
+// CHECK-NEXT:   MacroDirective: MD_Define
+// CHECK-NEXT: - Callback: Ifdef
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:55:2"
+// CHECK-NEXT:   MacroNameTok: MACRO
+// CHECK-NEXT:   MacroDefinition: [(local)]
+// CHECK-NEXT: - Callback: Endif
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:56:2"
+// CHECK-NEXT:   IfLoc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:55:2"
+// CHECK-NEXT: - Callback: Ifdef
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:57:2"
+// CHECK-NEXT:   MacroNameTok: NO_MACRO
+// CHECK-NEXT:   MacroDefinition: []
+// CHECK-NEXT: - Callback: Endif
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:58:2"
+// CHECK-NEXT:   IfLoc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:57:2"
+// CHECK-NEXT: - Callback: SourceRangeSkipped
+// CHECK-NEXT:   Range: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:57:2", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:58:2"]
+// CHECK-NEXT: - Callback: Ifndef
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:59:2"
+// CHECK-NEXT:   MacroNameTok: MACRO
+// CHECK-NEXT:   MacroDefinition: [(local)]
+// CHECK-NEXT: - Callback: Endif
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:60:2"
+// CHECK-NEXT:   IfLoc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:59:2"
+// CHECK-NEXT: - Callback: SourceRangeSkipped
+// CHECK-NEXT:   Range: ["{{.*}}{{[/\\]}}pp-trace-conditional.cpp:59:2", "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:60:2"]
+// CHECK-NEXT: - Callback: Ifndef
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:61:2"
+// CHECK-NEXT:   MacroNameTok: NO_MACRO
+// CHECK-NEXT:   MacroDefinition: []
+// CHECK-NEXT: - Callback: Endif
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:62:2"
+// CHECK-NEXT:   IfLoc: "{{.*}}{{[/\\]}}pp-trace-conditional.cpp:61:2"
+// CHECK-NEXT: - Callback: EndOfMainFile
+// CHECK-NEXT: ...
diff --git a/test/pp-trace/pp-trace-ident.cpp b/test/pp-trace/pp-trace-ident.cpp
new file mode 100644 (file)
index 0000000..9981c39
--- /dev/null
@@ -0,0 +1,10 @@
+// RUN: pp-trace -ignore FileChanged,MacroDefined %s -undef -target x86_64 -std=c++11 | FileCheck --strict-whitespace %s
+
+#ident "$Id$"
+
+// CHECK: ---
+// CHECK-NEXT: - Callback: Ident
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-ident.cpp:3:2"
+// CHECK-NEXT:   Str: "$Id$"
+// CHECK-NEXT: - Callback: EndOfMainFile
+// CHECK-NEXT: ...
diff --git a/test/pp-trace/pp-trace-include.cpp b/test/pp-trace/pp-trace-include.cpp
new file mode 100644 (file)
index 0000000..7d925d8
--- /dev/null
@@ -0,0 +1,141 @@
+// RUN: pp-trace %s -undef -target x86_64 -std=c++11 | FileCheck --strict-whitespace %s
+
+#include "Inputs/Level1A.h"
+#include "Inputs/Level1B.h"
+
+// CHECK: ---
+// CHECK-NEXT: - Callback: FileChanged
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-include.cpp:1:1"
+// CHECK-NEXT:   Reason: EnterFile
+// CHECK-NEXT:   FileType: C_User
+// CHECK-NEXT:   PrevFID: (invalid)
+// CHECK-NEXT: - Callback: FileChanged
+// CHECK-NEXT:   Loc: "<built-in>:1:1"
+// CHECK-NEXT:   Reason: EnterFile
+// CHECK-NEXT:   FileType: C_User
+// CHECK-NEXT:   PrevFID: (invalid)
+// CHECK-NEXT: - Callback: FileChanged
+// CHECK-NEXT:   Loc: "<built-in>:1:1"
+// CHECK-NEXT:   Reason: RenameFile
+// CHECK-NEXT:   FileType: C_System
+// CHECK-NEXT:   PrevFID: (invalid)
+// CHECK-NEXT: - Callback: MacroDefined
+// CHECK:        MacroNameTok: __STDC__
+// CHECK-NEXT:   MacroDirective: MD_Define
+// CHECK-NEXT: - Callback: MacroDefined
+// CHECK:        MacroNameTok: __STDC_HOSTED__
+// CHECK-NEXT:   MacroDirective: MD_Define
+// CHECK-NEXT: - Callback: MacroDefined
+// CHECK:        MacroNameTok: __cplusplus
+// CHECK-NEXT:   MacroDirective: MD_Define
+// CHECK-NEXT: - Callback: MacroDefined
+// CHECK:        MacroNameTok: __STDC_UTF_16__
+// CHECK-NEXT:   MacroDirective: MD_Define
+// CHECK-NEXT: - Callback: MacroDefined
+// CHECK:        MacroNameTok: __STDC_UTF_32__
+// CHECK-NEXT:   MacroDirective: MD_Define
+// CHECK:      - Callback: FileChanged
+// CHECK-NEXT:   Loc: "<command line>:1:1"
+// CHECK-NEXT:   Reason: EnterFile
+// CHECK-NEXT:   FileType: C_User
+// CHECK-NEXT:   PrevFID: (invalid)
+// CHECK-NEXT: - Callback: FileChanged
+// CHECK-NEXT:   Loc: "<built-in>:1:1"
+// CHECK-NEXT:   Reason: ExitFile
+// CHECK-NEXT:   FileType: C_User
+// CHECK-NEXT:   PrevFID: (invalid)
+// CHECK-NEXT: - Callback: FileChanged
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-include.cpp:1:1"
+// CHECK-NEXT:   Reason: ExitFile
+// CHECK-NEXT:   FileType: C_User
+// CHECK-NEXT:   PrevFID: (getFileEntryForID failed)
+// CHECK-NEXT: - Callback: InclusionDirective
+// CHECK-NEXT:   IncludeTok: include
+// CHECK-NEXT:   FileName: "Inputs/Level1A.h"
+// CHECK-NEXT:   IsAngled: false
+// CHECK-NEXT:   FilenameRange: "Inputs/Level1A.h"
+// CHECK-NEXT:   File: "{{.*}}{{[/\\]}}Inputs/Level1A.h"
+// CHECK-NEXT:   SearchPath: "{{.*}}{{[/\\]}}pp-trace"
+// CHECK-NEXT:   RelativePath: "Inputs/Level1A.h"
+// CHECK-NEXT:   Imported: (null)
+// CHECK-NEXT: - Callback: FileChanged
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}Inputs/Level1A.h:1:1"
+// CHECK-NEXT:   Reason: EnterFile
+// CHECK-NEXT:   FileType: C_User
+// CHECK-NEXT:   PrevFID: (invalid)
+// CHECK-NEXT: - Callback: InclusionDirective
+// CHECK-NEXT:   IncludeTok: include
+// CHECK-NEXT:   FileName: "Level2A.h"
+// CHECK-NEXT:   IsAngled: false
+// CHECK-NEXT:   FilenameRange: "Level2A.h"
+// CHECK-NEXT:   File: "{{.*}}{{[/\\]}}Inputs/Level2A.h"
+// CHECK-NEXT:   SearchPath: "{{.*}}{{[/\\]}}Inputs"
+// CHECK-NEXT:   RelativePath: "Level2A.h"
+// CHECK-NEXT:   Imported: (null)
+// CHECK-NEXT: - Callback: FileChanged
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}Inputs/Level2A.h:1:1"
+// CHECK-NEXT:   Reason: EnterFile
+// CHECK-NEXT:   FileType: C_User
+// CHECK-NEXT:   PrevFID: (invalid)
+// CHECK-NEXT: - Callback: MacroDefined
+// CHECK-NEXT:   MacroNameTok: MACRO_2A
+// CHECK-NEXT:   MacroDirective: MD_Define
+// CHECK-NEXT: - Callback: FileChanged
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}Inputs/Level1A.h:2:1"
+// CHECK-NEXT:   Reason: ExitFile
+// CHECK-NEXT:   FileType: C_User
+// CHECK-NEXT:   PrevFID: "{{.*}}{{[/\\]}}Inputs/Level2A.h"
+// CHECK-NEXT: - Callback: MacroDefined
+// CHECK-NEXT:   MacroNameTok: MACRO_1A
+// CHECK-NEXT:   MacroDirective: MD_Define
+// CHECK-NEXT: - Callback: FileChanged
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-include.cpp:4:1"
+// CHECK-NEXT:   Reason: ExitFile
+// CHECK-NEXT:   FileType: C_User
+// CHECK-NEXT:   PrevFID: "{{.*}}{{[/\\]}}Inputs/Level1A.h"
+// CHECK-NEXT: - Callback: InclusionDirective
+// CHECK-NEXT:   IncludeTok: include
+// CHECK-NEXT:   FileName: "Inputs/Level1B.h"
+// CHECK-NEXT:   IsAngled: false
+// CHECK-NEXT:   FilenameRange: "Inputs/Level1B.h"
+// CHECK-NEXT:   File: "{{.*}}{{[/\\]}}Inputs/Level1B.h"
+// CHECK-NEXT:   SearchPath: "{{.*}}{{[/\\]}}pp-trace"
+// CHECK-NEXT:   RelativePath: "Inputs/Level1B.h"
+// CHECK-NEXT:   Imported: (null)
+// CHECK-NEXT: - Callback: FileChanged
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}Inputs/Level1B.h:1:1"
+// CHECK-NEXT:   Reason: EnterFile
+// CHECK-NEXT:   FileType: C_User
+// CHECK-NEXT:   PrevFID: (invalid)
+// CHECK-NEXT: - Callback: InclusionDirective
+// CHECK-NEXT:   IncludeTok: include
+// CHECK-NEXT:   FileName: "Level2B.h"
+// CHECK-NEXT:   IsAngled: false
+// CHECK-NEXT:   FilenameRange: "Level2B.h"
+// CHECK-NEXT:   File: "{{.*}}{{[/\\]}}Inputs/Level2B.h"
+// CHECK-NEXT:   SearchPath: "{{.*}}{{[/\\]}}Inputs"
+// CHECK-NEXT:   RelativePath: "Level2B.h"
+// CHECK-NEXT:   Imported: (null)
+// CHECK-NEXT: - Callback: FileChanged
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}Inputs/Level2B.h:1:1"
+// CHECK-NEXT:   Reason: EnterFile
+// CHECK-NEXT:   FileType: C_User
+// CHECK-NEXT:   PrevFID: (invalid)
+// CHECK-NEXT: - Callback: MacroDefined
+// CHECK-NEXT:   MacroNameTok: MACRO_2B
+// CHECK-NEXT:   MacroDirective: MD_Define
+// CHECK-NEXT: - Callback: FileChanged
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}Inputs/Level1B.h:2:1"
+// CHECK-NEXT:   Reason: ExitFile
+// CHECK-NEXT:   FileType: C_User
+// CHECK-NEXT:   PrevFID: "{{.*}}{{[/\\]}}Inputs/Level2B.h"
+// CHECK-NEXT: - Callback: MacroDefined
+// CHECK-NEXT:   MacroNameTok: MACRO_1B
+// CHECK-NEXT:   MacroDirective: MD_Define
+// CHECK-NEXT: - Callback: FileChanged
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-include.cpp:5:1"
+// CHECK-NEXT:   Reason: ExitFile
+// CHECK-NEXT:   FileType: C_User
+// CHECK-NEXT:   PrevFID: "{{.*}}{{[/\\]}}Inputs/Level1B.h"
+// CHECK-NEXT: - Callback: EndOfMainFile
+// CHECK-NEXT: ...
diff --git a/test/pp-trace/pp-trace-macro.cpp b/test/pp-trace/pp-trace-macro.cpp
new file mode 100644 (file)
index 0000000..85e2088
--- /dev/null
@@ -0,0 +1,101 @@
+// RUN: pp-trace -ignore FileChanged %s -undef -target x86_64 -std=c++11 | FileCheck --strict-whitespace %s
+
+#define MACRO 1
+int i = MACRO;
+#if defined(MACRO)
+#endif
+#undef MACRO
+#if defined(MACRO)
+#endif
+#define FUNCMACRO(ARG1) ARG1
+int j = FUNCMACRO(1);
+#define X X_IMPL(a+y,b) X_IMPL2(c)
+#define X_IMPL(p1,p2)
+#define X_IMPL2(p1)
+X
+
+// CHECK: ---
+// CHECK-NEXT: - Callback: MacroDefined
+// CHECK:        MacroNameTok: __STDC__
+// CHECK-NEXT:   MacroDirective: MD_Define
+// CHECK-NEXT: - Callback: MacroDefined
+// CHECK:        MacroNameTok: __STDC_HOSTED__
+// CHECK-NEXT:   MacroDirective: MD_Define
+// CHECK-NEXT: - Callback: MacroDefined
+// CHECK:        MacroNameTok: __cplusplus
+// CHECK-NEXT:   MacroDirective: MD_Define
+// CHECK-NEXT: - Callback: MacroDefined
+// CHECK:        MacroNameTok: __STDC_UTF_16__
+// CHECK-NEXT:   MacroDirective: MD_Define
+// CHECK-NEXT: - Callback: MacroDefined
+// CHECK:        MacroNameTok: __STDC_UTF_32__
+// CHECK-NEXT:   MacroDirective: MD_Define
+// CHECK:      - Callback: MacroDefined
+// CHECK-NEXT:   MacroNameTok: MACRO
+// CHECK-NEXT:   MacroDirective: MD_Define
+// CHECK-NEXT: - Callback: MacroExpands
+// CHECK-NEXT:   MacroNameTok: MACRO
+// CHECK-NEXT:   MacroDefinition: [(local)]
+// CHECK-NEXT:   Range: ["{{.*}}{{[/\\]}}pp-trace-macro.cpp:4:9", "{{.*}}{{[/\\]}}pp-trace-macro.cpp:4:9"]
+// CHECK-NEXT:   Args: (null)
+// CHECK-NEXT: - Callback: Defined
+// CHECK-NEXT:   MacroNameTok: MACRO
+// CHECK-NEXT:   MacroDefinition: [(local)]
+// CHECK-NEXT:   Range: ["{{.*}}{{[/\\]}}pp-trace-macro.cpp:5:5", "{{.*}}{{[/\\]}}pp-trace-macro.cpp:5:19"]
+// CHECK-NEXT: - Callback: If
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-macro.cpp:5:2"
+// CHECK-NEXT:   ConditionRange: ["{{.*}}{{[/\\]}}pp-trace-macro.cpp:5:4", "{{.*}}{{[/\\]}}pp-trace-macro.cpp:6:1"]
+// CHECK-NEXT:   ConditionValue: CVK_True
+// CHECK-NEXT: - Callback: Endif
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-macro.cpp:6:2"
+// CHECK-NEXT:   IfLoc: "{{.*}}{{[/\\]}}pp-trace-macro.cpp:5:2"
+// CHECK-NEXT: - Callback: MacroUndefined
+// CHECK-NEXT:   MacroNameTok: MACRO
+// CHECK-NEXT:   MacroDefinition: [(local)]
+// CHECK-NEXT: - Callback: Defined
+// CHECK-NEXT:   MacroNameTok: MACRO
+// CHECK-NEXT:   MacroDefinition: []
+// CHECK-NEXT:   Range: ["{{.*}}{{[/\\]}}pp-trace-macro.cpp:8:5", "{{.*}}{{[/\\]}}pp-trace-macro.cpp:8:19"]
+// CHECK-NEXT: - Callback: If
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-macro.cpp:8:2"
+// CHECK-NEXT:   ConditionRange: ["{{.*}}{{[/\\]}}pp-trace-macro.cpp:8:4", "{{.*}}{{[/\\]}}pp-trace-macro.cpp:9:1"]
+// CHECK-NEXT:   ConditionValue: CVK_False
+// CHECK-NEXT: - Callback: Endif
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-macro.cpp:9:2"
+// CHECK-NEXT:   IfLoc: "{{.*}}{{[/\\]}}pp-trace-macro.cpp:8:2"
+// CHECK-NEXT: - Callback: SourceRangeSkipped
+// CHECK-NEXT:   Range: ["{{.*}}{{[/\\]}}pp-trace-macro.cpp:8:2", "{{.*}}{{[/\\]}}pp-trace-macro.cpp:9:2"]
+// CHECK-NEXT: - Callback: MacroDefined
+// CHECK-NEXT:   MacroNameTok: FUNCMACRO
+// CHECK-NEXT:   MacroDirective: MD_Define
+// CHECK-NEXT: - Callback: MacroExpands
+// CHECK-NEXT:   MacroNameTok: FUNCMACRO
+// CHECK-NEXT:   MacroDefinition: [(local)]
+// CHECK-NEXT:   Range: ["{{.*}}{{[/\\]}}pp-trace-macro.cpp:11:9", "{{.*}}{{[/\\]}}pp-trace-macro.cpp:11:20"]
+// CHECK-NEXT:   Args: [1]
+// CHECK-NEXT: - Callback: MacroDefined
+// CHECK-NEXT:   MacroNameTok: X
+// CHECK-NEXT:   MacroDirective: MD_Define
+// CHECK-NEXT: - Callback: MacroDefined
+// CHECK-NEXT:   MacroNameTok: X_IMPL
+// CHECK-NEXT:   MacroDirective: MD_Define
+// CHECK-NEXT: - Callback: MacroDefined
+// CHECK-NEXT:   MacroNameTok: X_IMPL2
+// CHECK-NEXT:   MacroDirective: MD_Define
+// CHECK-NEXT: - Callback: MacroExpands
+// CHECK-NEXT:   MacroNameTok: X
+// CHECK-NEXT:   MacroDefinition: [(local)]
+// CHECK-NEXT:   Range: ["{{.*}}{{[/\\]}}pp-trace-macro.cpp:15:1", "{{.*}}{{[/\\]}}pp-trace-macro.cpp:15:1"]
+// CHECK-NEXT:   Args: (null)
+// CHECK-NEXT: - Callback: MacroExpands
+// CHECK-NEXT:   MacroNameTok: X_IMPL
+// CHECK-NEXT:   MacroDefinition: [(local)]
+// CHECK-NEXT:   Range: [(nonfile), (nonfile)]
+// CHECK-NEXT:   Args: [a <plus> y, b]
+// CHECK-NEXT: - Callback: MacroExpands
+// CHECK-NEXT:   MacroNameTok: X_IMPL2
+// CHECK-NEXT:   MacroDefinition: [(local)]
+// CHECK-NEXT:   Range: [(nonfile), (nonfile)]
+// CHECK-NEXT:   Args: [c]
+// CHECK-NEXT: - Callback: EndOfMainFile
+// CHECK-NEXT: ...
diff --git a/test/pp-trace/pp-trace-modules.cpp b/test/pp-trace/pp-trace-modules.cpp
new file mode 100644 (file)
index 0000000..5e9e1de
--- /dev/null
@@ -0,0 +1,20 @@
+// RUN: rm -rf %t
+// RUN: pp-trace -ignore FileChanged,MacroDefined %s -x objective-c++ -undef -target x86_64 -std=c++11 -fmodules -fcxx-modules -fmodules-cache-path=%t -I%S -I%S/Input | FileCheck --strict-whitespace %s
+
+// CHECK: ---
+
+@import Level1A;
+
+// CHECK-NEXT: - Callback: moduleImport
+// CHECK-NEXT:   ImportLoc: "{{.*}}{{[/\\]}}pp-trace-modules.cpp:[[@LINE-3]]:2"
+// CHECK-NEXT:   Path: [{Name: Level1A, Loc: "{{.*}}{{[/\\]}}pp-trace-modules.cpp:[[@LINE-4]]:9"}]
+// CHECK-NEXT:   Imported: Level1A
+
+@import Level1B.Level2B;
+
+// CHECK-NEXT: - Callback: moduleImport
+// CHECK-NEXT:   ImportLoc: "{{.*}}{{[/\\]}}pp-trace-modules.cpp:[[@LINE-3]]:2"
+// CHECK-NEXT:   Path: [{Name: Level1B, Loc: "{{.*}}{{[/\\]}}pp-trace-modules.cpp:[[@LINE-4]]:9"}, {Name: Level2B, Loc: "{{.*}}{{[/\\]}}pp-trace-modules.cpp:[[@LINE-4]]:17"}]
+// CHECK-NEXT:   Imported: Level2B
+// CHECK-NEXT: - Callback: EndOfMainFile
+// CHECK-NEXT: ...
diff --git a/test/pp-trace/pp-trace-pragma-general.cpp b/test/pp-trace/pp-trace-pragma-general.cpp
new file mode 100644 (file)
index 0000000..ade06c9
--- /dev/null
@@ -0,0 +1,107 @@
+// RUN: pp-trace -ignore FileChanged,MacroDefined %s | FileCheck --strict-whitespace %s
+
+#pragma clang diagnostic push
+#pragma clang diagnostic pop
+#pragma clang diagnostic ignored "-Wformat"
+#pragma clang diagnostic warning "-Wformat"
+#pragma clang diagnostic error "-Wformat"
+#pragma clang diagnostic fatal "-Wformat"
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic pop
+#pragma GCC diagnostic ignored "-Wformat"
+#pragma GCC diagnostic warning "-Wformat"
+#pragma GCC diagnostic error "-Wformat"
+#pragma GCC diagnostic fatal "-Wformat"
+
+// CHECK: ---
+// CHECK-NEXT: - Callback: PragmaDirective
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-general.cpp:3:1"
+// CHECK-NEXT:   Introducer: PIK_HashPragma
+// CHECK-NEXT: - Callback: PragmaDiagnosticPush
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-general.cpp:3:15"
+// CHECK-NEXT:   Namespace: clang
+// CHECK-NEXT: - Callback: PragmaDirective
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-general.cpp:4:1"
+// CHECK-NEXT:   Introducer: PIK_HashPragma
+// CHECK-NEXT: - Callback: PragmaDiagnosticPop
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-general.cpp:4:15"
+// CHECK-NEXT:   Namespace: clang
+// CHECK-NEXT: - Callback: PragmaDirective
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-general.cpp:5:1"
+// CHECK-NEXT:   Introducer: PIK_HashPragma
+// CHECK-NEXT: - Callback: PragmaDiagnostic
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-general.cpp:5:15"
+// CHECK-NEXT:   Namespace: clang
+// CHECK-NEXT:   Mapping: MAP_IGNORE
+// CHECK-NEXT:   Str: -Wformat
+// CHECK-NEXT: - Callback: PragmaDirective
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-general.cpp:6:1"
+// CHECK-NEXT:   Introducer: PIK_HashPragma
+// CHECK-NEXT: - Callback: PragmaDiagnostic
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-general.cpp:6:15"
+// CHECK-NEXT:   Namespace: clang
+// CHECK-NEXT:   Mapping: MAP_WARNING
+// CHECK-NEXT:   Str: -Wformat
+// CHECK-NEXT: - Callback: PragmaDirective
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-general.cpp:7:1"
+// CHECK-NEXT:   Introducer: PIK_HashPragma
+// CHECK-NEXT: - Callback: PragmaDiagnostic
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-general.cpp:7:15"
+// CHECK-NEXT:   Namespace: clang
+// CHECK-NEXT:   Mapping: MAP_ERROR
+// CHECK-NEXT:   Str: -Wformat
+// CHECK-NEXT: - Callback: PragmaDirective
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-general.cpp:8:1"
+// CHECK-NEXT:   Introducer: PIK_HashPragma
+// CHECK-NEXT: - Callback: PragmaDiagnostic
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-general.cpp:8:15"
+// CHECK-NEXT:   Namespace: clang
+// CHECK-NEXT:   Mapping: MAP_FATAL
+// CHECK-NEXT:   Str: -Wformat
+// CHECK-NEXT: - Callback: PragmaDirective
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-general.cpp:10:1"
+// CHECK-NEXT:   Introducer: PIK_HashPragma
+// CHECK-NEXT: - Callback: PragmaDiagnosticPush
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-general.cpp:10:13"
+// CHECK-NEXT:   Namespace: GCC
+// CHECK-NEXT: - Callback: PragmaDirective
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-general.cpp:11:1"
+// CHECK-NEXT:   Introducer: PIK_HashPragma
+// CHECK-NEXT: - Callback: PragmaDiagnosticPop
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-general.cpp:11:13"
+// CHECK-NEXT:   Namespace: GCC
+// CHECK-NEXT: - Callback: PragmaDirective
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-general.cpp:12:1"
+// CHECK-NEXT:   Introducer: PIK_HashPragma
+// CHECK-NEXT: - Callback: PragmaDiagnostic
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-general.cpp:12:13"
+// CHECK-NEXT:   Namespace: GCC
+// CHECK-NEXT:   Mapping: MAP_IGNORE
+// CHECK-NEXT:   Str: -Wformat
+// CHECK-NEXT: - Callback: PragmaDirective
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-general.cpp:13:1"
+// CHECK-NEXT:   Introducer: PIK_HashPragma
+// CHECK-NEXT: - Callback: PragmaDiagnostic
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-general.cpp:13:13"
+// CHECK-NEXT:   Namespace: GCC
+// CHECK-NEXT:   Mapping: MAP_WARNING
+// CHECK-NEXT:   Str: -Wformat
+// CHECK-NEXT: - Callback: PragmaDirective
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-general.cpp:14:1"
+// CHECK-NEXT:   Introducer: PIK_HashPragma
+// CHECK-NEXT: - Callback: PragmaDiagnostic
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-general.cpp:14:13"
+// CHECK-NEXT:   Namespace: GCC
+// CHECK-NEXT:   Mapping: MAP_ERROR
+// CHECK-NEXT:   Str: -Wformat
+// CHECK-NEXT: - Callback: PragmaDirective
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-general.cpp:15:1"
+// CHECK-NEXT:   Introducer: PIK_HashPragma
+// CHECK-NEXT: - Callback: PragmaDiagnostic
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-general.cpp:15:13"
+// CHECK-NEXT:   Namespace: GCC
+// CHECK-NEXT:   Mapping: MAP_FATAL
+// CHECK-NEXT:   Str: -Wformat
+// CHECK-NEXT: - Callback: EndOfMainFile
+// CHECK-NEXT: ...
diff --git a/test/pp-trace/pp-trace-pragma-ms.cpp b/test/pp-trace/pp-trace-pragma-ms.cpp
new file mode 100644 (file)
index 0000000..2293747
--- /dev/null
@@ -0,0 +1,100 @@
+// RUN: pp-trace -ignore FileChanged,MacroDefined %s -target x86_64 -fms-extensions -w | FileCheck --strict-whitespace %s
+
+#pragma comment(compiler, "compiler comment")
+#pragma comment(exestr, "exestr comment")
+#pragma comment(lib, "lib comment")
+#pragma comment(linker, "linker comment")
+#pragma comment(user, "user comment")
+
+#pragma detect_mismatch("name argument", "value argument")
+
+#pragma __debug(assert)
+
+#pragma message("message argument")
+
+#pragma warning(push, 1)
+#pragma warning(pop)
+#pragma warning(disable : 1 2 3 ; error : 4 5 6 ; suppress : 7 8 9)
+
+// CHECK: ---
+// CHECK-NEXT: - Callback: PragmaDirective
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-ms.cpp:3:1"
+// CHECK-NEXT:   Introducer: PIK_HashPragma
+// CHECK-NEXT: - Callback: PragmaComment
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-ms.cpp:3:9"
+// CHECK-NEXT:   Kind: compiler
+// CHECK-NEXT:   Str: compiler comment
+// CHECK-NEXT: - Callback: PragmaDirective
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-ms.cpp:4:1"
+// CHECK-NEXT:   Introducer: PIK_HashPragma
+// CHECK-NEXT: - Callback: PragmaComment
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-ms.cpp:4:9"
+// CHECK-NEXT:   Kind: exestr
+// CHECK-NEXT:   Str: exestr comment
+// CHECK-NEXT: - Callback: PragmaDirective
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-ms.cpp:5:1"
+// CHECK-NEXT:   Introducer: PIK_HashPragma
+// CHECK-NEXT: - Callback: PragmaComment
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-ms.cpp:5:9"
+// CHECK-NEXT:   Kind: lib
+// CHECK-NEXT:   Str: lib comment
+// CHECK-NEXT: - Callback: PragmaDirective
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-ms.cpp:6:1"
+// CHECK-NEXT:   Introducer: PIK_HashPragma
+// CHECK-NEXT: - Callback: PragmaComment
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-ms.cpp:6:9"
+// CHECK-NEXT:   Kind: linker
+// CHECK-NEXT:   Str: linker comment
+// CHECK-NEXT: - Callback: PragmaDirective
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-ms.cpp:7:1"
+// CHECK-NEXT:   Introducer: PIK_HashPragma
+// CHECK-NEXT: - Callback: PragmaComment
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-ms.cpp:7:9"
+// CHECK-NEXT:   Kind: user
+// CHECK-NEXT:   Str: user comment
+// CHECK-NEXT: - Callback: PragmaDirective
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-ms.cpp:9:1"
+// CHECK-NEXT:   Introducer: PIK_HashPragma
+// CHECK-NEXT: - Callback: PragmaDetectMismatch
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-ms.cpp:9:9"
+// CHECK-NEXT:   Name: name argument
+// CHECK-NEXT:   Value: value argument
+// CHECK-NEXT: - Callback: PragmaDirective
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-ms.cpp:11:1"
+// CHECK-NEXT:   Introducer: PIK_HashPragma
+// CHECK-NEXT: - Callback: PragmaDirective
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-ms.cpp:13:1"
+// CHECK-NEXT:   Introducer: PIK_HashPragma
+// CHECK-NEXT: - Callback: PragmaMessage
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-ms.cpp:13:9"
+// CHECK-NEXT:   Namespace: 
+// CHECK-NEXT:   Kind: PMK_Message
+// CHECK-NEXT:   Str: message argument
+// CHECK-NEXT: - Callback: PragmaDirective
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-ms.cpp:15:1"
+// CHECK-NEXT:   Introducer: PIK_HashPragma
+// CHECK-NEXT: - Callback: PragmaWarningPush
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-ms.cpp:15:9"
+// CHECK-NEXT:   Level: 1
+// CHECK-NEXT: - Callback: PragmaDirective
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-ms.cpp:16:1"
+// CHECK-NEXT:   Introducer: PIK_HashPragma
+// CHECK-NEXT: - Callback: PragmaWarningPop
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-ms.cpp:16:9"
+// CHECK-NEXT: - Callback: PragmaDirective
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-ms.cpp:17:1"
+// CHECK-NEXT:   Introducer: PIK_HashPragma
+// CHECK-NEXT: - Callback: PragmaWarning
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-ms.cpp:17:9"
+// CHECK-NEXT:   WarningSpec: disable
+// CHECK-NEXT:   Ids: [1, 2, 3]
+// CHECK-NEXT: - Callback: PragmaWarning
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-ms.cpp:17:9"
+// CHECK-NEXT:   WarningSpec: error
+// CHECK-NEXT:   Ids: [4, 5, 6]
+// CHECK-NEXT: - Callback: PragmaWarning
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-ms.cpp:17:9"
+// CHECK-NEXT:   WarningSpec: suppress
+// CHECK-NEXT:   Ids: [7, 8, 9]
+// CHECK-NEXT: - Callback: EndOfMainFile
+// CHECK-NEXT: ...
diff --git a/test/pp-trace/pp-trace-pragma-opencl.cpp b/test/pp-trace/pp-trace-pragma-opencl.cpp
new file mode 100644 (file)
index 0000000..cfd72c0
--- /dev/null
@@ -0,0 +1,33 @@
+// RUN: pp-trace -ignore FileChanged,MacroDefined %s -x cl | FileCheck --strict-whitespace %s
+
+#pragma OPENCL EXTENSION all : disable
+#pragma OPENCL EXTENSION cl_khr_int64_base_atomics : disable
+#pragma OPENCL EXTENSION cl_khr_int64_base_atomics : enable
+
+// CHECK: ---
+// CHECK-NEXT: - Callback: PragmaDirective
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-opencl.cpp:3:1"
+// CHECK-NEXT:   Introducer: PIK_HashPragma
+// CHECK-NEXT: - Callback: PragmaOpenCLExtension
+// CHECK-NEXT:   NameLoc: "{{.*}}{{[/\\]}}pp-trace-pragma-opencl.cpp:3:26"
+// CHECK-NEXT:   Name: all
+// CHECK-NEXT:   StateLoc: "{{.*}}{{[/\\]}}pp-trace-pragma-opencl.cpp:3:32"
+// CHECK-NEXT:   State: 0
+// CHECK-NEXT: - Callback: PragmaDirective
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-opencl.cpp:4:1"
+// CHECK-NEXT:   Introducer: PIK_HashPragma
+// CHECK-NEXT: - Callback: PragmaOpenCLExtension
+// CHECK-NEXT:   NameLoc: "{{.*}}{{[/\\]}}pp-trace-pragma-opencl.cpp:4:26"
+// CHECK-NEXT:   Name: cl_khr_int64_base_atomics
+// CHECK-NEXT:   StateLoc: "{{.*}}{{[/\\]}}pp-trace-pragma-opencl.cpp:4:54"
+// CHECK-NEXT:   State: 0
+// CHECK-NEXT: - Callback: PragmaDirective
+// CHECK-NEXT:   Loc: "{{.*}}{{[/\\]}}pp-trace-pragma-opencl.cpp:5:1"
+// CHECK-NEXT:   Introducer: PIK_HashPragma
+// CHECK-NEXT: - Callback: PragmaOpenCLExtension
+// CHECK-NEXT:   NameLoc: "{{.*}}{{[/\\]}}pp-trace-pragma-opencl.cpp:5:26"
+// CHECK-NEXT:   Name: cl_khr_int64_base_atomics
+// CHECK-NEXT:   StateLoc: "{{.*}}{{[/\\]}}pp-trace-pragma-opencl.cpp:5:54"
+// CHECK-NEXT:   State: 1
+// CHECK-NEXT: - Callback: EndOfMainFile
+// CHECK-NEXT: ...
diff --git a/tool-template/CMakeLists.txt b/tool-template/CMakeLists.txt
new file mode 100644 (file)
index 0000000..8223e4c
--- /dev/null
@@ -0,0 +1,15 @@
+set(LLVM_LINK_COMPONENTS
+  Support
+  )
+
+add_clang_executable(tool-template
+  ToolTemplate.cpp
+  )
+
+target_link_libraries(tool-template
+  clangAST
+  clangASTMatchers
+  clangBasic
+  clangFrontend
+  clangTooling
+  )
diff --git a/tool-template/ToolTemplate.cpp b/tool-template/ToolTemplate.cpp
new file mode 100644 (file)
index 0000000..f97575e
--- /dev/null
@@ -0,0 +1,90 @@
+//===---- tools/extra/ToolTemplate.cpp - Template for refactoring tool ----===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+//  This file implements an empty refactoring tool using the clang tooling.
+//  The goal is to lower the "barrier to entry" for writing refactoring tools.
+//
+//  Usage:
+//  tool-template <cmake-output-dir> <file1> <file2> ...
+//
+//  Where <cmake-output-dir> is a CMake build directory in which a file named
+//  compile_commands.json exists (enable -DCMAKE_EXPORT_COMPILE_COMMANDS in
+//  CMake to get this output).
+//
+//  <file1> ... specify the paths of files in the CMake source tree. This path
+//  is looked up in the compile command database. If the path of a file is
+//  absolute, it needs to point into CMake's source tree. If the path is
+//  relative, the current working directory needs to be in the CMake source
+//  tree and the file must be in a subdirectory of the current working
+//  directory. "./" prefixes in the relative files will be automatically
+//  removed, but the rest of a relative path must be a suffix of a path in
+//  the compile command line database.
+//
+//  For example, to use tool-template on all files in a subtree of the
+//  source tree, use:
+//
+//    /path/in/subtree $ find . -name '*.cpp'|
+//        xargs tool-template /path/to/build
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Frontend/FrontendActions.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Tooling/CommonOptionsParser.h"
+#include "clang/Tooling/Refactoring.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Signals.h"
+
+using namespace clang;
+using namespace clang::ast_matchers;
+using namespace clang::tooling;
+using namespace llvm;
+
+namespace {
+class ToolTemplateCallback : public MatchFinder::MatchCallback {
+public:
+  ToolTemplateCallback(std::map<std::string, Replacements> *Replace)
+      : Replace(Replace) {}
+
+  void run(const MatchFinder::MatchResult &Result) override {
+    // TODO: This routine will get called for each thing that the matchers
+    // find.
+    // At this point, you can examine the match, and do whatever you want,
+    // including replacing the matched text with other text
+    (void)Replace; // This to prevent an "unused member variable" warning;
+  }
+
+private:
+  std::map<std::string, Replacements> *Replace;
+};
+} // end anonymous namespace
+
+// Set up the command line options
+static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage);
+static cl::OptionCategory ToolTemplateCategory("tool-template options");
+
+int main(int argc, const char **argv) {
+  llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
+  CommonOptionsParser OptionsParser(argc, argv, ToolTemplateCategory);
+  RefactoringTool Tool(OptionsParser.getCompilations(),
+                       OptionsParser.getSourcePathList());
+  ast_matchers::MatchFinder Finder;
+  ToolTemplateCallback Callback(&Tool.getReplacements());
+
+  // TODO: Put your matchers here.
+  // Use Finder.addMatcher(...) to define the patterns in the AST that you
+  // want to match against. You are not limited to just one matcher!
+
+  return Tool.run(newFrontendActionFactory(&Finder).get());
+}
diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt
new file mode 100644 (file)
index 0000000..49978d4
--- /dev/null
@@ -0,0 +1,14 @@
+add_custom_target(ExtraToolsUnitTests)
+set_target_properties(ExtraToolsUnitTests PROPERTIES FOLDER "Extra Tools Unit Tests")
+
+function(add_extra_unittest test_dirname)
+  add_unittest(ExtraToolsUnitTests ${test_dirname} ${ARGN})
+endfunction()
+
+add_subdirectory(change-namespace)
+add_subdirectory(clang-apply-replacements)
+add_subdirectory(clang-move)
+add_subdirectory(clang-query)
+add_subdirectory(clang-tidy)
+add_subdirectory(clangd)
+add_subdirectory(include-fixer)
diff --git a/unittests/change-namespace/CMakeLists.txt b/unittests/change-namespace/CMakeLists.txt
new file mode 100644 (file)
index 0000000..8091cf2
--- /dev/null
@@ -0,0 +1,28 @@
+set(LLVM_LINK_COMPONENTS
+  support
+  )
+
+get_filename_component(CHANGE_NAMESPACE_SOURCE_DIR
+  ${CMAKE_CURRENT_SOURCE_DIR}/../../change-namespace REALPATH)
+include_directories(
+  ${CHANGE_NAMESPACE_SOURCE_DIR}
+  )
+
+# We'd like clang/unittests/Tooling/RewriterTestContext.h in the test.
+include_directories(${CLANG_SOURCE_DIR})
+
+add_extra_unittest(ChangeNamespaceTests
+  ChangeNamespaceTests.cpp
+  )
+
+target_link_libraries(ChangeNamespaceTests
+  clangAST
+  clangASTMatchers
+  clangBasic
+  clangChangeNamespace
+  clangFormat
+  clangFrontend
+  clangRewrite
+  clangTooling
+  clangToolingCore
+  )
diff --git a/unittests/change-namespace/ChangeNamespaceTests.cpp b/unittests/change-namespace/ChangeNamespaceTests.cpp
new file mode 100644 (file)
index 0000000..dc8eeb7
--- /dev/null
@@ -0,0 +1,2098 @@
+//===-- ChangeNamespaceTests.cpp - Change namespace unit tests ---*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ChangeNamespace.h"
+#include "unittests/Tooling/RewriterTestContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Basic/FileSystemOptions.h"
+#include "clang/Basic/VirtualFileSystem.h"
+#include "clang/Format/Format.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/PCHContainerOperations.h"
+#include "clang/Tooling/Refactoring.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/IntrusiveRefCntPtr.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "gtest/gtest.h"
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace clang {
+namespace change_namespace {
+namespace {
+
+class ChangeNamespaceTest : public ::testing::Test {
+public:
+  std::string runChangeNamespaceOnCode(llvm::StringRef Code) {
+    clang::RewriterTestContext Context;
+    clang::FileID ID = Context.createInMemoryFile(FileName, Code);
+
+    std::map<std::string, tooling::Replacements> FileToReplacements;
+    change_namespace::ChangeNamespaceTool NamespaceTool(
+        OldNamespace, NewNamespace, FilePattern,
+        /*WhiteListedSymbolPatterns*/ {}, &FileToReplacements);
+    ast_matchers::MatchFinder Finder;
+    NamespaceTool.registerMatchers(&Finder);
+    std::unique_ptr<tooling::FrontendActionFactory> Factory =
+        tooling::newFrontendActionFactory(&Finder);
+    if (!tooling::runToolOnCodeWithArgs(Factory->create(), Code, {"-std=c++11"},
+                                        FileName))
+      return "";
+    formatAndApplyAllReplacements(FileToReplacements, Context.Rewrite);
+    return format(Context.getRewrittenText(ID));
+  }
+
+  std::string format(llvm::StringRef Code) {
+    tooling::Replacements Replaces = format::reformat(
+        format::getLLVMStyle(), Code, {tooling::Range(0, Code.size())});
+    auto ChangedCode = tooling::applyAllReplacements(Code, Replaces);
+    EXPECT_TRUE(static_cast<bool>(ChangedCode));
+    if (!ChangedCode) {
+      llvm::errs() << llvm::toString(ChangedCode.takeError());
+      return "";
+    }
+    return *ChangedCode;
+  }
+
+protected:
+  std::string FileName = "input.cc";
+  std::string OldNamespace = "na::nb";
+  std::string NewNamespace = "x::y";
+  std::string FilePattern = "input.cc";
+};
+
+TEST_F(ChangeNamespaceTest, NoMatchingNamespace) {
+  std::string Code = "namespace na {\n"
+                     "namespace nx {\n"
+                     "class A {};\n"
+                     "} // namespace nx\n"
+                     "} // namespace na\n";
+  std::string Expected = "namespace na {\n"
+                         "namespace nx {\n"
+                         "class A {};\n"
+                         "} // namespace nx\n"
+                         "} // namespace na\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, SimpleMoveWithoutTypeRefs) {
+  std::string Code = "namespace na {\n"
+                     "namespace nb {\n"
+                     "class A {};\n"
+                     "} // namespace nb\n"
+                     "} // namespace na\n";
+  std::string Expected = "\n\n"
+                         "namespace x {\n"
+                         "namespace y {\n"
+                         "class A {};\n"
+                         "} // namespace y\n"
+                         "} // namespace x\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, NewNsNestedInOldNs) {
+  NewNamespace = "na::nb::nc";
+  std::string Code = "namespace na {\n"
+                     "namespace nb {\n"
+                     "class A {};\n"
+                     "} // namespace nb\n"
+                     "} // namespace na\n";
+  std::string Expected = "namespace na {\n"
+                         "namespace nb {\n"
+                         "namespace nc {\n"
+                         "class A {};\n"
+                         "} // namespace nc\n"
+                         "} // namespace nb\n"
+                         "} // namespace na\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, NewNsNestedInOldNsWithSurroundingNewLines) {
+  NewNamespace = "na::nb::nc";
+  std::string Code = "namespace na {\n"
+                     "namespace nb {\n"
+                     "\n"
+                     "class A {};\n"
+                     "\n"
+                     "} // namespace nb\n"
+                     "} // namespace na\n";
+  std::string Expected = "namespace na {\n"
+                         "namespace nb {\n"
+                         "namespace nc {\n"
+                         "\n"
+                         "class A {};\n"
+                         "\n"
+                         "} // namespace nc\n"
+                         "} // namespace nb\n"
+                         "} // namespace na\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, MoveOldNsWithSurroundingNewLines) {
+  NewNamespace = "nx::ny";
+  std::string Code = "namespace na {\n"
+                     "namespace nb {\n"
+                     "\n"
+                     "class A {};\n"
+                     "\n"
+                     "} // namespace nb\n"
+                     "} // namespace na\n";
+  std::string Expected = "\n\n"
+                         "namespace nx {\n"
+                         "namespace ny {\n"
+                         "\n"
+                         "class A {};\n"
+                         "\n"
+                         "} // namespace ny\n"
+                         "} // namespace nx\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, NewNsNestedInOldNsWithRefs) {
+  NewNamespace = "na::nb::nc";
+  std::string Code = "namespace na {\n"
+                     "class A {};\n"
+                     "namespace nb {\n"
+                     "class B {};\n"
+                     "class C {};\n"
+                     "void f() { A a; B b; }\n"
+                     "} // namespace nb\n"
+                     "} // namespace na\n";
+  std::string Expected = "namespace na {\n"
+                         "class A {};\n"
+                         "namespace nb {\n"
+                         "namespace nc {\n"
+                         "class B {};\n"
+                         "class C {};\n"
+                         "void f() { A a; B b; }\n"
+                         "} // namespace nc\n"
+                         "} // namespace nb\n"
+                         "} // namespace na\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, SimpleMoveIntoAnotherNestedNamespace) {
+  NewNamespace = "na::nc";
+  std::string Code = "namespace na {\n"
+                     "namespace nb {\n"
+                     "class A {};\n"
+                     "} // namespace nb\n"
+                     "} // namespace na\n";
+  std::string Expected = "namespace na {\n"
+                         "\n"
+                         "namespace nc {\n"
+                         "class A {};\n"
+                         "} // namespace nc\n"
+                         "} // namespace na\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, MoveIntoAnotherNestedNamespaceWithRef) {
+  NewNamespace = "na::nc";
+  std::string Code = "namespace na {\n"
+                     "class A {};\n"
+                     "namespace nb {\n"
+                     "class X { A a; };\n"
+                     "} // namespace nb\n"
+                     "} // namespace na\n";
+  std::string Expected = "namespace na {\n"
+                         "class A {};\n"
+                         "\n"
+                         "namespace nc {\n"
+                         "class X { A a; };\n"
+                         "} // namespace nc\n"
+                         "} // namespace na\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, MoveIntoExistingNamespaceAndShortenRefs) {
+  std::string Code = "namespace x {\n"
+                     "namespace z {\n"
+                     "class Z {};\n"
+                     "} // namespace z\n"
+                     "namespace y {\n"
+                     "class T {};\n"
+                     "} // namespace y\n"
+                     "} // namespace x\n"
+                     "namespace na {\n"
+                     "class A{};\n"
+                     "namespace nb {\n"
+                     "class X { A a; x::z::Z zz; x::y::T t; };\n"
+                     "} // namespace nb\n"
+                     "} // namespace na\n";
+  std::string Expected = "namespace x {\n"
+                         "namespace z {\n"
+                         "class Z {};\n"
+                         "} // namespace z\n"
+                         "namespace y {\n"
+                         "class T {};\n"
+                         "} // namespace y\n"
+                         "} // namespace x\n"
+                         "namespace na {\n"
+                         "class A {};\n\n"
+                         "} // namespace na\n"
+                         "namespace x {\n"
+                         "namespace y {\n"
+                         "class X { na::A a; z::Z zz; T t; };\n"
+                         "} // namespace y\n"
+                         "} // namespace x\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, SimpleMoveNestedNamespace) {
+  NewNamespace = "na::x::y";
+  std::string Code = "namespace na {\n"
+                     "class A {};\n"
+                     "namespace nb {\n"
+                     "class B {};\n"
+                     "} // namespace nb\n"
+                     "} // namespace na\n";
+  std::string Expected = "namespace na {\n"
+                         "class A {};\n"
+                         "\n"
+                         "namespace x {\n"
+                         "namespace y {\n"
+                         "class B {};\n"
+                         "} // namespace y\n"
+                         "} // namespace x\n"
+                         "} // namespace na\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, SimpleMoveWithTypeRefs) {
+  std::string Code = "namespace na {\n"
+                     "class C_A {};\n"
+                     "namespace nc {\n"
+                     "class C_C {};"
+                     "} // namespace nc\n"
+                     "namespace nb {\n"
+                     "class C_X {\n"
+                     "public:\n"
+                     "  C_A a;\n"
+                     "  nc::C_C c;\n"
+                     "};\n"
+                     "class C_Y {\n"
+                     "  C_X x;\n"
+                     "};\n"
+                     "} // namespace nb\n"
+                     "} // namespace na\n";
+  std::string Expected = "namespace na {\n"
+                         "class C_A {};\n"
+                         "namespace nc {\n"
+                         "class C_C {};"
+                         "} // namespace nc\n"
+                         "\n"
+                         "} // namespace na\n"
+                         "namespace x {\n"
+                         "namespace y {\n"
+                         "class C_X {\n"
+                         "public:\n"
+                         "  na::C_A a;\n"
+                         "  na::nc::C_C c;\n"
+                         "};\n"
+                         "class C_Y {\n"
+                         "  C_X x;\n"
+                         "};\n"
+                         "} // namespace y\n"
+                         "} // namespace x\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, TypeLocInTemplateSpecialization) {
+  std::string Code = "namespace na {\n"
+                     "class A {};\n"
+                     "template <typename T>\n"
+                     "class B {};\n"
+                     "template <typename T1, typename T2>\n"
+                     "class Two {};\n"
+                     "namespace nc { class C {}; }\n"
+                     "} // na\n"
+                     "\n"
+                     "namespace na {\n"
+                     "namespace nb {\n"
+                     "void f() {\n"
+                     "  B<A> b;\n"
+                     "  B<nc::C> b_c;\n"
+                     "  Two<A, nc::C> two;\n"
+                     "}\n"
+                     "} // nb\n"
+                     "} // na\n";
+  std::string Expected = "namespace na {\n"
+                         "class A {};\n"
+                         "template <typename T>\n"
+                         "class B {};\n"
+                         "template <typename T1, typename T2>\n"
+                         "class Two {};\n"
+                         "namespace nc { class C {}; }\n"
+                         "} // na\n"
+                         "\n"
+                         "\n"
+                         "namespace x {\n"
+                         "namespace y {\n"
+                         "void f() {\n"
+                         "  na::B<na::A> b;\n"
+                         "  na::B<na::nc::C> b_c;\n"
+                         "  na::Two<na::A, na::nc::C> two;\n"
+                         "}\n"
+                         "} // namespace y\n"
+                         "} // namespace x\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, LeaveForwardDeclarationBehind) {
+  std::string Code = "namespace na {\n"
+                     "namespace nb {\n"
+                     "class FWD;\n"
+                     "class FWD2;\n"
+                     "class A {\n"
+                     "  FWD *fwd;\n"
+                     "};\n"
+                     "} // namespace nb\n"
+                     "} // namespace na\n";
+  std::string Expected = "namespace na {\n"
+                         "namespace nb {\n"
+                         "class FWD;\n"
+                         "class FWD2;\n"
+                         "} // namespace nb\n"
+                         "} // namespace na\n"
+                         "namespace x {\n"
+                         "namespace y {\n"
+                         "\n"
+                         "class A {\n"
+                         "  na::nb::FWD *fwd;\n"
+                         "};\n"
+                         "} // namespace y\n"
+                         "} // namespace x\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, InsertForwardDeclsProperly) {
+  std::string Code = "namespace na {\n"
+                     "namespace nb {\n"
+                     "\n"
+                     "class FWD;\n"
+                     "class FWD2;\n"
+                     "class A {\n"
+                     "  FWD *fwd;\n"
+                     "};\n"
+                     "\n"
+                     "} // namespace nb\n"
+                     "} // namespace na\n";
+  std::string Expected = "namespace na {\n"
+                         "namespace nb {\n"
+                         "class FWD;\n"
+                         "class FWD2;\n"
+                         "} // namespace nb\n"
+                         "} // namespace na\n"
+                         "namespace x {\n"
+                         "namespace y {\n"
+                         "\n"
+                         "class A {\n"
+                         "  na::nb::FWD *fwd;\n"
+                         "};\n"
+                         "\n"
+                         "} // namespace y\n"
+                         "} // namespace x\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, TemplateClassForwardDeclaration) {
+  std::string Code = "namespace na {\n"
+                     "namespace nb {\n"
+                     "class FWD;\n"
+                     "template<typename T> class FWD_TEMP;\n"
+                     "class A {\n"
+                     "  FWD *fwd;\n"
+                     "};\n"
+                     "template<typename T> class TEMP {};\n"
+                     "} // namespace nb\n"
+                     "} // namespace na\n";
+  std::string Expected = "namespace na {\n"
+                         "namespace nb {\n"
+                         "class FWD;\n"
+                         "template<typename T> class FWD_TEMP;\n"
+                         "} // namespace nb\n"
+                         "} // namespace na\n"
+                         "namespace x {\n"
+                         "namespace y {\n"
+                         "\n"
+                         "class A {\n"
+                         "  na::nb::FWD *fwd;\n"
+                         "};\n"
+                         "template<typename T> class TEMP {};\n"
+                         "} // namespace y\n"
+                         "} // namespace x\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, DontMoveForwardDeclarationInClass) {
+  std::string Code = "namespace na {\n"
+                     "namespace nb {\n"
+                     "class A {\n"
+                     "  class FWD;\n"
+                     "  FWD *fwd;\n"
+                     "  template<typename T> class FWD_TEMP;\n"
+                     "};\n"
+                     "} // namespace nb\n"
+                     "} // namespace na\n";
+  std::string Expected = "\n\n"
+                         "namespace x {\n"
+                         "namespace y {\n"
+                         "class A {\n"
+                         "  class FWD;\n"
+                         "  FWD *fwd;\n"
+                         "  template<typename T> class FWD_TEMP;\n"
+                         "};\n"
+                         "} // namespace y\n"
+                         "} // namespace x\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, MoveFunctions) {
+  std::string Code = "namespace na {\n"
+                     "class C_A {};\n"
+                     "namespace nc {\n"
+                     "class C_C {};"
+                     "} // namespace nc\n"
+                     "namespace nb {\n"
+                     "void fwd();\n"
+                     "void f(C_A ca, nc::C_C cc) {\n"
+                     "  C_A ca_1 = ca;\n"
+                     "}\n"
+                     "} // namespace nb\n"
+                     "} // namespace na\n";
+
+  std::string Expected = "namespace na {\n"
+                         "class C_A {};\n"
+                         "namespace nc {\n"
+                         "class C_C {};"
+                         "} // namespace nc\n"
+                         "\n"
+                         "} // namespace na\n"
+                         "namespace x {\n"
+                         "namespace y {\n"
+                         "void fwd();\n"
+                         "void f(na::C_A ca, na::nc::C_C cc) {\n"
+                         "  na::C_A ca_1 = ca;\n"
+                         "}\n"
+                         "} // namespace y\n"
+                         "} // namespace x\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, FixUsingShadowDecl) {
+  std::string Code = "class GLOB {};\n"
+                     "using BLOG = GLOB;\n"
+                     "namespace na {\n"
+                     "namespace nc {\n"
+                     "class SAME {};\n"
+                     "}\n"
+                     "namespace nd {\n"
+                     "class SAME {};\n"
+                     "}\n"
+                     "namespace nb {\n"
+                     "using nc::SAME;\n"
+                     "using YO = nd::SAME;\n"
+                     "typedef nd::SAME IDENTICAL;\n"
+                     "void f(nd::SAME Same) {}\n"
+                     "} // namespace nb\n"
+                     "} // namespace na\n";
+
+  std::string Expected = "class GLOB {};\n"
+                         "using BLOG = GLOB;\n"
+                         "namespace na {\n"
+                         "namespace nc {\n"
+                         "class SAME {};\n"
+                         "}\n"
+                         "namespace nd {\n"
+                         "class SAME {};\n"
+                         "}\n"
+                         "\n"
+                         "} // namespace na\n"
+                         "namespace x {\n"
+                         "namespace y {\n"
+                         "using ::na::nc::SAME;\n"
+                         "using YO = na::nd::SAME;\n"
+                         "typedef na::nd::SAME IDENTICAL;\n"
+                         "void f(na::nd::SAME Same) {}\n"
+                         "} // namespace y\n"
+                         "} // namespace x\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, DontFixUsingShadowDeclInClasses) {
+  std::string Code = "namespace na {\n"
+                     "class A {};\n"
+                     "class Base { public: Base() {} void m() {} };\n"
+                     "namespace nb {\n"
+                     "class D : public Base {\n"
+                     "public:\n"
+                     "  using AA = A; using B = Base;\n"
+                     "  using Base::m; using Base::Base;\n"
+                     "};"
+                     "} // namespace nb\n"
+                     "} // namespace na\n";
+
+  std::string Expected = "namespace na {\n"
+                         "class A {};\n"
+                         "class Base { public: Base() {} void m() {} };\n"
+                         "\n"
+                         "} // namespace na\n"
+                         "namespace x {\n"
+                         "namespace y {\n"
+                         "class D : public na::Base {\n"
+                         "public:\n"
+                         "  using AA = na::A; using B = na::Base;\n"
+                         "  using Base::m; using Base::Base;\n"
+                         "};"
+                         "} // namespace y\n"
+                         "} // namespace x\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, TypeInNestedNameSpecifier) {
+  std::string Code =
+      "namespace na {\n"
+      "class C_A {\n"
+      "public:\n"
+      "  class Nested {\n"
+      "    public:\n"
+      "      static int NestedX;\n"
+      "      static void nestedFunc() {}\n"
+      "  };\n"
+      "};\n"
+      "namespace nb {\n"
+      "class C_X {\n"
+      "  C_A na;\n"
+      "  C_A::Nested nested;\n"
+      "  void f() {\n"
+      "    C_A::Nested::nestedFunc();\n"
+      "    int X = C_A::Nested::NestedX;\n"
+      "  }\n"
+      "};\n"
+      "}  // namespace nb\n"
+      "}  // namespace na\n";
+  std::string Expected =
+      "namespace na {\n"
+      "class C_A {\n"
+      "public:\n"
+      "  class Nested {\n"
+      "    public:\n"
+      "      static int NestedX;\n"
+      "      static void nestedFunc() {}\n"
+      "  };\n"
+      "};\n"
+      "\n"
+      "}  // namespace na\n"
+      "namespace x {\n"
+      "namespace y {\n"
+      "class C_X {\n"
+      "  na::C_A na;\n"
+      "  na::C_A::Nested nested;\n"
+      "  void f() {\n"
+      "    na::C_A::Nested::nestedFunc();\n"
+      "    int X = na::C_A::Nested::NestedX;\n"
+      "  }\n"
+      "};\n"
+      "}  // namespace y\n"
+      "}  // namespace x\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, FixFunctionNameSpecifiers) {
+  std::string Code =
+      "namespace na {\n"
+      "class A {\n"
+      "public:\n"
+      "  static void f() {}\n"
+      "  static void g();\n"
+      "};\n"
+      "void A::g() {}"
+      "void a_f() {}\n"
+      "static void static_f() {}\n"
+      "namespace nb {\n"
+      "void f() { a_f(); static_f(); A::f(); }\n"
+      "void g() { f(); A::g(); }\n"
+      "}  // namespace nb\n"
+      "}  // namespace na\n";
+  std::string Expected =
+      "namespace na {\n"
+      "class A {\n"
+      "public:\n"
+      "  static void f() {}\n"
+      "  static void g();\n"
+      "};\n"
+      "void A::g() {}"
+      "void a_f() {}\n"
+      "static void static_f() {}\n"
+      "\n"
+      "}  // namespace na\n"
+      "namespace x {\n"
+      "namespace y {\n"
+      "void f() { na::a_f(); na::static_f(); na::A::f(); }\n"
+      "void g() { f(); na::A::g(); }\n"
+      "}  // namespace y\n"
+      "}  // namespace x\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, FixOverloadedOperatorFunctionNameSpecifiers) {
+  std::string Code =
+      "namespace na {\n"
+      "class A {\n"
+      "public:\n"
+      "  int x;\n"
+      "  bool operator==(const A &RHS) const { return x == RHS.x; }\n"
+      "};\n"
+      "bool operator<(const A &LHS, const A &RHS) { return LHS.x == RHS.x; }\n"
+      "namespace nb {\n"
+      "bool f() {\n"
+      "  A x, y;\n"
+      "  auto f = operator<;\n"
+      "  return (x == y) && (x < y) && (operator<(x, y));\n"
+      "}\n"
+      "}  // namespace nb\n"
+      "}  // namespace na\n";
+  std::string Expected =
+      "namespace na {\n"
+      "class A {\n"
+      "public:\n"
+      "  int x;\n"
+      "  bool operator==(const A &RHS) const { return x == RHS.x; }\n"
+      "};\n"
+      "bool operator<(const A &LHS, const A &RHS) { return LHS.x == RHS.x; }\n"
+      "\n"
+      "}  // namespace na\n"
+      "namespace x {\n"
+      "namespace y {\n"
+      "bool f() {\n"
+      "  na::A x, y;\n"
+      "  auto f = na::operator<;\n"
+      // FIXME: function calls to overloaded operators are not fixed now even if
+      // they are referenced by qualified names.
+      "  return (x == y) && (x < y) && (operator<(x,y));\n"
+      "}\n"
+      "}  // namespace y\n"
+      "}  // namespace x\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, FixNonCallingFunctionReferences) {
+  std::string Code = "namespace na {\n"
+                     "class A {\n"
+                     "public:\n"
+                     "  static void f() {}\n"
+                     "};\n"
+                     "void a_f() {}\n"
+                     "static void s_f() {}\n"
+                     "namespace nb {\n"
+                     "void f() {\n"
+                     "  auto *ref1 = A::f;\n"
+                     "  auto *ref2 = a_f;\n"
+                     "  auto *ref3 = s_f;\n"
+                     "}\n"
+                     "}  // namespace nb\n"
+                     "}  // namespace na\n";
+  std::string Expected =
+      "namespace na {\n"
+      "class A {\n"
+      "public:\n"
+      "  static void f() {}\n"
+      "};\n"
+      "void a_f() {}\n"
+      "static void s_f() {}\n"
+      "\n"
+      "}  // namespace na\n"
+      "namespace x {\n"
+      "namespace y {\n"
+      "void f() {\n"
+      "  auto *ref1 = na::A::f;\n"
+      "  auto *ref2 = na::a_f;\n"
+      "  auto *ref3 = na::s_f;\n"
+      "}\n"
+      "}  // namespace y\n"
+      "}  // namespace x\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, MoveAndFixGlobalVariables) {
+  std::string Code = "namespace na {\n"
+                     "int GlobA;\n"
+                     "static int GlobAStatic = 0;\n"
+                     "namespace nc { int GlobC; }\n"
+                     "namespace nb {\n"
+                     "int GlobB;\n"
+                     "void f() {\n"
+                     "  int a = GlobA;\n"
+                     "  int b = GlobAStatic;\n"
+                     "  int c = nc::GlobC;\n"
+                     "}\n"
+                     "} // namespace nb\n"
+                     "} // namespace na\n";
+
+  std::string Expected = "namespace na {\n"
+                         "int GlobA;\n"
+                         "static int GlobAStatic = 0;\n"
+                         "namespace nc { int GlobC; }\n"
+                         "\n"
+                         "} // namespace na\n"
+                         "namespace x {\n"
+                         "namespace y {\n"
+                         "int GlobB;\n"
+                         "void f() {\n"
+                         "  int a = na::GlobA;\n"
+                         "  int b = na::GlobAStatic;\n"
+                         "  int c = na::nc::GlobC;\n"
+                         "}\n"
+                         "}  // namespace y\n"
+                         "}  // namespace x\n";
+
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, DoNotFixStaticVariableOfClass) {
+  std::string Code = "namespace na {\n"
+                     "class A {\n"
+                     "public:\n"
+                     "static int A1;\n"
+                     "static int A2;\n"
+                     "};\n"
+                     "int A::A1 = 0;\n"
+                     "namespace nb {\n"
+                     "void f() {\n"
+                     "  int a = A::A1; int b = A::A2;\n"
+                     "}\n"
+                     "} // namespace nb\n"
+                     "} // namespace na\n";
+
+  std::string Expected = "namespace na {\n"
+                         "class A {\n"
+                         "public:\n"
+                         "static int A1;\n"
+                         "static int A2;\n"
+                         "};\n"
+                         "int A::A1 = 0;\n"
+                         "\n"
+                         "} // namespace na\n"
+                         "namespace x {\n"
+                         "namespace y {\n"
+                         "void f() {\n"
+                         "  int a = na::A::A1; int b = na::A::A2;\n"
+                         "}\n"
+                         "}  // namespace y\n"
+                         "}  // namespace x\n";
+
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, NoMisplaceAtEOF) {
+  std::string Code = "namespace na {\n"
+                     "namespace nb {\n"
+                     "class A;\n"
+                     "class B {};\n"
+                     "}"
+                     "}";
+  std::string Expected = "namespace na {\n"
+                         "namespace nb {\n"
+                         "class A;\n"
+                         "}\n"
+                         "}\n"
+                         "namespace x {\n"
+                         "namespace y {\n"
+                         "\n"
+                         "class B {};\n"
+                         "} // namespace y\n"
+                         "} // namespace x\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, CommentsBeforeMovedClass) {
+  std::string Code = "namespace na {\n"
+                     "namespace nb {\n"
+                     "\n\n"
+                     "// Wild comments.\n"
+                     "\n"
+                     "// Comments.\n"
+                     "// More comments.\n"
+                     "class B {\n"
+                     "  // Private comments.\n"
+                     "  int a;\n"
+                     "};\n"
+                     "}\n"
+                     "}";
+  std::string Expected = "\n"
+                         "\n"
+                         "namespace x {\n"
+                         "namespace y {\n"
+                         "\n\n"
+                         "// Wild comments.\n"
+                         "\n"
+                         "// Comments.\n"
+                         "// More comments.\n"
+                         "class B {\n"
+                         "  // Private comments.\n"
+                         "  int a;\n"
+                         "};\n"
+                         "} // namespace y\n"
+                         "} // namespace x\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, UsingShadowDeclInGlobal) {
+  std::string Code = "namespace glob {\n"
+                     "class Glob {};\n"
+                     "}\n"
+                     "using glob::Glob;\n"
+                     "namespace na {\n"
+                     "namespace nb {\n"
+                     "void f() { Glob g; }\n"
+                     "} // namespace nb\n"
+                     "} // namespace na\n";
+
+  std::string Expected = "namespace glob {\n"
+                         "class Glob {};\n"
+                         "}\n"
+                         "using glob::Glob;\n"
+                         "\n"
+                         "namespace x {\n"
+                         "namespace y {\n"
+                         "void f() { Glob g; }\n"
+                         "} // namespace y\n"
+                         "} // namespace x\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, UsingNamespaceInGlobal) {
+  std::string Code = "namespace glob {\n"
+                     "class Glob {};\n"
+                     "}\n"
+                     "using namespace glob;\n"
+                     "namespace na {\n"
+                     "namespace nb {\n"
+                     "void f() { Glob g; }\n"
+                     "} // namespace nb\n"
+                     "} // namespace na\n";
+
+  std::string Expected = "namespace glob {\n"
+                         "class Glob {};\n"
+                         "}\n"
+                         "using namespace glob;\n"
+                         "\n"
+                         "namespace x {\n"
+                         "namespace y {\n"
+                         "void f() { Glob g; }\n"
+                         "} // namespace y\n"
+                         "} // namespace x\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, NamespaceAliasInGlobal) {
+  std::string Code = "namespace glob {\n"
+                     "class Glob {};\n"
+                     "}\n"
+                     "namespace glob2 { class Glob2 {}; }\n"
+                     "namespace gl = glob;\n"
+                     "namespace gl2 = glob2;\n"
+                     "namespace na {\n"
+                     "namespace nb {\n"
+                     "void f() { gl::Glob g; gl2::Glob2 g2; }\n"
+                     "} // namespace nb\n"
+                     "} // namespace na\n";
+
+  std::string Expected =
+      "namespace glob {\n"
+      "class Glob {};\n"
+      "}\n"
+      "namespace glob2 { class Glob2 {}; }\n"
+      "namespace gl = glob;\n"
+      "namespace gl2 = glob2;\n"
+      "\n"
+      "namespace x {\n"
+      "namespace y {\n"
+      "void f() { gl::Glob g; gl2::Glob2 g2; }\n"
+      "} // namespace y\n"
+      "} // namespace x\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, NamespaceAliasInNamespace) {
+  std::string Code = "namespace glob {\n"
+                     "class Glob {};\n"
+                     "}\n"
+                     "namespace na {\n"
+                     "namespace nb {\n"
+                     "namespace gl = glob;\n"
+                     "void f() { gl::Glob g; }\n"
+                     "} // namespace nb\n"
+                     "} // namespace na\n";
+
+  std::string Expected = "namespace glob {\n"
+                         "class Glob {};\n"
+                         "}\n"
+                         "\n"
+                         "namespace x {\n"
+                         "namespace y {\n"
+                         "namespace gl = glob;\n"
+                         "void f() { gl::Glob g; }\n"
+                         "} // namespace y\n"
+                         "} // namespace x\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, NamespaceAliasInAncestorNamespace) {
+  NewNamespace = "na::nx";
+  std::string Code = "namespace glob {\n"
+                     "class Glob {};\n"
+                     "}\n"
+                     "namespace other { namespace gl = glob; }\n"
+                     "namespace na {\n"
+                     "namespace ga = glob;\n"
+                     "namespace nb {\n"
+                     "void f() { ga::Glob g; }\n"
+                     "} // namespace nb\n"
+                     "} // namespace na\n";
+
+  std::string Expected = "namespace glob {\n"
+                         "class Glob {};\n"
+                         "}\n"
+                         "namespace other { namespace gl = glob; }\n"
+                         "namespace na {\n"
+                         "namespace ga = glob;\n"
+                         "\n"
+                         "namespace nx {\n"
+                         "void f() { ga::Glob g; }\n"
+                         "} // namespace nx\n"
+                         "} // namespace na\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, NamespaceAliasInOtherNamespace) {
+  std::string Code = "namespace glob {\n"
+                     "class Glob {};\n"
+                     "}\n"
+                     "namespace other { namespace gl = glob; }\n"
+                     "namespace na {\n"
+                     "namespace ga = glob;\n"
+                     "namespace nb {\n"
+                     "void f() { glob::Glob g; }\n"
+                     "} // namespace nb\n"
+                     "} // namespace na\n";
+
+  std::string Expected = "namespace glob {\n"
+                         "class Glob {};\n"
+                         "}\n"
+                         "namespace other { namespace gl = glob; }\n"
+                         "namespace na {\n"
+                         "namespace ga = glob;\n"
+                         "\n"
+                         "} // namespace na\n"
+                         "namespace x {\n"
+                         "namespace y {\n"
+                         "void f() { glob::Glob g; }\n"
+                         "} // namespace y\n"
+                         "} // namespace x\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, UsingDeclAfterReference) {
+  std::string Code = "namespace glob {\n"
+                     "class Glob {};\n"
+                     "}\n"
+                     "namespace na {\n"
+                     "namespace nb {\n"
+                     "void f() { glob::Glob g; }\n"
+                     "} // namespace nb\n"
+                     "} // namespace na\n"
+                     "using glob::Glob;\n"
+                     "using namespace glob;\n";
+
+  std::string Expected = "namespace glob {\n"
+                         "class Glob {};\n"
+                         "}\n"
+                         "\n"
+                         "namespace x {\n"
+                         "namespace y {\n"
+                         "void f() { glob::Glob g; }\n"
+                         "} // namespace y\n"
+                         "} // namespace x\n"
+                         "using glob::Glob;\n"
+                         "using namespace glob;\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, UsingNamespaceAfterReference) {
+  NewNamespace = "na::nc";
+  std::string Code = "namespace glob {\n"
+                     "class Glob {};\n"
+                     "}\n"
+                     "namespace na {\n"
+                     "namespace nb {\n"
+                     "void f() { glob::Glob g; }\n"
+                     "} // namespace nb\n"
+                     "using namespace glob;\n"
+                     "} // namespace na\n";
+
+  std::string Expected = "namespace glob {\n"
+                         "class Glob {};\n"
+                         "}\n"
+                         "namespace na {\n"
+                         "\n"
+                         "namespace nc {\n"
+                         "void f() { glob::Glob g; }\n"
+                         "} // namespace nc\n"
+                         "using namespace glob;\n"
+                         "} // namespace na\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, UsingNamespaceAndUsingShadowInGlobal) {
+  std::string Code = "namespace glob1 {\n"
+                     "namespace glob2 {\n"
+                     "class Glob {};\n"
+                     "}\n"
+                     "}\n"
+                     "using glob1::glob2::Glob;\n"
+                     "using namespace glob1;\n"
+                     "namespace na {\n"
+                     "namespace nb {\n"
+                     "void f() { Glob g; }\n"
+                     "} // namespace nb\n"
+                     "} // namespace na\n";
+
+  std::string Expected = "namespace glob1 {\n"
+                         "namespace glob2 {\n"
+                         "class Glob {};\n"
+                         "}\n"
+                         "}\n"
+                         "using glob1::glob2::Glob;\n"
+                         "using namespace glob1;\n"
+                         "\n"
+                         "namespace x {\n"
+                         "namespace y {\n"
+                         "void f() { Glob g; }\n"
+                         "} // namespace y\n"
+                         "} // namespace x\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, UsingAliasInGlobal) {
+  std::string Code = "namespace glob {\n"
+                     "class Glob {};\n"
+                     "}\n"
+                     "using GLB = glob::Glob;\n"
+                     "using BLG = glob::Glob;\n"
+                     "namespace na {\n"
+                     "namespace nb {\n"
+                     "void f() { GLB g; BLG blg; }\n"
+                     "} // namespace nb\n"
+                     "} // namespace na\n";
+
+  std::string Expected = "namespace glob {\n"
+                         "class Glob {};\n"
+                         "}\n"
+                         "using GLB = glob::Glob;\n"
+                         "using BLG = glob::Glob;\n"
+                         "\n"
+                         "namespace x {\n"
+                         "namespace y {\n"
+                         "void f() { GLB g; BLG blg; }\n"
+                         "} // namespace y\n"
+                         "} // namespace x\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, UsingShadowDeclAndMovedNamespace) {
+  std::string Code = "namespace na { class C_A {};\n }\n"
+                     "using na::C_A;\n"
+                     "namespace na {\n"
+                     "namespace nb {\n"
+                     "class C_X {\n"
+                     "public:\n"
+                     "  C_A a;\n"
+                     "};\n"
+                     "} // namespace nb\n"
+                     "} // namespace na\n";
+  std::string Expected = "namespace na { class C_A {};\n }\n"
+                         "using na::C_A;\n"
+                         "\n"
+                         "namespace x {\n"
+                         "namespace y {\n"
+                         "class C_X {\n"
+                         "public:\n"
+                         "  C_A a;\n"
+                         "};\n"
+                         "} // namespace y\n"
+                         "} // namespace x\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, UsingNamespaceDeclAndMovedNamespace) {
+  std::string Code = "namespace na { class C_A {};\n }\n"
+                     "using namespace na;\n"
+                     "namespace na {\n"
+                     "namespace nb {\n"
+                     "class C_X {\n"
+                     "public:\n"
+                     "  C_A ca;\n"
+                     "};\n"
+                     "} // namespace nb\n"
+                     "} // namespace na\n";
+  std::string Expected = "namespace na { class C_A {};\n }\n"
+                         "using namespace na;\n"
+                         "\n"
+                         "namespace x {\n"
+                         "namespace y {\n"
+                         "class C_X {\n"
+                         "public:\n"
+                         "  C_A ca;\n"
+                         "};\n"
+                         "} // namespace y\n"
+                         "} // namespace x\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, UsingShadowDeclInFunction) {
+  std::string Code = "namespace glob {\n"
+                     "class Glob {};\n"
+                     "}\n"
+                     "namespace na {\n"
+                     "namespace nb {\n"
+                     "void f() {\n"
+                     "  using glob::Glob;\n"
+                     "  Glob g;\n"
+                     "}\n"
+                     "} // namespace nb\n"
+                     "} // namespace na\n";
+
+  std::string Expected = "namespace glob {\n"
+                         "class Glob {};\n"
+                         "}\n"
+                         "\n"
+                         "namespace x {\n"
+                         "namespace y {\n"
+                         "void f() {\n"
+                         "  using ::glob::Glob;\n"
+                         "  Glob g;\n"
+                         "}\n"
+                         "} // namespace y\n"
+                         "} // namespace x\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, UsingShadowDeclInClass) {
+  std::string Code = "namespace na { class C_A {}; }\n"
+                     "namespace na {\n"
+                     "namespace nb {\n"
+                     "void f() {\n"
+                     "  using ::na::C_A;\n"
+                     "  C_A ca;\n"
+                     "}\n"
+                     "} // namespace nb\n"
+                     "} // namespace na\n";
+  std::string Expected = "namespace na { class C_A {}; }\n"
+                         "\n"
+                         "namespace x {\n"
+                         "namespace y {\n"
+                         "void f() {\n"
+                         "  using ::na::C_A;\n"
+                         "  C_A ca;\n"
+                         "}\n"
+                         "} // namespace y\n"
+                         "} // namespace x\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, UsingDeclInMovedNamespace) {
+  std::string Code = "namespace nx { void f(); }\n"
+                     "namespace na {\n"
+                     "using nx::f;\n"
+                     "namespace nb {\n"
+                     "void d() { f(); }\n"
+                     "} // nb\n"
+                     "} // na\n";
+
+  std::string Expected = "namespace nx { void f(); }\n"
+                         "namespace na {\n"
+                         "using nx::f;\n"
+                         "\n"
+                         "} // na\n"
+                         "namespace x {\n"
+                         "namespace y {\n"
+                         "void d() { nx::f(); }\n"
+                         "} // namespace y\n"
+                         "} // namespace x\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, UsingDeclInMovedNamespaceNotNested) {
+  OldNamespace = "na";
+  std::string Code = "namespace nx { void f(); }\n"
+                     "namespace na {\n"
+                     "using ::nx::f;\n"
+                     "void d() { f(); }\n"
+                     "} // na\n";
+
+  std::string Expected = "namespace nx { void f(); }\n"
+                         "\n"
+                         "namespace x {\n"
+                         "namespace y {\n"
+                         "using ::nx::f;\n"
+                         "void d() { f(); }\n"
+                         "} // namespace y\n"
+                         "} // namespace x\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, UsingDeclInMovedNamespaceMultiNested) {
+  OldNamespace = "a::b::c::d";
+  NewNamespace = "a::b::x::y";
+  std::string Code = "namespace nx { void f(); void g(); }\n"
+                     "namespace a {\n"
+                     "namespace b {\n"
+                     "using ::nx::f;\n"
+                     "namespace c {\n"
+                     "using ::nx::g;\n"
+                     "namespace d {\n"
+                     "void d() { f(); g(); }\n"
+                     "} // d\n"
+                     "} // c\n"
+                     "} // b\n"
+                     "} // a\n";
+
+  std::string Expected = "namespace nx { void f(); void g(); }\n"
+                         "namespace a {\n"
+                         "namespace b {\n"
+                         "using ::nx::f;\n"
+                         "namespace c {\n"
+                         "using ::nx::g;\n"
+                         "\n"
+                         "} // c\n"
+                         "namespace x {\n"
+                         "namespace y {\n"
+                         "void d() { f(); nx::g(); }\n"
+                         "} // namespace y\n"
+                         "} // namespace x\n"
+                         "} // b\n"
+                         "} // a\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, UsingShadowDeclInTheParentOfOldNamespace) {
+  OldNamespace = "nb::nc";
+  NewNamespace = "nb::nd";
+  std::string Code = "namespace na { class A {}; }\n"
+                     "namespace nb {\n"
+                     "using na::A;\n"
+                     "namespace nc {\n"
+                     "void d() { A a; }\n"
+                     "} // nc\n"
+                     "} // nb\n";
+
+  std::string Expected = "namespace na { class A {}; }\n"
+                         "namespace nb {\n"
+                         "using na::A;\n"
+                         "\n"
+                         "namespace nd {\n"
+                         "void d() { A a; }\n"
+                         "} // namespace nd\n"
+                         "} // nb\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, UsingShadowDeclInOldNamespace) {
+  OldNamespace = "nb";
+  NewNamespace = "nc";
+  std::string Code = "namespace na { class A {}; }\n"
+                     "namespace nb {\n"
+                     "using na::A;\n"
+                     "void d() { A a; }\n"
+                     "struct X { A a; };\n"
+                     "} // nb\n";
+
+  std::string Expected = "namespace na { class A {}; }\n"
+                         "\n"
+                         "namespace nc {\n"
+                         "using ::na::A;\n"
+                         "void d() { A a; }\n"
+                         "struct X { A a; };\n"
+                         "} // namespace nc\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, UsingShadowDeclOfTemplateClass) {
+  OldNamespace = "nb";
+  NewNamespace = "nc";
+  std::string Code = "namespace na {\n"
+                     "template <typename T>\n"
+                     "class A { T t; };\n"
+                     "} // namespace na\n"
+                     "namespace nb {\n"
+                     "using na::A;\n"
+                     "void d() { A<int> a; }\n"
+                     "} // nb\n";
+
+  std::string Expected = "namespace na {\n"
+                         "template <typename T>\n"
+                         "class A { T t; };\n"
+                         "} // namespace na\n"
+                         "\n"
+                         "namespace nc {\n"
+                         "using ::na::A;\n"
+                         "void d() { A<int> a; }\n"
+                         "} // namespace nc\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, UsingShadowDeclOfTemplateFunction) {
+  OldNamespace = "nb";
+  NewNamespace = "nc";
+  std::string Code = "namespace na {\n"
+                     "template <typename T>\n"
+                     "void f() { T t; };\n"
+                     "} // namespace na\n"
+                     "namespace nb {\n"
+                     "using na::f;\n"
+                     "void d() { f<int>(); }\n"
+                     "} // nb\n";
+
+  std::string Expected = "namespace na {\n"
+                         "template <typename T>\n"
+                         "void f() { T t; };\n"
+                         "} // namespace na\n"
+                         "\n"
+                         "namespace nc {\n"
+                         "using ::na::f;\n"
+                         "void d() { f<int>(); }\n"
+                         "} // namespace nc\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, UsingAliasDecl) {
+  std::string Code =
+      "namespace nx { namespace ny { class X {}; } }\n"
+      "namespace na {\n"
+      "namespace nb {\n"
+      "using Y = nx::ny::X;\n"
+      "void f() { Y y; }\n"
+      "} // namespace nb\n"
+      "} // namespace na\n";
+
+  std::string Expected = "namespace nx { namespace ny { class X {}; } }\n"
+                         "\n"
+                         "namespace x {\n"
+                         "namespace y {\n"
+                         "using Y = nx::ny::X;\n"
+                         "void f() { Y y; }\n"
+                         "} // namespace y\n"
+                         "} // namespace x\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, UsingAliasDeclInGlobal) {
+  std::string Code =
+      "namespace nx { namespace ny { class X {}; } }\n"
+      "using Y = nx::ny::X;\n"
+      "namespace na {\n"
+      "namespace nb {\n"
+      "void f() { Y y; }\n"
+      "} // namespace nb\n"
+      "} // namespace na\n";
+
+  std::string Expected = "namespace nx { namespace ny { class X {}; } }\n"
+                         "using Y = nx::ny::X;\n"
+                         "\n"
+                         "namespace x {\n"
+                         "namespace y {\n"
+                         "void f() { Y y; }\n"
+                         "} // namespace y\n"
+                         "} // namespace x\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+
+TEST_F(ChangeNamespaceTest, TypedefAliasDecl) {
+  std::string Code =
+      "namespace nx { namespace ny { class X {}; } }\n"
+      "namespace na {\n"
+      "namespace nb {\n"
+      "typedef nx::ny::X Y;\n"
+      "void f() { Y y; }\n"
+      "} // namespace nb\n"
+      "} // namespace na\n";
+
+  std::string Expected = "namespace nx { namespace ny { class X {}; } }\n"
+                         "\n"
+                         "namespace x {\n"
+                         "namespace y {\n"
+                         "typedef nx::ny::X Y;\n"
+                         "void f() { Y y; }\n"
+                         "} // namespace y\n"
+                         "} // namespace x\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, DerivedClassWithConstructors) {
+  std::string Code =
+      "namespace nx { namespace ny { class X { public: X(int i) {} }; } }\n"
+      "namespace na {\n"
+      "namespace nb {\n"
+      "class A : public nx::ny::X {\n"
+      "public:\n"
+      "  A() : X(0) {}\n"
+      "  A(int i);\n"
+      "};\n"
+      "A::A(int i) : X(i) {}\n"
+      "} // namespace nb\n"
+      "} // namespace na\n";
+  std::string Expected =
+      "namespace nx { namespace ny { class X { public: X(int i) {} }; } }\n"
+      "\n\n"
+      "namespace x {\n"
+      "namespace y {\n"
+      "class A : public nx::ny::X {\n"
+      "public:\n"
+      "  A() : X(0) {}\n"
+      "  A(int i);\n"
+      "};\n"
+      "A::A(int i) : X(i) {}\n"
+      "} // namespace y\n"
+      "} // namespace x\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, DerivedClassWithQualifiedConstructors) {
+  std::string Code =
+      "namespace nx { namespace ny { class X { public: X(int i) {} }; } }\n"
+      "namespace na {\n"
+      "namespace nb {\n"
+      "class A : public nx::ny::X {\n"
+      "public:\n"
+      "  A() : X::X(0) {}\n"
+      "  A(int i);\n"
+      "};\n"
+      "A::A(int i) : X::X(i) {}\n"
+      "} // namespace nb\n"
+      "} // namespace na\n";
+  std::string Expected =
+      "namespace nx { namespace ny { class X { public: X(int i) {} }; } }\n"
+      "\n\n"
+      "namespace x {\n"
+      "namespace y {\n"
+      "class A : public nx::ny::X {\n"
+      "public:\n"
+      "  A() : X::X(0) {}\n"
+      "  A(int i);\n"
+      "};\n"
+      "A::A(int i) : X::X(i) {}\n"
+      "} // namespace y\n"
+      "} // namespace x\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, DerivedClassWithConstructorsAndTypeRefs) {
+  std::string Code =
+      "namespace nx { namespace ny { class X { public: X(int i) {} }; } }\n"
+      "namespace na {\n"
+      "namespace nb {\n"
+      "class A : public nx::ny::X {\n"
+      "public:\n"
+      "  A() : X(0) {}\n"
+      "  A(int i);\n"
+      "};\n"
+      "A::A(int i) : X(i) { X x(1);}\n"
+      "} // namespace nb\n"
+      "} // namespace na\n";
+  std::string Expected =
+      "namespace nx { namespace ny { class X { public: X(int i) {} }; } }\n"
+      "\n\n"
+      "namespace x {\n"
+      "namespace y {\n"
+      "class A : public nx::ny::X {\n"
+      "public:\n"
+      "  A() : X(0) {}\n"
+      "  A(int i);\n"
+      "};\n"
+      "A::A(int i) : X(i) { nx::ny::X x(1);}\n"
+      "} // namespace y\n"
+      "} // namespace x\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, MoveToGlobalNamespace) {
+  NewNamespace = "";
+  std::string Code = "namespace na {\n"
+                     "class C_A {};\n"
+                     "namespace nc {\n"
+                     "class C_C {};"
+                     "} // namespace nc\n"
+                     "namespace nb {\n"
+                     "class C_X {\n"
+                     "public:\n"
+                     "  C_A a;\n"
+                     "  nc::C_C c;\n"
+                     "};\n"
+                     "class C_Y {\n"
+                     "  C_X x;\n"
+                     "};\n"
+                     "} // namespace nb\n"
+                     "} // namespace na\n";
+  std::string Expected = "namespace na {\n"
+                         "class C_A {};\n"
+                         "namespace nc {\n"
+                         "class C_C {};"
+                         "} // namespace nc\n"
+                         "\n"
+                         "} // namespace na\n"
+                         "class C_X {\n"
+                         "public:\n"
+                         "  na::C_A a;\n"
+                         "  na::nc::C_C c;\n"
+                         "};\n"
+                         "class C_Y {\n"
+                         "  C_X x;\n"
+                         "};\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, KeepGlobalSpecifier) {
+  std::string Code = "class Glob {};\n"
+                      "namespace na {\n"
+                     "class C_A {};\n"
+                     "namespace nc {\n"
+                     "class C_C {};"
+                     "} // namespace nc\n"
+                     "namespace nb {\n"
+                     "class C_X {\n"
+                     "public:\n"
+                     "  ::Glob glob_1;\n"
+                     "  Glob glob_2;\n"
+                     "  C_A a_1;\n"
+                     "  ::na::C_A a_2;\n"
+                     "  nc::C_C c;\n"
+                     "};\n"
+                     "} // namespace nb\n"
+                     "} // namespace na\n";
+  std::string Expected = "class Glob {};\n"
+                         "namespace na {\n"
+                         "class C_A {};\n"
+                         "namespace nc {\n"
+                         "class C_C {};"
+                         "} // namespace nc\n"
+                         "\n"
+                         "} // namespace na\n"
+                         "namespace x {\n"
+                         "namespace y {\n"
+                         "class C_X {\n"
+                         "public:\n"
+                         "  ::Glob glob_1;\n"
+                         "  Glob glob_2;\n"
+                         "  na::C_A a_1;\n"
+                         "  ::na::C_A a_2;\n"
+                         "  na::nc::C_C c;\n"
+                         "};\n"
+                         "} // namespace y\n"
+                         "} // namespace x\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, UsingAliasInTemplate) {
+  NewNamespace = "na::nb::nc";
+  std::string Code = "namespace some_ns {\n"
+                     "template <typename T, typename S>\n"
+                     "class G {};\n"
+                     "} // namespace some_ns\n"
+                     "namespace na {\n"
+                     "template<typename P>\n"
+                     "using GG = some_ns::G<int, P>;\n"
+                     "} // namespace na\n"
+                     "namespace na {\n"
+                     "namespace nb {\n"
+                     "void f() {\n"
+                     "  GG<float> g;\n"
+                     "}\n"
+                     "} // namespace nb\n"
+                     "} // namespace na\n";
+  std::string Expected = "namespace some_ns {\n"
+                         "template <typename T, typename S>\n"
+                         "class G {};\n"
+                         "} // namespace some_ns\n"
+                         "namespace na {\n"
+                         "template<typename P>\n"
+                         "using GG = some_ns::G<int, P>;\n"
+                         "} // namespace na\n"
+                         "namespace na {\n"
+                         "namespace nb {\n"
+                         "namespace nc {\n"
+                         "void f() {\n"
+                         "  GG<float> g;\n"
+                         "}\n"
+                         "} // namespace nc\n"
+                         "} // namespace nb\n"
+                         "} // namespace na\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, TemplateUsingAliasInBaseClass) {
+  NewNamespace = "na::nb::nc";
+  std::string Code = "namespace some_ns {\n"
+                     "template <typename T, typename S>\n"
+                     "class G {};\n"
+                     "} // namespace some_ns\n"
+                     "namespace na {\n"
+                     "class Base {\n"
+                     "public:\n"
+                     "  template<typename P>\n"
+                     "  using GG = some_ns::G<int, P>;\n"
+                     "\n"
+                     "  struct Nested {};\n"
+                     "};\n"
+                     "class Derived : public Base {};\n"
+                     "} // namespace na\n"
+                     "namespace na {\n"
+                     "namespace nb {\n"
+                     "void f() {\n"
+                     "  Derived::GG<float> g;\n"
+                     "  const Derived::GG<int> gg;\n"
+                     "  const Derived::GG<int>* gg_ptr;\n"
+                     "  struct Derived::Nested nested;\n"
+                     "  const struct Derived::Nested *nested_ptr;\n"
+                     "}\n"
+                     "} // namespace nb\n"
+                     "} // namespace na\n";
+  std::string Expected = "namespace some_ns {\n"
+                         "template <typename T, typename S>\n"
+                         "class G {};\n"
+                         "} // namespace some_ns\n"
+                         "namespace na {\n"
+                         "class Base {\n"
+                         "public:\n"
+                         "  template<typename P>\n"
+                         "  using GG = some_ns::G<int, P>;\n"
+                         "\n"
+                         "  struct Nested {};\n"
+                         "};\n"
+                         "class Derived : public Base {};\n"
+                         "} // namespace na\n"
+                         "namespace na {\n"
+                         "namespace nb {\n"
+                         "namespace nc {\n"
+                         "void f() {\n"
+                         "  Derived::GG<float> g;\n"
+                         "  const Derived::GG<int> gg;\n"
+                         "  const Derived::GG<int>* gg_ptr;\n"
+                         "  struct Derived::Nested nested;\n"
+                         "  const struct Derived::Nested *nested_ptr;\n"
+                         "}\n"
+                         "} // namespace nc\n"
+                         "} // namespace nb\n"
+                         "} // namespace na\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, ExistingNamespaceConflictWithNewNamespace) {
+  OldNamespace = "nx";
+  NewNamespace = "ny::na::nc";
+  std::string Code = "namespace na {\n"
+                     "class A {};\n"
+                     "} // namespace na\n"
+                     "namespace nb {\n"
+                     "class B {};\n"
+                     "} // namespace nb\n"
+                     "namespace nx {\n"
+                     "class X {\n"
+                     " na::A a; nb::B b;\n"
+                     "};\n"
+                     "} // namespace nx\n";
+  std::string Expected = "namespace na {\n"
+                         "class A {};\n"
+                         "} // namespace na\n"
+                         "namespace nb {\n"
+                         "class B {};\n"
+                         "} // namespace nb\n"
+                         "\n"
+                         "namespace ny {\n"
+                         "namespace na {\n"
+                         "namespace nc {\n"
+                         "class X {\n"
+                         " ::na::A a; nb::B b;\n"
+                         "};\n"
+                         "} // namespace nc\n"
+                         "} // namespace na\n"
+                         "} // namespace ny\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, SymbolConflictWithNewNamespace) {
+  OldNamespace = "nx";
+  NewNamespace = "ny::na::nc";
+  std::string Code = "namespace na {\n"
+                     "class A {};\n"
+                     "namespace nb {\n"
+                     "class B {};\n"
+                     "} // namespace nb\n"
+                     "} // namespace na\n"
+                     "namespace ny {\n"
+                     "class Y {};\n"
+                     "}\n"
+                     "namespace nx {\n"
+                     "class X {\n"
+                     "  na::A a; na::nb::B b;\n"
+                     "  ny::Y y;"
+                     "};\n"
+                     "} // namespace nx\n";
+  std::string Expected = "namespace na {\n"
+                         "class A {};\n"
+                         "namespace nb {\n"
+                         "class B {};\n"
+                         "} // namespace nb\n"
+                         "} // namespace na\n"
+                         "namespace ny {\n"
+                         "class Y {};\n"
+                         "}\n"
+                         "\n"
+                         "namespace ny {\n"
+                         "namespace na {\n"
+                         "namespace nc {\n"
+                         "class X {\n"
+                         "  ::na::A a; ::na::nb::B b;\n"
+                         "  Y y;\n"
+                         "};\n"
+                         "} // namespace nc\n"
+                         "} // namespace na\n"
+                         "} // namespace ny\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, ShortenNamespaceSpecifier) {
+  OldNamespace = "nx";
+  NewNamespace = "ny::na";
+  std::string Code = "class G {};\n"
+                     "namespace ny {\n"
+                     "class Y {};\n"
+                     "namespace na {\n"
+                     "class A {};\n"
+                     "namespace nc { class C {}; } // namespace nc\n"
+                     "}\n // namespace na\n"
+                     "}\n // namespace ny\n"
+                     "namespace nx {\n"
+                     "class X {\n"
+                     " G g; ny::Y y; ny::na::A a; ny::na::nc::C c;\n"
+                     "};\n"
+                     "} // namespace nx\n";
+  std::string Expected = "class G {};\n"
+                         "namespace ny {\n"
+                         "class Y {};\n"
+                         "namespace na {\n"
+                         "class A {};\n"
+                         "namespace nc { class C {}; } // namespace nc\n"
+                         "}\n // namespace na\n"
+                         "}\n // namespace ny\n"
+                         "\n"
+                         "namespace ny {\n"
+                         "namespace na {\n"
+                         "class X {\n"
+                         " G g; Y y; A a; nc::C c;\n"
+                         "};\n"
+                         "} // namespace na\n"
+                         "} // namespace ny\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, ShortenNamespaceSpecifierInAnonymousNamespace) {
+  OldNamespace = "nx";
+  NewNamespace = "ny::na";
+  std::string Code = "class G {};\n"
+                     "namespace ny {\n"
+                     "class Y {};\n"
+                     "namespace na {\n"
+                     "class A {};\n"
+                     "namespace nc { class C {}; } // namespace nc\n"
+                     "}\n // namespace na\n"
+                     "}\n // namespace ny\n"
+                     "namespace nx {\n"
+                     "namespace {\n"
+                     "class X {\n"
+                     " G g; ::ny::Y y; ::ny::na::A a; ::ny::na::nc::C c;\n"
+                     "};\n"
+                     "} // namespace\n"
+                     "} // namespace nx\n";
+  std::string Expected = "class G {};\n"
+                         "namespace ny {\n"
+                         "class Y {};\n"
+                         "namespace na {\n"
+                         "class A {};\n"
+                         "namespace nc { class C {}; } // namespace nc\n"
+                         "}\n // namespace na\n"
+                         "}\n // namespace ny\n"
+                         "\n"
+                         "namespace ny {\n"
+                         "namespace na {\n"
+                         "namespace {\n"
+                         "class X {\n"
+                         " G g; Y y; A a; nc::C c;\n"
+                         "};\n"
+                         "} // namespace\n"
+                         "} // namespace na\n"
+                         "} // namespace ny\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, SimpleMoveEnum) {
+  std::string Code = "namespace na {\n"
+                     "namespace nb {\n"
+                     "enum class X { X1, X2 };\n"
+                     "enum Y { Y1, Y2 };\n"
+                     "} // namespace nb\n"
+                     "} // namespace na\n";
+  std::string Expected = "\n\nnamespace x {\n"
+                         "namespace y {\n"
+                         "enum class X { X1, X2 };\n"
+                         "enum Y { Y1, Y2 };\n"
+                         "} // namespace y\n"
+                         "} // namespace x\n";
+
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, ReferencesToEnums) {
+  std::string Code = "enum Glob { G1, G2 };\n"
+                     "namespace na {\n"
+                     "enum class X { X1 };\n"
+                     "enum Y { Y1, Y2 };\n"
+                     "namespace nb {\n"
+                     "void f() {\n"
+                     "  Glob g1 = Glob::G1;\n"
+                     "  Glob g2 = G2;\n"
+                     "  X x1 = X::X1;\n"
+                     "  Y y1 = Y::Y1;\n"
+                     "  Y y2 = Y2;\n"
+                     "}\n"
+                     "} // namespace nb\n"
+                     "} // namespace na\n";
+  std::string Expected = "enum Glob { G1, G2 };\n"
+                         "namespace na {\n"
+                         "enum class X { X1 };\n"
+                         "enum Y { Y1, Y2 };\n"
+                         "\n"
+                         "} // namespace na\n"
+                         "namespace x {\n"
+                         "namespace y {\n"
+                         "void f() {\n"
+                         "  Glob g1 = Glob::G1;\n"
+                         "  Glob g2 = G2;\n"
+                         "  na::X x1 = na::X::X1;\n"
+                         "  na::Y y1 = na::Y::Y1;\n"
+                         "  na::Y y2 = na::Y::Y2;\n"
+                         "}\n"
+                         "} // namespace y\n"
+                         "} // namespace x\n";
+
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, NoRedundantEnumUpdate) {
+  std::string Code = "namespace ns {\n"
+                     "enum class X { X1 };\n"
+                     "enum Y { Y1, Y2 };\n"
+                     "} // namespace ns\n"
+                     "namespace na {\n"
+                     "namespace nb {\n"
+                     "void f() {\n"
+                     "  ns::X x1 = ns::X::X1;\n"
+                     "  ns::Y y1 = ns::Y::Y1;\n"
+                     "  ns::Y y2 = ns::Y2;\n"
+                     "}\n"
+                     "} // namespace nb\n"
+                     "} // namespace na\n";
+  std::string Expected = "namespace ns {\n"
+                         "enum class X { X1 };\n"
+                         "enum Y { Y1, Y2 };\n"
+                         "} // namespace ns\n"
+                         "\n"
+                         "namespace x {\n"
+                         "namespace y {\n"
+                         "void f() {\n"
+                         "  ns::X x1 = ns::X::X1;\n"
+                         "  ns::Y y1 = ns::Y::Y1;\n"
+                         // FIXME: this is redundant
+                         "  ns::Y y2 = ns::Y::Y2;\n"
+                         "}\n"
+                         "} // namespace y\n"
+                         "} // namespace x\n";
+  ;
+
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, EnumsAndUsingShadows) {
+  std::string Code = "namespace ns {\n"
+                     "enum class X { X1 };\n"
+                     "enum Y { Y1, Y2, Y3 };\n"
+                     "} // namespace ns\n"
+                     "using ns::X;\n"
+                     "using ns::Y;\n"
+                     "using ns::Y::Y2;\n"
+                     "using ns::Y::Y3;\n"
+                     "namespace na {\n"
+                     "namespace nb {\n"
+                     "void f() {\n"
+                     "  X x1 = X::X1;\n"
+                     "  Y y1 = Y::Y1;\n"
+                     "  Y y2 = Y2;\n"
+                     "  Y y3 = Y3;\n"
+                     "}\n"
+                     "} // namespace nb\n"
+                     "} // namespace na\n";
+  std::string Expected = "namespace ns {\n"
+                         "enum class X { X1 };\n"
+                         "enum Y { Y1, Y2, Y3 };\n"
+                         "} // namespace ns\n"
+                         "using ns::X;\n"
+                         "using ns::Y;\n"
+                         "using ns::Y::Y2;\n"
+                         "using ns::Y::Y3;\n"
+                         "\n"
+                         "namespace x {\n"
+                         "namespace y {\n"
+                         "void f() {\n"
+                         "  X x1 = X::X1;\n"
+                         "  Y y1 = Y::Y1;\n"
+                         "  Y y2 = Y2;\n"
+                         "  Y y3 = Y3;\n"
+                         "}\n"
+                         "} // namespace y\n"
+                         "} // namespace x\n";
+
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, EnumsAndAliases) {
+  std::string Code = "namespace ns {\n"
+                     "enum class X { X1 };\n"
+                     "enum Y { Y1, Y2, Y3 };\n"
+                     "} // namespace ns\n"
+                     "typedef ns::X TX;\n"
+                     "typedef ns::Y TY;\n"
+                     "using UX = ns::X;\n"
+                     "using UY = ns::Y;\n"
+                     "namespace na {\n"
+                     "namespace nb {\n"
+                     "void f() {\n"
+                     "  ns::X x1 = ns::X::X1;\n"
+                     "  TX tx1 = TX::X1;\n"
+                     "  UX ux1 = UX::X1;\n"
+                     "  ns::Y y1 = ns::Y::Y1;\n"
+                     "  TY ty1 = TY::Y1;\n"
+                     "  UY uy1 = UY::Y1;\n"
+                     "}\n"
+                     "} // namespace nb\n"
+                     "} // namespace na\n";
+  std::string Expected = "namespace ns {\n"
+                         "enum class X { X1 };\n"
+                         "enum Y { Y1, Y2, Y3 };\n"
+                         "} // namespace ns\n"
+                         "typedef ns::X TX;\n"
+                         "typedef ns::Y TY;\n"
+                         "using UX = ns::X;\n"
+                         "using UY = ns::Y;\n"
+                         "\n"
+                         "namespace x {\n"
+                         "namespace y {\n"
+                         "void f() {\n"
+                         "  ns::X x1 = ns::X::X1;\n"
+                         "  TX tx1 = TX::X1;\n"
+                         "  UX ux1 = UX::X1;\n"
+                         "  ns::Y y1 = ns::Y::Y1;\n"
+                         "  TY ty1 = TY::Y1;\n"
+                         "  UY uy1 = UY::Y1;\n"
+                         "}\n"
+                         "} // namespace y\n"
+                         "} // namespace x\n";
+
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, EnumInClass) {
+  std::string Code = "namespace na {\n"
+                     "struct X { enum E { E1 }; };\n"
+                     "namespace nb {\n"
+                     "void f() {\n"
+                     "  X::E e = X::E1;\n"
+                     "  X::E ee = X::E::E1;\n"
+                     "}\n"
+                     "} // namespace nb\n"
+                     "} // namespace na\n";
+  std::string Expected = "namespace na {\n"
+                         "struct X { enum E { E1 }; };\n"
+                         "\n"
+                         "} // namespace na\n"
+                         "namespace x {\n"
+                         "namespace y {\n"
+                         "void f() {\n"
+                         "  na::X::E e = na::X::E1;\n"
+                         "  na::X::E ee = na::X::E::E1;\n"
+                         "}\n"
+                         "} // namespace y\n"
+                         "} // namespace x\n";
+
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, TypeAsTemplateParameter) {
+  std::string Code = "namespace na {\n"
+                     "struct X {};\n"
+                     "namespace nb {\n"
+                     "template <typename TT>\n"
+                     "void TempTemp(const TT& t) {\n"
+                     "  TT tmp;\n"
+                     "}\n"
+                     "template <typename T>\n"
+                     "void Temp(const T& t) {\n"
+                     "  T tmp = t;\n"
+                     "  TempTemp(tmp);\n"
+                     "  TempTemp(t);\n"
+                     "}\n"
+                     "void f() {\n"
+                     "  X x;\n"
+                     "  Temp(x);\n"
+                     "}\n"
+                     "} // namespace nb\n"
+                     "} // namespace na\n";
+  std::string Expected = "namespace na {\n"
+                         "struct X {};\n"
+                         "\n"
+                         "} // namespace na\n"
+                         "namespace x {\n"
+                         "namespace y {\n"
+                         "template <typename TT>\n"
+                         "void TempTemp(const TT& t) {\n"
+                         "  TT tmp;\n"
+                         "}\n"
+                         "template <typename T>\n"
+                         "void Temp(const T& t) {\n"
+                         "  T tmp = t;\n"
+                         "  TempTemp(tmp);\n"
+                         "  TempTemp(t);\n"
+                         "}\n"
+                         "void f() {\n"
+                         "  na::X x;\n"
+                         "  Temp(x);\n"
+                         "}\n"
+                         "} // namespace y\n"
+                         "} // namespace x\n";
+
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+} // anonymous namespace
+} // namespace change_namespace
+} // namespace clang
diff --git a/unittests/clang-apply-replacements/ApplyReplacementsTest.cpp b/unittests/clang-apply-replacements/ApplyReplacementsTest.cpp
new file mode 100644 (file)
index 0000000..39f5b6f
--- /dev/null
@@ -0,0 +1,53 @@
+//===- clang-apply-replacements/ApplyReplacementsTest.cpp
+//----------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang-apply-replacements/Tooling/ApplyReplacements.h"
+#include "gtest/gtest.h"
+
+using namespace clang::replace;
+using namespace llvm;
+
+namespace clang {
+namespace tooling {
+
+static TUDiagnostics
+makeTUDiagnostics(const std::string &MainSourceFile, StringRef DiagnosticName,
+                  const DiagnosticMessage &Message,
+                  const StringMap<Replacements> &Replacements,
+                  StringRef BuildDirectory) {
+  TUDiagnostics TUs;
+  TUs.push_back({MainSourceFile,
+                 {{DiagnosticName,
+                   Message,
+                   Replacements,
+                   {},
+                   Diagnostic::Warning,
+                   BuildDirectory}}});
+  return TUs;
+}
+
+// Test to ensure diagnostics with no fixes, will be merged correctly
+// before applying.
+TEST(ApplyReplacementsTest, mergeDiagnosticsWithNoFixes) {
+  IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(new DiagnosticOptions());
+  DiagnosticsEngine Diagnostics(
+      IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), DiagOpts.get());
+  FileManager Files((FileSystemOptions()));
+  SourceManager SM(Diagnostics, Files);
+  TUDiagnostics TUs =
+      makeTUDiagnostics("path/to/source.cpp", "diagnostic", {}, {}, "path/to");
+  FileToReplacementsMap ReplacementsMap;
+
+  EXPECT_TRUE(mergeAndDeduplicate(TUs, ReplacementsMap, SM));
+  EXPECT_TRUE(ReplacementsMap.empty());
+}
+
+} // end namespace tooling
+} // end namespace clang
diff --git a/unittests/clang-apply-replacements/CMakeLists.txt b/unittests/clang-apply-replacements/CMakeLists.txt
new file mode 100644 (file)
index 0000000..f06dae6
--- /dev/null
@@ -0,0 +1,19 @@
+get_filename_component(ClangApplyReplacementsLocation
+  "${CMAKE_CURRENT_SOURCE_DIR}/../../clang-apply-replacements/include" REALPATH)
+get_filename_component(CommonIncLocation
+  "${CMAKE_CURRENT_SOURCE_DIR}/../include" REALPATH)
+include_directories(
+  ${ClangApplyReplacementsLocation}
+  ${CommonIncLocation}
+  )
+
+add_extra_unittest(ClangApplyReplacementsTests
+  ApplyReplacementsTest.cpp
+  ReformattingTest.cpp
+  )
+
+target_link_libraries(ClangApplyReplacementsTests
+  clangApplyReplacements
+  clangBasic
+  clangToolingCore
+  )
diff --git a/unittests/clang-apply-replacements/ReformattingTest.cpp b/unittests/clang-apply-replacements/ReformattingTest.cpp
new file mode 100644 (file)
index 0000000..48083ed
--- /dev/null
@@ -0,0 +1,67 @@
+//===- clang-apply-replacements/ReformattingTest.cpp ----------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang-apply-replacements/Tooling/ApplyReplacements.h"
+#include "common/VirtualFileHelper.h"
+#include "clang/Format/Format.h"
+#include "clang/Tooling/Refactoring.h"
+#include "gtest/gtest.h"
+
+using namespace clang;
+using namespace clang::tooling;
+using namespace clang::replace;
+
+typedef std::vector<clang::tooling::Replacement> ReplacementsVec;
+
+static Replacement makeReplacement(unsigned Offset, unsigned Length,
+                                   unsigned ReplacementLength,
+                                   llvm::StringRef FilePath = "") {
+  return Replacement(FilePath, Offset, Length,
+                     std::string(ReplacementLength, '~'));
+}
+
+// generate a set of replacements containing one element
+static ReplacementsVec makeReplacements(unsigned Offset, unsigned Length,
+                                        unsigned ReplacementLength,
+                                        llvm::StringRef FilePath = "~") {
+  ReplacementsVec Replaces;
+  Replaces.push_back(
+      makeReplacement(Offset, Length, ReplacementLength, FilePath));
+  return Replaces;
+}
+
+// Put these functions in the clang::tooling namespace so arg-dependent name
+// lookup finds these functions for the EXPECT_EQ macros below.
+namespace clang {
+namespace tooling {
+
+std::ostream &operator<<(std::ostream &os, const Range &R) {
+  return os << "Range(" << R.getOffset() << ", " << R.getLength() << ")";
+}
+
+} // namespace tooling
+} // namespace clang
+
+// Ensure zero-length ranges are produced. Even lines where things are deleted
+// need reformatting.
+TEST(CalculateChangedRangesTest, producesZeroLengthRange) {
+  RangeVector Changes = calculateChangedRanges(makeReplacements(0, 4, 0));
+  EXPECT_EQ(Range(0, 0), Changes.front());
+}
+
+// Basic test to ensure replacements turn into ranges properly.
+TEST(CalculateChangedRangesTest, calculatesRanges) {
+  ReplacementsVec R;
+  R.push_back(makeReplacement(2, 0, 3));
+  R.push_back(makeReplacement(5, 2, 4));
+  RangeVector Changes = calculateChangedRanges(R);
+
+  Range ExpectedRanges[] = { Range(2, 3), Range(8, 4) };
+  EXPECT_TRUE(std::equal(Changes.begin(), Changes.end(), ExpectedRanges));
+}
diff --git a/unittests/clang-move/CMakeLists.txt b/unittests/clang-move/CMakeLists.txt
new file mode 100644 (file)
index 0000000..e393b29
--- /dev/null
@@ -0,0 +1,28 @@
+set(LLVM_LINK_COMPONENTS
+  support
+  )
+
+get_filename_component(INCLUDE_FIXER_SOURCE_DIR
+  ${CMAKE_CURRENT_SOURCE_DIR}/../../clang-move REALPATH)
+include_directories(
+  ${INCLUDE_FIXER_SOURCE_DIR}
+  )
+
+# We'd like to clang/unittests/Tooling/RewriterTestContext.h in the test.
+include_directories(${CLANG_SOURCE_DIR})
+
+add_extra_unittest(ClangMoveTests
+  ClangMoveTests.cpp
+  )
+
+target_link_libraries(ClangMoveTests
+  clangAST
+  clangASTMatchers
+  clangBasic
+  clangFormat
+  clangFrontend
+  clangMove
+  clangRewrite
+  clangTooling
+  clangToolingCore
+  )
diff --git a/unittests/clang-move/ClangMoveTests.cpp b/unittests/clang-move/ClangMoveTests.cpp
new file mode 100644 (file)
index 0000000..ad2a052
--- /dev/null
@@ -0,0 +1,580 @@
+//===-- ClangMoveTest.cpp - clang-move unit tests -------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ClangMove.h"
+#include "unittests/Tooling/RewriterTestContext.h"
+#include "clang/Format/Format.h"
+#include "clang/Frontend/FrontendActions.h"
+#include "clang/Frontend/TextDiagnosticPrinter.h"
+#include "clang/Rewrite/Core/Rewriter.h"
+#include "clang/Tooling/Refactoring.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/StringRef.h"
+#include "gtest/gtest.h"
+#include <string>
+#include <vector>
+
+namespace clang {
+namespace move {
+namespace {
+
+const char TestHeaderName[] = "foo.h";
+
+const char TestCCName[] = "foo.cc";
+
+const char TestHeader[] = "namespace a {\n"
+                          "class C1; // test\n"
+                          "template <typename T> class C2;\n"
+                          "namespace b {\n"
+                          "// This is a Foo class\n"
+                          "// which is used in\n"
+                          "// test.\n"
+                          "class Foo {\n"
+                          "public:\n"
+                          "  void f();\n"
+                          "\n"
+                          "private:\n"
+                          "  C1 *c1;\n"
+                          "  static int b;\n"
+                          "}; // abc\n"
+                          "\n"
+                          "class Foo2 {\n"
+                          "public:\n"
+                          "  int f();\n"
+                          "};\n"
+                          "} // namespace b\n"
+                          "} // namespace a\n";
+
+const char TestCC[] = "#include \"foo.h\"\n"
+                      "namespace a {\n"
+                      "namespace b {\n"
+                      "namespace {\n"
+                      "// comment1.\n"
+                      "void f1() {}\n"
+                      "/// comment2.\n"
+                      "int kConstInt1 = 0;\n"
+                      "} // namespace\n"
+                      "\n"
+                      "/* comment 3*/\n"
+                      "static int kConstInt2 = 1;\n"
+                      "\n"
+                      "/** comment4\n"
+                      " */\n"
+                      "static int help() {\n"
+                      "  int a = 0;\n"
+                      "  return a;\n"
+                      "}\n"
+                      "\n"
+                      "// comment5\n"
+                      "// comment5\n"
+                      "void Foo::f() {\n"
+                      "  f1();\n"
+                      "  kConstInt1;\n"
+                      "  kConstInt2;\n"
+                      "  help();\n"
+                      "}\n"
+                      "\n"
+                      "/////////////\n"
+                      "// comment //\n"
+                      "/////////////\n"
+                      "int Foo::b = 2;\n"
+                      "int Foo2::f() {\n"
+                      "  kConstInt1;\n"
+                      "  kConstInt2;\n"
+                      "  help();\n"
+                      "  f1();\n"
+                      "  return 1;\n"
+                      "}\n"
+                      "} // namespace b\n"
+                      "} // namespace a\n";
+
+const char ExpectedTestHeader[] = "namespace a {\n"
+                                  "class C1; // test\n"
+                                  "template <typename T> class C2;\n"
+                                  "namespace b {\n"
+                                  "\n"
+                                  "class Foo2 {\n"
+                                  "public:\n"
+                                  "  int f();\n"
+                                  "};\n"
+                                  "} // namespace b\n"
+                                  "} // namespace a\n";
+
+const char ExpectedTestCC[] = "#include \"foo.h\"\n"
+                              "namespace a {\n"
+                              "namespace b {\n"
+                              "namespace {\n"
+                              "// comment1.\n"
+                              "void f1() {}\n"
+                              "/// comment2.\n"
+                              "int kConstInt1 = 0;\n"
+                              "} // namespace\n"
+                              "\n"
+                              "/* comment 3*/\n"
+                              "static int kConstInt2 = 1;\n"
+                              "\n"
+                              "/** comment4\n"
+                              " */\n"
+                              "static int help() {\n"
+                              "  int a = 0;\n"
+                              "  return a;\n"
+                              "}\n"
+                              "\n"
+                              "int Foo2::f() {\n"
+                              "  kConstInt1;\n"
+                              "  kConstInt2;\n"
+                              "  help();\n"
+                              "  f1();\n"
+                              "  return 1;\n"
+                              "}\n"
+                              "} // namespace b\n"
+                              "} // namespace a\n";
+
+const char ExpectedNewHeader[] = "#ifndef NEW_FOO_H\n"
+                                 "#define NEW_FOO_H\n"
+                                 "\n"
+                                 "namespace a {\n"
+                                 "class C1; // test\n"
+                                 "\n"
+                                 "template <typename T> class C2;\n"
+                                 "namespace b {\n"
+                                 "// This is a Foo class\n"
+                                 "// which is used in\n"
+                                 "// test.\n"
+                                 "class Foo {\n"
+                                 "public:\n"
+                                 "  void f();\n"
+                                 "\n"
+                                 "private:\n"
+                                 "  C1 *c1;\n"
+                                 "  static int b;\n"
+                                 "}; // abc\n"
+                                 "} // namespace b\n"
+                                 "} // namespace a\n"
+                                 "\n"
+                                 "#endif // NEW_FOO_H\n";
+
+const char ExpectedNewCC[] = "namespace a {\n"
+                             "namespace b {\n"
+                             "namespace {\n"
+                             "// comment1.\n"
+                             "void f1() {}\n"
+                             "\n"
+                             "/// comment2.\n"
+                             "int kConstInt1 = 0;\n"
+                             "} // namespace\n"
+                             "\n"
+                             "/* comment 3*/\n"
+                             "static int kConstInt2 = 1;\n"
+                             "\n"
+                             "/** comment4\n"
+                             " */\n"
+                             "static int help() {\n"
+                             "  int a = 0;\n"
+                             "  return a;\n"
+                             "}\n"
+                             "\n"
+                             "// comment5\n"
+                             "// comment5\n"
+                             "void Foo::f() {\n"
+                             "  f1();\n"
+                             "  kConstInt1;\n"
+                             "  kConstInt2;\n"
+                             "  help();\n"
+                             "}\n"
+                             "\n"
+                             "/////////////\n"
+                             "// comment //\n"
+                             "/////////////\n"
+                             "int Foo::b = 2;\n"
+                             "} // namespace b\n"
+                             "} // namespace a\n";
+
+std::map<std::string, std::string>
+runClangMoveOnCode(const move::MoveDefinitionSpec &Spec,
+                   const char *const Header = TestHeader,
+                   const char *const CC = TestCC,
+                   DeclarationReporter *const Reporter = nullptr) {
+  clang::RewriterTestContext Context;
+
+  std::map<llvm::StringRef, clang::FileID> FileToFileID;
+  std::vector<std::pair<std::string, std::string>> FileToSourceText = {
+      {TestHeaderName, Header}, {TestCCName, CC}};
+
+  auto CreateFiles = [&Context, &FileToFileID](llvm::StringRef Name,
+                                               llvm::StringRef Code) {
+    if (!Name.empty()) {
+      FileToFileID[Name] = Context.createInMemoryFile(Name, Code);
+    }
+  };
+  CreateFiles(Spec.NewCC, "");
+  CreateFiles(Spec.NewHeader, "");
+  CreateFiles(Spec.OldHeader, Header);
+  CreateFiles(Spec.OldCC, CC);
+
+  std::map<std::string, tooling::Replacements> FileToReplacements;
+  llvm::SmallString<128> InitialDirectory;
+  std::error_code EC = llvm::sys::fs::current_path(InitialDirectory);
+  assert(!EC);
+  (void)EC;
+  ClangMoveContext MoveContext = {Spec, FileToReplacements,
+                                  InitialDirectory.str(), "LLVM",
+                                  Reporter != nullptr};
+
+  auto Factory = llvm::make_unique<clang::move::ClangMoveActionFactory>(
+      &MoveContext, Reporter);
+
+  tooling::runToolOnCodeWithArgs(
+      Factory->create(), CC, {"-std=c++11", "-fparse-all-comments"},
+      TestCCName, "clang-move", std::make_shared<PCHContainerOperations>(),
+      FileToSourceText);
+  formatAndApplyAllReplacements(FileToReplacements, Context.Rewrite, "llvm");
+  // The Key is file name, value is the new code after moving the class.
+  std::map<std::string, std::string> Results;
+  for (const auto &It : FileToReplacements) {
+    StringRef FilePath = It.first;
+    Results[FilePath] = Context.getRewrittenText(FileToFileID[FilePath]);
+  }
+  return Results;
+}
+
+TEST(ClangMove, MoveHeaderAndCC) {
+  move::MoveDefinitionSpec Spec;
+  Spec.Names = {std::string("a::b::Foo")};
+  Spec.OldHeader = "foo.h";
+  Spec.OldCC = "foo.cc";
+  Spec.NewHeader = "new_foo.h";
+  Spec.NewCC = "new_foo.cc";
+  std::string ExpectedHeader = "#include \"" + Spec.NewHeader + "\"\n\n";
+  auto Results = runClangMoveOnCode(Spec);
+  EXPECT_EQ(ExpectedTestHeader, Results[Spec.OldHeader]);
+  EXPECT_EQ(ExpectedTestCC, Results[Spec.OldCC]);
+  EXPECT_EQ(ExpectedNewHeader, Results[Spec.NewHeader]);
+  EXPECT_EQ(ExpectedHeader + ExpectedNewCC, Results[Spec.NewCC]);
+}
+
+TEST(ClangMove, MoveHeaderOnly) {
+  move::MoveDefinitionSpec Spec;
+  Spec.Names = {std::string("a::b::Foo")};
+  Spec.OldHeader = "foo.h";
+  Spec.NewHeader = "new_foo.h";
+  auto Results = runClangMoveOnCode(Spec);
+  EXPECT_EQ(2u, Results.size());
+  EXPECT_EQ(ExpectedTestHeader, Results[Spec.OldHeader]);
+  EXPECT_EQ(ExpectedNewHeader, Results[Spec.NewHeader]);
+}
+
+TEST(ClangMove, MoveCCOnly) {
+  move::MoveDefinitionSpec Spec;
+  Spec.Names = {std::string("a::b::Foo")};
+  Spec.OldCC = "foo.cc";
+  Spec.NewCC = "new_foo.cc";
+  std::string ExpectedHeader = "#include \"foo.h\"\n\n";
+  auto Results = runClangMoveOnCode(Spec);
+  EXPECT_EQ(2u, Results.size());
+  EXPECT_EQ(ExpectedTestCC, Results[Spec.OldCC]);
+  EXPECT_EQ(ExpectedHeader + ExpectedNewCC, Results[Spec.NewCC]);
+}
+
+TEST(ClangMove, MoveNonExistClass) {
+  move::MoveDefinitionSpec Spec;
+  Spec.Names = {std::string("NonExistFoo")};
+  Spec.OldHeader = "foo.h";
+  Spec.OldCC = "foo.cc";
+  Spec.NewHeader = "new_foo.h";
+  Spec.NewCC = "new_foo.cc";
+  auto Results = runClangMoveOnCode(Spec);
+  EXPECT_EQ(0u, Results.size());
+}
+
+TEST(ClangMove, MoveAll) {
+  std::vector<std::string> TestHeaders = {
+    "class A {\npublic:\n  int f();\n};",
+    // forward declaration.
+    "class B;\nclass A {\npublic:\n  int f();\n};",
+    // template forward declaration.
+    "template <typename T> class B;\nclass A {\npublic:\n  int f();\n};",
+    "namespace a {}\nclass A {\npublic:\n  int f();\n};",
+    "namespace a {}\nusing namespace a;\nclass A {\npublic:\n  int f();\n};",
+  };
+  const char Code[] = "#include \"foo.h\"\nint A::f() { return 0; }";
+  move::MoveDefinitionSpec Spec;
+  Spec.Names.push_back("A");
+  Spec.OldHeader = "foo.h";
+  Spec.OldCC = "foo.cc";
+  Spec.NewHeader = "new_foo.h";
+  Spec.NewCC = "new_foo.cc";
+  for (const auto& Header : TestHeaders) {
+    auto Results = runClangMoveOnCode(Spec, Header.c_str(), Code);
+    EXPECT_EQ(Header, Results[Spec.NewHeader]);
+    EXPECT_EQ("", Results[Spec.OldHeader]);
+    EXPECT_EQ("", Results[Spec.OldCC]);
+  }
+}
+
+TEST(ClangMove, MoveAllMultipleClasses) {
+  move::MoveDefinitionSpec Spec;
+  std::vector<std::string> TestHeaders = {
+    "class C;\nclass A {\npublic:\n  int f();\n};\nclass B {};",
+    "class C;\nclass B;\nclass A {\npublic:\n  int f();\n};\nclass B {};",
+  };
+  const char Code[] = "#include \"foo.h\"\nint A::f() { return 0; }";
+  Spec.Names = {std::string("A"), std::string("B")};
+  Spec.OldHeader = "foo.h";
+  Spec.OldCC = "foo.cc";
+  Spec.NewHeader = "new_foo.h";
+  Spec.NewCC = "new_foo.cc";
+  for (const auto& Header : TestHeaders) {
+    auto Results = runClangMoveOnCode(Spec, Header.c_str(), Code);
+    EXPECT_EQ(Header, Results[Spec.NewHeader]);
+    EXPECT_EQ("", Results[Spec.OldHeader]);
+    EXPECT_EQ("", Results[Spec.OldCC]);
+  }
+}
+
+TEST(ClangMove, DontMoveAll) {
+  const char ExpectedHeader[] = "#ifndef NEW_FOO_H\n"
+                                "#define NEW_FOO_H\n"
+                                "\n"
+                                "class A {\npublic:\n  int f();\n};\n"
+                                "\n"
+                                "#endif // NEW_FOO_H\n";
+  const char Code[] = "#include \"foo.h\"\nint A::f() { return 0; }";
+  std::vector<std::string> TestHeaders = {
+    "class B {};\nclass A {\npublic:\n  int f();\n};\n",
+    "void f() {};\nclass A {\npublic:\n  int f();\n};\n",
+  };
+  move::MoveDefinitionSpec Spec;
+  Spec.Names.push_back("A");
+  Spec.OldHeader = "foo.h";
+  Spec.OldCC = "foo.cc";
+  Spec.NewHeader = "new_foo.h";
+  Spec.NewCC = "new_foo.cc";
+  for (const auto& Header : TestHeaders) {
+    auto Results = runClangMoveOnCode(Spec, Header.c_str(), Code);
+    EXPECT_EQ(ExpectedHeader, Results[Spec.NewHeader]);
+    // The expected old header should not contain class A definition.
+    std::string ExpectedOldHeader = Header.substr(0, Header.size() - 32);
+    EXPECT_EQ(ExpectedOldHeader, Results[Spec.OldHeader]);
+  }
+}
+
+TEST(ClangMove, MacroInFunction) {
+  const char TestHeader[] = "#define INT int\n"
+                            "class A {\npublic:\n  int f();\n};\n"
+                            "class B {};\n";
+  const char TestCode[] = "#include \"foo.h\"\n"
+                          "INT A::f() { return 0; }\n";
+  const char ExpectedNewCode[] = "#include \"new_foo.h\"\n\n"
+                                 "INT A::f() { return 0; }\n";
+  move::MoveDefinitionSpec Spec;
+  Spec.Names.push_back("A");
+  Spec.OldHeader = "foo.h";
+  Spec.OldCC = "foo.cc";
+  Spec.NewHeader = "new_foo.h";
+  Spec.NewCC = "new_foo.cc";
+  auto Results = runClangMoveOnCode(Spec, TestHeader, TestCode);
+  EXPECT_EQ(ExpectedNewCode, Results[Spec.NewCC]);
+}
+
+TEST(ClangMove, DefinitionInMacro) {
+  const char TestHeader[] = "#define DEF(CLASS) void CLASS##_::f() {}\n"
+                            "class A_ {\nvoid f();\n};\n"
+                            "class B {};\n";
+  const char TestCode[] = "#include \"foo.h\"\n"
+                          "DEF(A)\n";
+  const char ExpectedNewCode[] = "#include \"new_foo.h\"\n\n"
+                                 "DEF(A)\n";
+  move::MoveDefinitionSpec Spec;
+  Spec.Names.push_back("A_");
+  Spec.OldHeader = "foo.h";
+  Spec.OldCC = "foo.cc";
+  Spec.NewHeader = "new_foo.h";
+  Spec.NewCC = "new_foo.cc";
+  auto Results = runClangMoveOnCode(Spec, TestHeader, TestCode);
+  EXPECT_EQ(ExpectedNewCode, Results[Spec.NewCC]);
+}
+
+TEST(ClangMove, WellFormattedCode) {
+  const std::string CommonHeader =
+      "namespace a {\n"
+      "namespace b {\n"
+      "namespace c {\n"
+      "class C;\n"
+      "\n"
+      "class A {\npublic:\n  void f();\n  void f2();\n};\n"
+      "} // namespace c\n"
+      "} // namespace b\n"
+      "\n"
+      "namespace d {\n"
+      "namespace e {\n"
+      "class B {\npublic:\n  void f();\n};\n"
+      "} // namespace e\n"
+      "} // namespace d\n"
+      "} // namespace a\n";
+  const std::string CommonCode = "\n"
+                                 "namespace a {\n"
+                                 "namespace b {\n"
+                                 "namespace c {\n"
+                                 "void A::f() {}\n"
+                                 "\n"
+                                 "void A::f2() {}\n"
+                                 "} // namespace c\n"
+                                 "} // namespace b\n"
+                                 "\n"
+                                 "namespace d {\n"
+                                 "namespace e {\n"
+                                 "void B::f() {}\n"
+                                 "} // namespace e\n"
+                                 "} // namespace d\n"
+                                 "} // namespace a\n";
+  // Add dummy class to prevent behavior of moving all declarations from header.
+  const std::string TestHeader = CommonHeader + "class D {};\n";
+  const std::string TestCode = "#include \"foo.h\"\n" + CommonCode;
+  const std::string ExpectedNewHeader = "#ifndef NEW_FOO_H\n"
+                                        "#define NEW_FOO_H\n"
+                                        "\n" +
+                                        CommonHeader +
+                                        "\n"
+                                        "#endif // NEW_FOO_H\n";
+  const std::string ExpectedNewCC = "#include \"new_foo.h\"\n" + CommonCode;
+  move::MoveDefinitionSpec Spec;
+  Spec.Names.push_back("a::b::c::A");
+  Spec.Names.push_back("a::d::e::B");
+  Spec.OldHeader = "foo.h";
+  Spec.OldCC = "foo.cc";
+  Spec.NewHeader = "new_foo.h";
+  Spec.NewCC = "new_foo.cc";
+  auto Results = runClangMoveOnCode(Spec, TestHeader.c_str(), TestCode.c_str());
+  EXPECT_EQ(ExpectedNewCC, Results[Spec.NewCC]);
+  EXPECT_EQ(ExpectedNewHeader, Results[Spec.NewHeader]);
+}
+
+TEST(ClangMove, AddDependentNewHeader) {
+  const char TestHeader[] = "class A {};\n"
+                            "class B {};\n";
+  const char TestCode[] = "#include \"foo.h\"\n";
+  const char ExpectedOldHeader[] = "#include \"new_foo.h\"\nclass B {};\n";
+  const char ExpectedNewHeader[] = "#ifndef NEW_FOO_H\n"
+                                   "#define NEW_FOO_H\n"
+                                   "\n"
+                                   "class A {};\n"
+                                   "\n"
+                                   "#endif // NEW_FOO_H\n";
+  move::MoveDefinitionSpec Spec;
+  Spec.Names.push_back("A");
+  Spec.OldHeader = "foo.h";
+  Spec.OldCC = "foo.cc";
+  Spec.NewHeader = "new_foo.h";
+  Spec.NewCC = "new_foo.cc";
+  Spec.OldDependOnNew = true;
+  auto Results = runClangMoveOnCode(Spec, TestHeader, TestCode);
+  EXPECT_EQ(ExpectedOldHeader, Results[Spec.OldHeader]);
+  EXPECT_EQ(ExpectedNewHeader, Results[Spec.NewHeader]);
+}
+
+TEST(ClangMove, AddDependentOldHeader) {
+  const char TestHeader[] = "class A {};\n"
+                            "class B {};\n";
+  const char TestCode[] = "#include \"foo.h\"\n";
+  const char ExpectedNewHeader[] = "#ifndef NEW_FOO_H\n"
+                                   "#define NEW_FOO_H\n"
+                                   "\n"
+                                   "#include \"foo.h\"\n"
+                                   "\n"
+                                   "class B {};\n"
+                                   "\n"
+                                   "#endif // NEW_FOO_H\n";
+  const char ExpectedOldHeader[] = "class A {};\n";
+  move::MoveDefinitionSpec Spec;
+  Spec.Names.push_back("B");
+  Spec.OldHeader = "foo.h";
+  Spec.OldCC = "foo.cc";
+  Spec.NewHeader = "new_foo.h";
+  Spec.NewCC = "new_foo.cc";
+  Spec.NewDependOnOld = true;
+  auto Results = runClangMoveOnCode(Spec, TestHeader, TestCode);
+  EXPECT_EQ(ExpectedNewHeader, Results[Spec.NewHeader]);
+  EXPECT_EQ(ExpectedOldHeader, Results[Spec.OldHeader]);
+}
+
+TEST(ClangMove, DumpDecls) {
+  const char TestHeader[] = "template <typename T>\n"
+                            "class A {\n"
+                            " public:\n"
+                            "  void f();\n"
+                            "  template <typename U> void h();\n"
+                            "  static int b;\n"
+                            "};\n"
+                            "\n"
+                            "template <typename T> void A<T>::f() {}\n"
+                            "\n"
+                            "template <typename T>\n"
+                            "template <typename U>\n"
+                            "void A<T>::h() {}\n"
+                            "\n"
+                            "template <typename T> int A<T>::b = 2;\n"
+                            "\n"
+                            "template <> class A<int> {};\n"
+                            "\n"
+                            "class B {};\n"
+                            "\n"
+                            "namespace a {\n"
+                            "class Move1 {};\n"
+                            "void f1() {}\n"
+                            "void f2();\n"
+                            "} // namespace a\n"
+                            "\n"
+                            "class ForwardClass;\n"
+                            "namespace a {\n"
+                            "namespace b {\n"
+                            "class Move1 { public : void f(); };\n"
+                            "void f() {}\n"
+                            "enum E1 { Green };\n"
+                            "enum class E2 { Red };\n"
+                            "typedef int Int2;\n"
+                            "typedef A<double> A_d;"
+                            "using Int = int;\n"
+                            "extern int kGlobalInt;\n"
+                            "extern const char* const kGlobalStr;\n"
+                            "} // namespace b\n"
+                            "} // namespace a\n";
+  const char TestCode[] = "#include \"foo.h\"\n";
+  move::MoveDefinitionSpec Spec;
+  Spec.Names.push_back("B");
+  Spec.OldHeader = "foo.h";
+  Spec.OldCC = "foo.cc";
+  Spec.NewHeader = "new_foo.h";
+  Spec.NewCC = "new_foo.cc";
+  DeclarationReporter Reporter;
+  std::set<DeclarationReporter::DeclarationPair> ExpectedDeclarations = {
+      {"A", "Class"},
+      {"B", "Class"},
+      {"a::Move1", "Class"},
+      {"a::f1", "Function"},
+      {"a::f2", "Function"},
+      {"a::b::Move1", "Class"},
+      {"a::b::f", "Function"},
+      {"a::b::E1", "Enum"},
+      {"a::b::E2", "Enum"},
+      {"a::b::Int2", "TypeAlias"},
+      {"a::b::A_d", "TypeAlias"},
+      {"a::b::Int", "TypeAlias"},
+      {"a::b::kGlobalInt", "Variable"},
+      {"a::b::kGlobalStr", "Variable"}};
+  runClangMoveOnCode(Spec, TestHeader, TestCode, &Reporter);
+  std::set<DeclarationReporter::DeclarationPair> Results;
+  for (const auto& DelPair : Reporter.getDeclarationList())
+    Results.insert(DelPair);
+  EXPECT_EQ(ExpectedDeclarations, Results);
+}
+
+} // namespace
+} // namespce move
+} // namespace clang
diff --git a/unittests/clang-query/CMakeLists.txt b/unittests/clang-query/CMakeLists.txt
new file mode 100644 (file)
index 0000000..3f145d2
--- /dev/null
@@ -0,0 +1,22 @@
+set(LLVM_LINK_COMPONENTS
+  support
+  )
+
+include_directories(
+  ${CMAKE_CURRENT_SOURCE_DIR}/../../clang-query
+  )
+
+add_extra_unittest(ClangQueryTests
+  QueryEngineTest.cpp
+  QueryParserTest.cpp
+  )
+
+target_link_libraries(ClangQueryTests
+  clangAST
+  clangASTMatchers
+  clangBasic
+  clangDynamicASTMatchers
+  clangFrontend
+  clangQuery
+  clangTooling
+  )
diff --git a/unittests/clang-query/QueryEngineTest.cpp b/unittests/clang-query/QueryEngineTest.cpp
new file mode 100644 (file)
index 0000000..631a53e
--- /dev/null
@@ -0,0 +1,146 @@
+//===---- QueryTest.cpp - clang-query test --------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Query.h"
+#include "QueryParser.h"
+#include "QuerySession.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/ASTMatchers/Dynamic/VariantValue.h"
+#include "clang/Frontend/ASTUnit.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/raw_ostream.h"
+#include "gtest/gtest.h"
+#include <string>
+
+using namespace clang;
+using namespace clang::ast_matchers;
+using namespace clang::ast_matchers::dynamic;
+using namespace clang::query;
+using namespace clang::tooling;
+
+class QueryEngineTest : public ::testing::Test {
+  ArrayRef<std::unique_ptr<ASTUnit>> mkASTUnit2(std::unique_ptr<ASTUnit> a,
+                                                std::unique_ptr<ASTUnit> b) {
+    ASTs[0] = std::move(a);
+    ASTs[1] = std::move(b);
+    return ArrayRef<std::unique_ptr<ASTUnit>>(ASTs);
+  }
+
+protected:
+  QueryEngineTest()
+      : S(mkASTUnit2(buildASTFromCode("void foo1(void) {}\nvoid foo2(void) {}",
+                                      "foo.cc"),
+                     buildASTFromCode("void bar1(void) {}\nvoid bar2(void) {}",
+                                      "bar.cc"))),
+        OS(Str) {}
+
+  std::unique_ptr<ASTUnit> ASTs[2];
+  QuerySession S;
+
+  std::string Str;
+  llvm::raw_string_ostream OS;
+};
+
+TEST_F(QueryEngineTest, Basic) {
+  DynTypedMatcher FnMatcher = functionDecl();
+  DynTypedMatcher FooMatcher = functionDecl(hasName("foo1"));
+
+  EXPECT_TRUE(NoOpQuery().run(OS, S));
+
+  EXPECT_EQ("", OS.str());
+
+  Str.clear();
+
+  EXPECT_FALSE(InvalidQuery("Parse error").run(OS, S));
+
+  EXPECT_EQ("Parse error\n", OS.str());
+
+  Str.clear();
+
+  EXPECT_TRUE(HelpQuery().run(OS, S));
+
+  EXPECT_TRUE(OS.str().find("Available commands:") != std::string::npos);
+
+  Str.clear();
+
+  EXPECT_TRUE(MatchQuery(FnMatcher).run(OS, S));
+
+  EXPECT_TRUE(OS.str().find("foo.cc:1:1: note: \"root\" binds here") !=
+              std::string::npos);
+  EXPECT_TRUE(OS.str().find("foo.cc:2:1: note: \"root\" binds here") !=
+              std::string::npos);
+  EXPECT_TRUE(OS.str().find("bar.cc:1:1: note: \"root\" binds here") !=
+              std::string::npos);
+  EXPECT_TRUE(OS.str().find("bar.cc:2:1: note: \"root\" binds here") !=
+              std::string::npos);
+  EXPECT_TRUE(OS.str().find("4 matches.") != std::string::npos);
+
+  Str.clear();
+
+  EXPECT_TRUE(MatchQuery(FooMatcher).run(OS, S));
+
+  EXPECT_TRUE(OS.str().find("foo.cc:1:1: note: \"root\" binds here") !=
+              std::string::npos);
+  EXPECT_TRUE(OS.str().find("1 match.") != std::string::npos);
+
+  Str.clear();
+
+  EXPECT_TRUE(
+      SetQuery<OutputKind>(&QuerySession::OutKind, OK_Print).run(OS, S));
+  EXPECT_TRUE(MatchQuery(FooMatcher).run(OS, S));
+
+  EXPECT_TRUE(OS.str().find("Binding for \"root\":\nvoid foo1()") !=
+              std::string::npos);
+
+  Str.clear();
+
+  EXPECT_TRUE(SetQuery<OutputKind>(&QuerySession::OutKind, OK_Dump).run(OS, S));
+  EXPECT_TRUE(MatchQuery(FooMatcher).run(OS, S));
+
+  EXPECT_TRUE(OS.str().find("FunctionDecl") != std::string::npos);
+
+  Str.clear();
+
+  EXPECT_TRUE(SetQuery<bool>(&QuerySession::BindRoot, false).run(OS, S));
+  EXPECT_TRUE(MatchQuery(FooMatcher).run(OS, S));
+
+  EXPECT_TRUE(OS.str().find("No bindings.") != std::string::npos);
+
+  Str.clear();
+
+  EXPECT_FALSE(MatchQuery(isArrow()).run(OS, S));
+
+  EXPECT_EQ("Not a valid top-level matcher.\n", OS.str());
+}
+
+TEST_F(QueryEngineTest, LetAndMatch) {
+  EXPECT_TRUE(QueryParser::parse("let x \"foo1\"", S)->run(OS, S));
+  EXPECT_EQ("", OS.str());
+  Str.clear();
+
+  EXPECT_TRUE(QueryParser::parse("let y hasName(x)", S)->run(OS, S));
+  EXPECT_EQ("", OS.str());
+  Str.clear();
+
+  EXPECT_TRUE(QueryParser::parse("match functionDecl(y)", S)->run(OS, S));
+  EXPECT_TRUE(OS.str().find("foo.cc:1:1: note: \"root\" binds here") !=
+              std::string::npos);
+  EXPECT_TRUE(OS.str().find("1 match.") != std::string::npos);
+  Str.clear();
+
+  EXPECT_TRUE(QueryParser::parse("unlet x", S)->run(OS, S));
+  EXPECT_EQ("", OS.str());
+  Str.clear();
+
+  EXPECT_FALSE(QueryParser::parse("let y hasName(x)", S)->run(OS, S));
+  EXPECT_EQ("1:2: Error parsing argument 1 for matcher hasName.\n"
+            "1:10: Value not found: x\n", OS.str());
+  Str.clear();
+}
diff --git a/unittests/clang-query/QueryParserTest.cpp b/unittests/clang-query/QueryParserTest.cpp
new file mode 100644 (file)
index 0000000..5588ea3
--- /dev/null
@@ -0,0 +1,158 @@
+//===---- QueryParserTest.cpp - clang-query test --------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "QueryParser.h"
+#include "Query.h"
+#include "QuerySession.h"
+#include "llvm/LineEditor/LineEditor.h"
+#include "gtest/gtest.h"
+
+using namespace clang;
+using namespace clang::query;
+
+class QueryParserTest : public ::testing::Test {
+protected:
+  QueryParserTest() : QS(llvm::ArrayRef<std::unique_ptr<ASTUnit>>()) {}
+  QueryRef parse(StringRef Code) { return QueryParser::parse(Code, QS); }
+
+  QuerySession QS;
+};
+
+TEST_F(QueryParserTest, NoOp) {
+  QueryRef Q = parse("");
+  EXPECT_TRUE(isa<NoOpQuery>(Q));
+
+  Q = parse("\n");
+  EXPECT_TRUE(isa<NoOpQuery>(Q));
+}
+
+TEST_F(QueryParserTest, Invalid) {
+  QueryRef Q = parse("foo");
+  ASSERT_TRUE(isa<InvalidQuery>(Q));
+  EXPECT_EQ("unknown command: foo", cast<InvalidQuery>(Q)->ErrStr);
+}
+
+TEST_F(QueryParserTest, Help) {
+  QueryRef Q = parse("help");
+  ASSERT_TRUE(isa<HelpQuery>(Q));
+
+  Q = parse("help me");
+  ASSERT_TRUE(isa<InvalidQuery>(Q));
+  EXPECT_EQ("unexpected extra input: ' me'", cast<InvalidQuery>(Q)->ErrStr);
+}
+
+TEST_F(QueryParserTest, Set) {
+  QueryRef Q = parse("set");
+  ASSERT_TRUE(isa<InvalidQuery>(Q));
+  EXPECT_EQ("expected variable name", cast<InvalidQuery>(Q)->ErrStr);
+
+  Q = parse("set foo bar");
+  ASSERT_TRUE(isa<InvalidQuery>(Q));
+  EXPECT_EQ("unknown variable: 'foo'", cast<InvalidQuery>(Q)->ErrStr);
+
+  Q = parse("set output");
+  ASSERT_TRUE(isa<InvalidQuery>(Q));
+  EXPECT_EQ("expected 'diag', 'print' or 'dump', got ''",
+            cast<InvalidQuery>(Q)->ErrStr);
+
+  Q = parse("set bind-root true foo");
+  ASSERT_TRUE(isa<InvalidQuery>(Q));
+  EXPECT_EQ("unexpected extra input: ' foo'", cast<InvalidQuery>(Q)->ErrStr);
+
+  Q = parse("set output foo");
+  ASSERT_TRUE(isa<InvalidQuery>(Q));
+  EXPECT_EQ("expected 'diag', 'print' or 'dump', got 'foo'",
+            cast<InvalidQuery>(Q)->ErrStr);
+
+  Q = parse("set output dump");
+  ASSERT_TRUE(isa<SetQuery<OutputKind> >(Q));
+  EXPECT_EQ(&QuerySession::OutKind, cast<SetQuery<OutputKind> >(Q)->Var);
+  EXPECT_EQ(OK_Dump, cast<SetQuery<OutputKind> >(Q)->Value);
+
+  Q = parse("set bind-root foo");
+  ASSERT_TRUE(isa<InvalidQuery>(Q));
+  EXPECT_EQ("expected 'true' or 'false', got 'foo'",
+            cast<InvalidQuery>(Q)->ErrStr);
+
+  Q = parse("set bind-root true");
+  ASSERT_TRUE(isa<SetQuery<bool> >(Q));
+  EXPECT_EQ(&QuerySession::BindRoot, cast<SetQuery<bool> >(Q)->Var);
+  EXPECT_EQ(true, cast<SetQuery<bool> >(Q)->Value);
+}
+
+TEST_F(QueryParserTest, Match) {
+  QueryRef Q = parse("match decl()");
+  ASSERT_TRUE(isa<MatchQuery>(Q));
+  EXPECT_TRUE(cast<MatchQuery>(Q)->Matcher.canConvertTo<Decl>());
+
+  Q = parse("m stmt()");
+  ASSERT_TRUE(isa<MatchQuery>(Q));
+  EXPECT_TRUE(cast<MatchQuery>(Q)->Matcher.canConvertTo<Stmt>());
+}
+
+TEST_F(QueryParserTest, LetUnlet) {
+  QueryRef Q = parse("let foo decl()");
+  ASSERT_TRUE(isa<LetQuery>(Q));
+  EXPECT_EQ("foo", cast<LetQuery>(Q)->Name);
+  EXPECT_TRUE(cast<LetQuery>(Q)->Value.isMatcher());
+  EXPECT_TRUE(cast<LetQuery>(Q)->Value.getMatcher().hasTypedMatcher<Decl>());
+
+  Q = parse("let bar \"str\"");
+  ASSERT_TRUE(isa<LetQuery>(Q));
+  EXPECT_EQ("bar", cast<LetQuery>(Q)->Name);
+  EXPECT_TRUE(cast<LetQuery>(Q)->Value.isString());
+  EXPECT_EQ("str", cast<LetQuery>(Q)->Value.getString());
+
+  Q = parse("let");
+  ASSERT_TRUE(isa<InvalidQuery>(Q));
+  EXPECT_EQ("expected variable name", cast<InvalidQuery>(Q)->ErrStr);
+
+  Q = parse("unlet x");
+  ASSERT_TRUE(isa<LetQuery>(Q));
+  EXPECT_EQ("x", cast<LetQuery>(Q)->Name);
+  EXPECT_FALSE(cast<LetQuery>(Q)->Value.hasValue());
+
+  Q = parse("unlet");
+  ASSERT_TRUE(isa<InvalidQuery>(Q));
+  EXPECT_EQ("expected variable name", cast<InvalidQuery>(Q)->ErrStr);
+
+  Q = parse("unlet x bad_data");
+  ASSERT_TRUE(isa<InvalidQuery>(Q));
+  EXPECT_EQ("unexpected extra input: ' bad_data'",
+            cast<InvalidQuery>(Q)->ErrStr);
+}
+
+TEST_F(QueryParserTest, Complete) {
+  std::vector<llvm::LineEditor::Completion> Comps =
+      QueryParser::complete("", 0, QS);
+  ASSERT_EQ(6u, Comps.size());
+  EXPECT_EQ("help ", Comps[0].TypedText);
+  EXPECT_EQ("help", Comps[0].DisplayText);
+  EXPECT_EQ("let ", Comps[1].TypedText);
+  EXPECT_EQ("let", Comps[1].DisplayText);
+  EXPECT_EQ("match ", Comps[2].TypedText);
+  EXPECT_EQ("match", Comps[2].DisplayText);
+  EXPECT_EQ("set ", Comps[3].TypedText);
+  EXPECT_EQ("set", Comps[3].DisplayText);
+  EXPECT_EQ("unlet ", Comps[4].TypedText);
+  EXPECT_EQ("unlet", Comps[4].DisplayText);
+  EXPECT_EQ("quit", Comps[5].DisplayText);
+  EXPECT_EQ("quit ", Comps[5].TypedText);
+
+  Comps = QueryParser::complete("set o", 5, QS);
+  ASSERT_EQ(1u, Comps.size());
+  EXPECT_EQ("utput ", Comps[0].TypedText);
+  EXPECT_EQ("output", Comps[0].DisplayText);
+
+  Comps = QueryParser::complete("match while", 11, QS);
+  ASSERT_EQ(1u, Comps.size());
+  EXPECT_EQ("Stmt(", Comps[0].TypedText);
+  EXPECT_EQ("Matcher<Stmt> whileStmt(Matcher<WhileStmt>...)",
+            Comps[0].DisplayText);
+}
diff --git a/unittests/clang-tidy/CMakeLists.txt b/unittests/clang-tidy/CMakeLists.txt
new file mode 100644 (file)
index 0000000..0d9ab7c
--- /dev/null
@@ -0,0 +1,36 @@
+set(LLVM_LINK_COMPONENTS
+  support
+  )
+
+get_filename_component(CLANG_LINT_SOURCE_DIR
+  ${CMAKE_CURRENT_SOURCE_DIR}/../../clang-tidy REALPATH)
+include_directories(${CLANG_LINT_SOURCE_DIR})
+
+add_extra_unittest(ClangTidyTests
+  ClangTidyDiagnosticConsumerTest.cpp
+  ClangTidyOptionsTest.cpp
+  IncludeInserterTest.cpp
+  GoogleModuleTest.cpp
+  LLVMModuleTest.cpp
+  MiscModuleTest.cpp
+  NamespaceAliaserTest.cpp
+  OverlappingReplacementsTest.cpp
+  UsingInserterTest.cpp
+  ReadabilityModuleTest.cpp)
+
+target_link_libraries(ClangTidyTests
+  clangAST
+  clangASTMatchers
+  clangBasic
+  clangFrontend
+  clangLex
+  clangTidy
+  clangTidyAndroidModule
+  clangTidyGoogleModule
+  clangTidyLLVMModule
+  clangTidyMiscModule
+  clangTidyReadabilityModule
+  clangTidyUtils
+  clangTooling
+  clangToolingCore
+  )
diff --git a/unittests/clang-tidy/ClangTidyDiagnosticConsumerTest.cpp b/unittests/clang-tidy/ClangTidyDiagnosticConsumerTest.cpp
new file mode 100644 (file)
index 0000000..0ee800d
--- /dev/null
@@ -0,0 +1,94 @@
+#include "ClangTidy.h"\r
+#include "ClangTidyTest.h"\r
+#include "gtest/gtest.h"\r
+\r
+namespace clang {\r
+namespace tidy {\r
+namespace test {\r
+\r
+class TestCheck : public ClangTidyCheck {\r
+public:\r
+  TestCheck(StringRef Name, ClangTidyContext *Context)\r
+      : ClangTidyCheck(Name, Context) {}\r
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override {\r
+    Finder->addMatcher(ast_matchers::varDecl().bind("var"), this);\r
+  }\r
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override {\r
+    const auto *Var = Result.Nodes.getNodeAs<VarDecl>("var");\r
+    // Add diagnostics in the wrong order.\r
+    diag(Var->getLocation(), "variable");\r
+    diag(Var->getTypeSpecStartLoc(), "type specifier");\r
+  }\r
+};\r
+\r
+TEST(ClangTidyDiagnosticConsumer, SortsErrors) {\r
+  std::vector<ClangTidyError> Errors;\r
+  runCheckOnCode<TestCheck>("int a;", &Errors);\r
+  EXPECT_EQ(2ul, Errors.size());\r
+  EXPECT_EQ("type specifier", Errors[0].Message.Message);\r
+  EXPECT_EQ("variable", Errors[1].Message.Message);\r
+}\r
+\r
+TEST(GlobList, Empty) {\r
+  GlobList Filter("");\r
+\r
+  EXPECT_TRUE(Filter.contains(""));\r
+  EXPECT_FALSE(Filter.contains("aaa"));\r
+}\r
+\r
+TEST(GlobList, Nothing) {\r
+  GlobList Filter("-*");\r
+\r
+  EXPECT_FALSE(Filter.contains(""));\r
+  EXPECT_FALSE(Filter.contains("a"));\r
+  EXPECT_FALSE(Filter.contains("-*"));\r
+  EXPECT_FALSE(Filter.contains("-"));\r
+  EXPECT_FALSE(Filter.contains("*"));\r
+}\r
+\r
+TEST(GlobList, Everything) {\r
+  GlobList Filter("*");\r
+\r
+  EXPECT_TRUE(Filter.contains(""));\r
+  EXPECT_TRUE(Filter.contains("aaaa"));\r
+  EXPECT_TRUE(Filter.contains("-*"));\r
+  EXPECT_TRUE(Filter.contains("-"));\r
+  EXPECT_TRUE(Filter.contains("*"));\r
+}\r
+\r
+TEST(GlobList, Simple) {\r
+  GlobList Filter("aaa");\r
+\r
+  EXPECT_TRUE(Filter.contains("aaa"));\r
+  EXPECT_FALSE(Filter.contains(""));\r
+  EXPECT_FALSE(Filter.contains("aa"));\r
+  EXPECT_FALSE(Filter.contains("aaaa"));\r
+  EXPECT_FALSE(Filter.contains("bbb"));\r
+}\r
+\r
+TEST(GlobList, WhitespacesAtBegin) {\r
+  GlobList Filter("-*,   a.b.*");\r
+\r
+  EXPECT_TRUE(Filter.contains("a.b.c"));\r
+  EXPECT_FALSE(Filter.contains("b.c"));\r
+}\r
+\r
+TEST(GlobList, Complex) {\r
+  GlobList Filter("*,-a.*, -b.*,   a.1.* ,-a.1.A.*,-..,-...,-..+,-*$, -*qwe* ");\r
+\r
+  EXPECT_TRUE(Filter.contains("aaa"));\r
+  EXPECT_TRUE(Filter.contains("qqq"));\r
+  EXPECT_FALSE(Filter.contains("a."));\r
+  EXPECT_FALSE(Filter.contains("a.b"));\r
+  EXPECT_FALSE(Filter.contains("b."));\r
+  EXPECT_FALSE(Filter.contains("b.b"));\r
+  EXPECT_TRUE(Filter.contains("a.1.b"));\r
+  EXPECT_FALSE(Filter.contains("a.1.A.a"));\r
+  EXPECT_FALSE(Filter.contains("qwe"));\r
+  EXPECT_FALSE(Filter.contains("asdfqweasdf"));\r
+  EXPECT_TRUE(Filter.contains("asdfqwEasdf"));\r
+}\r
+\r
+} // namespace test\r
+} // namespace tidy\r
+} // namespace clang\r
diff --git a/unittests/clang-tidy/ClangTidyOptionsTest.cpp b/unittests/clang-tidy/ClangTidyOptionsTest.cpp
new file mode 100644 (file)
index 0000000..36105a2
--- /dev/null
@@ -0,0 +1,104 @@
+#include "ClangTidyOptions.h"
+#include "gtest/gtest.h"
+#include "llvm/ADT/StringExtras.h"
+
+namespace clang {
+namespace tidy {
+namespace test {
+
+TEST(ParseLineFilter, EmptyFilter) {
+  ClangTidyGlobalOptions Options;
+  EXPECT_FALSE(parseLineFilter("", Options));
+  EXPECT_TRUE(Options.LineFilter.empty());
+  EXPECT_FALSE(parseLineFilter("[]", Options));
+  EXPECT_TRUE(Options.LineFilter.empty());
+}
+
+TEST(ParseLineFilter, InvalidFilter) {
+  ClangTidyGlobalOptions Options;
+  EXPECT_TRUE(!!parseLineFilter("asdf", Options));
+  EXPECT_TRUE(Options.LineFilter.empty());
+
+  EXPECT_TRUE(!!parseLineFilter("[{}]", Options));
+  EXPECT_TRUE(!!parseLineFilter("[{\"name\":\"\"}]", Options));
+  EXPECT_TRUE(
+      !!parseLineFilter("[{\"name\":\"test\",\"lines\":[[1]]}]", Options));
+  EXPECT_TRUE(
+      !!parseLineFilter("[{\"name\":\"test\",\"lines\":[[1,2,3]]}]", Options));
+  EXPECT_TRUE(
+      !!parseLineFilter("[{\"name\":\"test\",\"lines\":[[1,-1]]}]", Options));
+}
+
+TEST(ParseLineFilter, ValidFilter) {
+  ClangTidyGlobalOptions Options;
+  std::error_code Error = parseLineFilter(
+      "[{\"name\":\"file1.cpp\",\"lines\":[[3,15],[20,30],[42,42]]},"
+      "{\"name\":\"file2.h\"},"
+      "{\"name\":\"file3.cc\",\"lines\":[[100,1000]]}]",
+      Options);
+  EXPECT_FALSE(Error);
+  EXPECT_EQ(3u, Options.LineFilter.size());
+  EXPECT_EQ("file1.cpp", Options.LineFilter[0].Name);
+  EXPECT_EQ(3u, Options.LineFilter[0].LineRanges.size());
+  EXPECT_EQ(3u, Options.LineFilter[0].LineRanges[0].first);
+  EXPECT_EQ(15u, Options.LineFilter[0].LineRanges[0].second);
+  EXPECT_EQ(20u, Options.LineFilter[0].LineRanges[1].first);
+  EXPECT_EQ(30u, Options.LineFilter[0].LineRanges[1].second);
+  EXPECT_EQ(42u, Options.LineFilter[0].LineRanges[2].first);
+  EXPECT_EQ(42u, Options.LineFilter[0].LineRanges[2].second);
+  EXPECT_EQ("file2.h", Options.LineFilter[1].Name);
+  EXPECT_EQ(0u, Options.LineFilter[1].LineRanges.size());
+  EXPECT_EQ("file3.cc", Options.LineFilter[2].Name);
+  EXPECT_EQ(1u, Options.LineFilter[2].LineRanges.size());
+  EXPECT_EQ(100u, Options.LineFilter[2].LineRanges[0].first);
+  EXPECT_EQ(1000u, Options.LineFilter[2].LineRanges[0].second);
+}
+
+TEST(ParseConfiguration, ValidConfiguration) {
+  llvm::ErrorOr<ClangTidyOptions> Options =
+      parseConfiguration("Checks: \"-*,misc-*\"\n"
+                         "HeaderFilterRegex: \".*\"\n"
+                         "AnalyzeTemporaryDtors: true\n"
+                         "User: some.user");
+  EXPECT_TRUE(!!Options);
+  EXPECT_EQ("-*,misc-*", *Options->Checks);
+  EXPECT_EQ(".*", *Options->HeaderFilterRegex);
+  EXPECT_TRUE(*Options->AnalyzeTemporaryDtors);
+  EXPECT_EQ("some.user", *Options->User);
+}
+
+TEST(ParseConfiguration, MergeConfigurations) {
+  llvm::ErrorOr<ClangTidyOptions> Options1 = parseConfiguration(R"(
+      Checks: "check1,check2"
+      HeaderFilterRegex: "filter1"
+      AnalyzeTemporaryDtors: true
+      User: user1
+      ExtraArgs: ['arg1', 'arg2']
+      ExtraArgsBefore: ['arg-before1', 'arg-before2']
+  )");
+  ASSERT_TRUE(!!Options1);
+  llvm::ErrorOr<ClangTidyOptions> Options2 = parseConfiguration(R"(
+      Checks: "check3,check4"
+      HeaderFilterRegex: "filter2"
+      AnalyzeTemporaryDtors: false
+      User: user2
+      ExtraArgs: ['arg3', 'arg4']
+      ExtraArgsBefore: ['arg-before3', 'arg-before4']
+  )");
+  ASSERT_TRUE(!!Options2);
+  ClangTidyOptions Options = Options1->mergeWith(*Options2);
+  EXPECT_EQ("check1,check2,check3,check4", *Options.Checks);
+  EXPECT_EQ("filter2", *Options.HeaderFilterRegex);
+  EXPECT_FALSE(*Options.AnalyzeTemporaryDtors);
+  EXPECT_EQ("user2", *Options.User);
+  ASSERT_TRUE(Options.ExtraArgs.hasValue());
+  EXPECT_EQ("arg1,arg2,arg3,arg4", llvm::join(Options.ExtraArgs->begin(),
+                                              Options.ExtraArgs->end(), ","));
+  ASSERT_TRUE(Options.ExtraArgsBefore.hasValue());
+  EXPECT_EQ("arg-before1,arg-before2,arg-before3,arg-before4",
+            llvm::join(Options.ExtraArgsBefore->begin(),
+                       Options.ExtraArgsBefore->end(), ","));
+}
+} // namespace test
+} // namespace tidy
+} // namespace clang
diff --git a/unittests/clang-tidy/ClangTidyTest.h b/unittests/clang-tidy/ClangTidyTest.h
new file mode 100644 (file)
index 0000000..6dfce90
--- /dev/null
@@ -0,0 +1,151 @@
+//===--- ClangTidyTest.h - clang-tidy ---------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANG_TIDY_CLANGTIDYTEST_H
+#define LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANG_TIDY_CLANGTIDYTEST_H
+
+#include "ClangTidy.h"
+#include "ClangTidyDiagnosticConsumer.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendActions.h"
+#include "clang/Tooling/Refactoring.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/Optional.h"
+#include <map>
+#include <memory>
+
+namespace clang {
+namespace tidy {
+namespace test {
+
+class TestClangTidyAction : public ASTFrontendAction {
+public:
+  TestClangTidyAction(SmallVectorImpl<std::unique_ptr<ClangTidyCheck>> &Checks,
+                      ast_matchers::MatchFinder &Finder,
+                      ClangTidyContext &Context)
+      : Checks(Checks), Finder(Finder), Context(Context) {}
+
+private:
+  std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler,
+                                                 StringRef File) override {
+    Context.setSourceManager(&Compiler.getSourceManager());
+    Context.setCurrentFile(File);
+    Context.setASTContext(&Compiler.getASTContext());
+
+    for (auto &Check : Checks) {
+      Check->registerMatchers(&Finder);
+      Check->registerPPCallbacks(Compiler);
+    }
+    return Finder.newASTConsumer();
+  }
+
+  SmallVectorImpl<std::unique_ptr<ClangTidyCheck>> &Checks;
+  ast_matchers::MatchFinder &Finder;
+  ClangTidyContext &Context;
+};
+
+template <typename Check, typename... Checks> struct CheckFactory {
+  static void
+  createChecks(ClangTidyContext *Context,
+               SmallVectorImpl<std::unique_ptr<ClangTidyCheck>> &Result) {
+    CheckFactory<Check>::createChecks(Context, Result);
+    CheckFactory<Checks...>::createChecks(Context, Result);
+  }
+};
+
+template <typename Check> struct CheckFactory<Check> {
+  static void
+  createChecks(ClangTidyContext *Context,
+               SmallVectorImpl<std::unique_ptr<ClangTidyCheck>> &Result) {
+    Result.emplace_back(llvm::make_unique<Check>(
+        "test-check-" + std::to_string(Result.size()), Context));
+  }
+};
+
+template <typename... CheckList>
+std::string
+runCheckOnCode(StringRef Code, std::vector<ClangTidyError> *Errors = nullptr,
+               const Twine &Filename = "input.cc",
+               ArrayRef<std::string> ExtraArgs = None,
+               const ClangTidyOptions &ExtraOptions = ClangTidyOptions(),
+               std::map<StringRef, StringRef> PathsToContent =
+                   std::map<StringRef, StringRef>()) {
+  ClangTidyOptions Options = ExtraOptions;
+  Options.Checks = "*";
+  ClangTidyContext Context(llvm::make_unique<DefaultOptionsProvider>(
+      ClangTidyGlobalOptions(), Options));
+  ClangTidyDiagnosticConsumer DiagConsumer(Context);
+
+  std::vector<std::string> ArgCXX11(1, "clang-tidy");
+  ArgCXX11.push_back("-fsyntax-only");
+  ArgCXX11.push_back("-std=c++11");
+  ArgCXX11.push_back("-Iinclude");
+  ArgCXX11.insert(ArgCXX11.end(), ExtraArgs.begin(), ExtraArgs.end());
+  ArgCXX11.push_back(Filename.str());
+
+  ast_matchers::MatchFinder Finder;
+  llvm::IntrusiveRefCntPtr<vfs::InMemoryFileSystem> InMemoryFileSystem(
+      new vfs::InMemoryFileSystem);
+  llvm::IntrusiveRefCntPtr<FileManager> Files(
+      new FileManager(FileSystemOptions(), InMemoryFileSystem));
+
+  SmallVector<std::unique_ptr<ClangTidyCheck>, 1> Checks;
+  CheckFactory<CheckList...>::createChecks(&Context, Checks);
+  tooling::ToolInvocation Invocation(
+      ArgCXX11, new TestClangTidyAction(Checks, Finder, Context), Files.get());
+  InMemoryFileSystem->addFile(Filename, 0,
+                              llvm::MemoryBuffer::getMemBuffer(Code));
+  for (const auto &FileContent : PathsToContent) {
+    InMemoryFileSystem->addFile(
+        Twine("include/") + FileContent.first, 0,
+        llvm::MemoryBuffer::getMemBuffer(FileContent.second));
+  }
+  Invocation.setDiagnosticConsumer(&DiagConsumer);
+  if (!Invocation.run()) {
+    std::string ErrorText;
+    for (const auto &Error : Context.getErrors()) {
+      ErrorText += Error.Message.Message + "\n";
+    }
+    llvm::report_fatal_error(ErrorText);
+  }
+
+  DiagConsumer.finish();
+  tooling::Replacements Fixes;
+  for (const ClangTidyError &Error : Context.getErrors()) {
+    for (const auto &FileAndFixes : Error.Fix) {
+      for (const auto &Fix : FileAndFixes.second) {
+        auto Err = Fixes.add(Fix);
+        // FIXME: better error handling. Keep the behavior for now.
+        if (Err) {
+          llvm::errs() << llvm::toString(std::move(Err)) << "\n";
+          return "";
+        }
+      }
+    }
+  }
+  if (Errors)
+    *Errors = Context.getErrors();
+  auto Result = tooling::applyAllReplacements(Code, Fixes);
+  if (!Result) {
+    // FIXME: propogate the error.
+    llvm::consumeError(Result.takeError());
+    return "";
+  }
+  return *Result;
+}
+
+#define EXPECT_NO_CHANGES(Check, Code)                                         \
+  EXPECT_EQ(Code, runCheckOnCode<Check>(Code))
+
+} // namespace test
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANG_TIDY_CLANGTIDYTEST_H
diff --git a/unittests/clang-tidy/GoogleModuleTest.cpp b/unittests/clang-tidy/GoogleModuleTest.cpp
new file mode 100644 (file)
index 0000000..feb7ae9
--- /dev/null
@@ -0,0 +1,110 @@
+#include "ClangTidyTest.h"
+#include "google/ExplicitConstructorCheck.h"
+#include "google/GlobalNamesInHeadersCheck.h"
+#include "gtest/gtest.h"
+
+using namespace clang::tidy::google;
+
+namespace clang {
+namespace tidy {
+namespace test {
+
+TEST(ExplicitConstructorCheckTest, SingleArgumentConstructorsOnly) {
+  EXPECT_NO_CHANGES(ExplicitConstructorCheck, "class C { C(); };");
+  EXPECT_NO_CHANGES(ExplicitConstructorCheck, "class C { C(int i, int j); };");
+  EXPECT_NO_CHANGES(ExplicitConstructorCheck, "class C { C(const C&); };");
+  EXPECT_NO_CHANGES(ExplicitConstructorCheck, "class C { C(C&&); };");
+  EXPECT_NO_CHANGES(ExplicitConstructorCheck,
+                    "class C { C(const C&) = delete; };");
+  EXPECT_NO_CHANGES(ExplicitConstructorCheck,
+                    "class C { C(int) = delete; };");
+}
+
+TEST(ExplicitConstructorCheckTest, Basic) {
+  EXPECT_EQ("class C { explicit C(int i); };",
+            runCheckOnCode<ExplicitConstructorCheck>("class C { C(int i); };"));
+}
+
+TEST(ExplicitConstructorCheckTest, DefaultParameters) {
+  EXPECT_EQ("class C { explicit C(int i, int j = 0); };",
+            runCheckOnCode<ExplicitConstructorCheck>(
+                "class C { C(int i, int j = 0); };"));
+}
+
+TEST(ExplicitConstructorCheckTest, OutOfLineDefinitions) {
+  EXPECT_EQ("class C { explicit C(int i); }; C::C(int i) {}",
+            runCheckOnCode<ExplicitConstructorCheck>(
+                "class C { C(int i); }; C::C(int i) {}"));
+}
+
+TEST(ExplicitConstructorCheckTest, RemoveExplicit) {
+  EXPECT_EQ("class A { A(const A&); };\n"
+            "class B { /*asdf*/  B(B&&); };\n"
+            "class C { /*asdf*/  C(const C&, int i = 0); };",
+            runCheckOnCode<ExplicitConstructorCheck>(
+                "class A { explicit    A(const A&); };\n"
+                "class B { explicit   /*asdf*/  B(B&&); };\n"
+                "class C { explicit/*asdf*/  C(const C&, int i = 0); };"));
+}
+
+TEST(ExplicitConstructorCheckTest, RemoveExplicitWithMacros) {
+  EXPECT_EQ(
+      "#define A(T) class T##Bar { explicit T##Bar(const T##Bar &b) {} };\n"
+      "A(Foo);",
+      runCheckOnCode<ExplicitConstructorCheck>(
+          "#define A(T) class T##Bar { explicit T##Bar(const T##Bar &b) {} };\n"
+          "A(Foo);"));
+}
+
+class GlobalNamesInHeadersCheckTest : public ::testing::Test {
+protected:
+  bool runCheckOnCode(const std::string &Code, const std::string &Filename) {
+    static const char *const Header = "namespace std {\n"
+                                      "class string {};\n"
+                                      "}  // namespace std\n"
+                                      "\n"
+                                      "#define SOME_MACRO(x) using x\n";
+    std::vector<ClangTidyError> Errors;
+    std::vector<std::string> Args;
+    if (!StringRef(Filename).endswith(".cpp")) {
+      Args.emplace_back("-xc++-header");
+    }
+    test::runCheckOnCode<readability::GlobalNamesInHeadersCheck>(
+        Header + Code, &Errors, Filename, Args);
+    if (Errors.empty())
+      return false;
+    assert(Errors.size() == 1);
+    assert(
+        Errors[0].Message.Message ==
+        "using declarations in the global namespace in headers are prohibited");
+    return true;
+  }
+};
+
+TEST_F(GlobalNamesInHeadersCheckTest, UsingDeclarations) {
+  EXPECT_TRUE(runCheckOnCode("using std::string;", "foo.h"));
+  EXPECT_FALSE(runCheckOnCode("using std::string;", "foo.cpp"));
+  EXPECT_FALSE(runCheckOnCode("namespace my_namespace {\n"
+                              "using std::string;\n"
+                              "}  // my_namespace\n",
+                              "foo.h"));
+  EXPECT_FALSE(runCheckOnCode("SOME_MACRO(std::string);", "foo.h"));
+}
+
+TEST_F(GlobalNamesInHeadersCheckTest, UsingDirectives) {
+  EXPECT_TRUE(runCheckOnCode("using namespace std;", "foo.h"));
+  EXPECT_FALSE(runCheckOnCode("using namespace std;", "foo.cpp"));
+  EXPECT_FALSE(runCheckOnCode("namespace my_namespace {\n"
+                              "using namespace std;\n"
+                              "}  // my_namespace\n",
+                              "foo.h"));
+  EXPECT_FALSE(runCheckOnCode("SOME_MACRO(namespace std);", "foo.h"));
+}
+
+TEST_F(GlobalNamesInHeadersCheckTest, RegressionAnonymousNamespace) {
+  EXPECT_FALSE(runCheckOnCode("namespace {}", "foo.h"));
+}
+
+} // namespace test
+} // namespace tidy
+} // namespace clang
diff --git a/unittests/clang-tidy/IncludeInserterTest.cpp b/unittests/clang-tidy/IncludeInserterTest.cpp
new file mode 100644 (file)
index 0000000..a5f5898
--- /dev/null
@@ -0,0 +1,612 @@
+//===---- IncludeInserterTest.cpp - clang-tidy ----------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "../clang-tidy/utils/IncludeInserter.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "ClangTidyTest.h"
+#include "gtest/gtest.h"
+
+// FIXME: Canonicalize paths correctly on windows.
+// Currently, adding virtual files will canonicalize the paths before
+// storing the virtual entries.
+// When resolving virtual entries in the FileManager, the paths (for
+// example coming from a #include directive) are not canonicalized
+// to native paths; thus, the virtual file is not found.
+// This needs to be fixed in the FileManager before we can make
+// clang-tidy tests work.
+#if !defined(_WIN32)
+
+namespace clang {
+namespace tidy {
+namespace {
+
+class IncludeInserterCheckBase : public ClangTidyCheck {
+public:
+  IncludeInserterCheckBase(StringRef CheckName, ClangTidyContext *Context)
+      : ClangTidyCheck(CheckName, Context) {}
+
+  void registerPPCallbacks(CompilerInstance &Compiler) override {
+    Inserter.reset(new utils::IncludeInserter(
+        Compiler.getSourceManager(),
+        Compiler.getLangOpts(),
+        utils::IncludeSorter::IS_Google));
+    Compiler.getPreprocessor().addPPCallbacks(Inserter->CreatePPCallbacks());
+  }
+
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override {
+    Finder->addMatcher(ast_matchers::declStmt().bind("stmt"), this);
+  }
+
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override {
+    auto Diag = diag(Result.Nodes.getNodeAs<DeclStmt>("stmt")->getLocStart(),
+                     "foo, bar");
+    for (StringRef header : HeadersToInclude()) {
+      auto Fixit = Inserter->CreateIncludeInsertion(
+          Result.SourceManager->getMainFileID(), header, IsAngledInclude());
+      if (Fixit) {
+        Diag << *Fixit;
+      }
+    }
+  }
+
+  virtual std::vector<StringRef> HeadersToInclude() const = 0;
+  virtual bool IsAngledInclude() const = 0;
+
+  std::unique_ptr<utils::IncludeInserter> Inserter;
+};
+
+class NonSystemHeaderInserterCheck : public IncludeInserterCheckBase {
+public:
+  NonSystemHeaderInserterCheck(StringRef CheckName, ClangTidyContext *Context)
+      : IncludeInserterCheckBase(CheckName, Context) {}
+
+  std::vector<StringRef> HeadersToInclude() const override {
+    return {"path/to/header.h"};
+  }
+  bool IsAngledInclude() const override { return false; }
+};
+
+class EarlyInAlphabetHeaderInserterCheck : public IncludeInserterCheckBase {
+public:
+  EarlyInAlphabetHeaderInserterCheck(StringRef CheckName, ClangTidyContext *Context)
+      : IncludeInserterCheckBase(CheckName, Context) {}
+
+  std::vector<StringRef> HeadersToInclude() const override {
+    return {"a/header.h"};
+  }
+  bool IsAngledInclude() const override { return false; }
+};
+
+class MultipleHeaderInserterCheck : public IncludeInserterCheckBase {
+public:
+  MultipleHeaderInserterCheck(StringRef CheckName, ClangTidyContext *Context)
+      : IncludeInserterCheckBase(CheckName, Context) {}
+
+  std::vector<StringRef> HeadersToInclude() const override {
+    return {"path/to/header.h", "path/to/header2.h", "path/to/header.h"};
+  }
+  bool IsAngledInclude() const override { return false; }
+};
+
+class CSystemIncludeInserterCheck : public IncludeInserterCheckBase {
+public:
+  CSystemIncludeInserterCheck(StringRef CheckName, ClangTidyContext *Context)
+      : IncludeInserterCheckBase(CheckName, Context) {}
+
+  std::vector<StringRef> HeadersToInclude() const override {
+    return {"stdlib.h"};
+  }
+  bool IsAngledInclude() const override { return true; }
+};
+
+class CXXSystemIncludeInserterCheck : public IncludeInserterCheckBase {
+public:
+  CXXSystemIncludeInserterCheck(StringRef CheckName, ClangTidyContext *Context)
+      : IncludeInserterCheckBase(CheckName, Context) {}
+
+  std::vector<StringRef> HeadersToInclude() const override { return {"set"}; }
+  bool IsAngledInclude() const override { return true; }
+};
+
+template <typename Check>
+std::string runCheckOnCode(StringRef Code, StringRef Filename) {
+  std::vector<ClangTidyError> Errors;
+  return test::runCheckOnCode<Check>(Code, &Errors, Filename, None,
+                                     ClangTidyOptions(),
+                                     {// Main file include
+                                      {"clang_tidy/tests/"
+                                       "insert_includes_test_header.h",
+                                       "\n"},
+                                      // Non system headers
+                                      {"a/header.h", "\n"},
+                                      {"path/to/a/header.h", "\n"},
+                                      {"path/to/z/header.h", "\n"},
+                                      {"path/to/header.h", "\n"},
+                                      {"path/to/header2.h", "\n"},
+                                      // Fake system headers.
+                                      {"stdlib.h", "\n"},
+                                      {"unistd.h", "\n"},
+                                      {"list", "\n"},
+                                      {"map", "\n"},
+                                      {"set", "\n"},
+                                      {"vector", "\n"}});
+}
+
+TEST(IncludeInserterTest, InsertAfterLastNonSystemInclude) {
+  const char *PreCode = R"(
+#include "clang_tidy/tests/insert_includes_test_header.h"
+
+#include <list>
+#include <map>
+
+#include "path/to/a/header.h"
+
+void foo() {
+  int a = 0;
+})";
+  const char *PostCode = R"(
+#include "clang_tidy/tests/insert_includes_test_header.h"
+
+#include <list>
+#include <map>
+
+#include "path/to/a/header.h"
+#include "path/to/header.h"
+
+void foo() {
+  int a = 0;
+})";
+
+  EXPECT_EQ(PostCode, runCheckOnCode<NonSystemHeaderInserterCheck>(
+                          PreCode, "clang_tidy/tests/"
+                                   "insert_includes_test_input2.cc"));
+}
+
+TEST(IncludeInserterTest, InsertMultipleIncludesAndDeduplicate) {
+  const char *PreCode = R"(
+#include "clang_tidy/tests/insert_includes_test_header.h"
+
+#include <list>
+#include <map>
+
+#include "path/to/a/header.h"
+
+void foo() {
+  int a = 0;
+})";
+  const char *PostCode = R"(
+#include "clang_tidy/tests/insert_includes_test_header.h"
+
+#include <list>
+#include <map>
+
+#include "path/to/a/header.h"
+#include "path/to/header.h"
+#include "path/to/header2.h"
+
+void foo() {
+  int a = 0;
+})";
+
+  EXPECT_EQ(PostCode, runCheckOnCode<MultipleHeaderInserterCheck>(
+                          PreCode, "clang_tidy/tests/"
+                                   "insert_includes_test_input2.cc"));
+}
+
+TEST(IncludeInserterTest, InsertBeforeFirstNonSystemInclude) {
+  const char *PreCode = R"(
+#include "clang_tidy/tests/insert_includes_test_header.h"
+
+#include <list>
+#include <map>
+
+#include "path/to/z/header.h"
+
+void foo() {
+  int a = 0;
+})";
+  const char *PostCode = R"(
+#include "clang_tidy/tests/insert_includes_test_header.h"
+
+#include <list>
+#include <map>
+
+#include "path/to/header.h"
+#include "path/to/z/header.h"
+
+void foo() {
+  int a = 0;
+})";
+
+  EXPECT_EQ(PostCode, runCheckOnCode<NonSystemHeaderInserterCheck>(
+                          PreCode, "clang_tidy/tests/"
+                                   "insert_includes_test_input2.cc"));
+}
+
+TEST(IncludeInserterTest, InsertBetweenNonSystemIncludes) {
+  const char *PreCode = R"(
+#include "clang_tidy/tests/insert_includes_test_header.h"
+
+#include <list>
+#include <map>
+
+#include "path/to/a/header.h"
+#include "path/to/z/header.h"
+
+void foo() {
+  int a = 0;
+})";
+  const char *PostCode = R"(
+#include "clang_tidy/tests/insert_includes_test_header.h"
+
+#include <list>
+#include <map>
+
+#include "path/to/a/header.h"
+#include "path/to/header.h"
+#include "path/to/z/header.h"
+
+void foo() {
+  int a = 0;
+})";
+
+  EXPECT_EQ(PostCode, runCheckOnCode<NonSystemHeaderInserterCheck>(
+                          PreCode, "clang_tidy/tests/"
+                                   "insert_includes_test_input2.cc"));
+}
+
+TEST(IncludeInserterTest, NonSystemIncludeAlreadyIncluded) {
+  const char *PreCode = R"(
+#include "clang_tidy/tests/insert_includes_test_header.h"
+
+#include <list>
+#include <map>
+
+#include "path/to/a/header.h"
+#include "path/to/header.h"
+#include "path/to/z/header.h"
+
+void foo() {
+  int a = 0;
+})";
+  EXPECT_EQ(PreCode, runCheckOnCode<NonSystemHeaderInserterCheck>(
+                         PreCode, "clang_tidy/tests/"
+                                  "insert_includes_test_input2.cc"));
+}
+
+TEST(IncludeInserterTest, InsertNonSystemIncludeAfterLastCXXSystemInclude) {
+  const char *PreCode = R"(
+#include "clang_tidy/tests/insert_includes_test_header.h"
+
+#include <list>
+#include <map>
+
+void foo() {
+  int a = 0;
+})";
+  const char *PostCode = R"(
+#include "clang_tidy/tests/insert_includes_test_header.h"
+
+#include <list>
+#include <map>
+
+#include "path/to/header.h"
+
+void foo() {
+  int a = 0;
+})";
+
+  EXPECT_EQ(PostCode, runCheckOnCode<NonSystemHeaderInserterCheck>(
+                          PreCode, "clang_tidy/tests/"
+                                   "insert_includes_test_header.cc"));
+}
+
+TEST(IncludeInserterTest, InsertNonSystemIncludeAfterMainFileInclude) {
+  const char *PreCode = R"(
+#include "clang_tidy/tests/insert_includes_test_header.h"
+
+void foo() {
+  int a = 0;
+})";
+  const char *PostCode = R"(
+#include "clang_tidy/tests/insert_includes_test_header.h"
+
+#include "path/to/header.h"
+
+void foo() {
+  int a = 0;
+})";
+
+  EXPECT_EQ(PostCode, runCheckOnCode<NonSystemHeaderInserterCheck>(
+                          PreCode, "clang_tidy/tests/"
+                                   "insert_includes_test_header.cc"));
+}
+
+TEST(IncludeInserterTest, InsertCXXSystemIncludeAfterLastCXXSystemInclude) {
+  const char *PreCode = R"(
+#include "clang_tidy/tests/insert_includes_test_header.h"
+
+#include <list>
+#include <map>
+
+#include "path/to/a/header.h"
+
+void foo() {
+  int a = 0;
+})";
+  const char *PostCode = R"(
+#include "clang_tidy/tests/insert_includes_test_header.h"
+
+#include <list>
+#include <map>
+#include <set>
+
+#include "path/to/a/header.h"
+
+void foo() {
+  int a = 0;
+})";
+
+  EXPECT_EQ(PostCode, runCheckOnCode<CXXSystemIncludeInserterCheck>(
+                          PreCode, "clang_tidy/tests/"
+                                   "insert_includes_test_header.cc"));
+}
+
+TEST(IncludeInserterTest, InsertCXXSystemIncludeBeforeFirstCXXSystemInclude) {
+  const char *PreCode = R"(
+#include "clang_tidy/tests/insert_includes_test_header.h"
+
+#include <vector>
+
+#include "path/to/a/header.h"
+
+void foo() {
+  int a = 0;
+})";
+  const char *PostCode = R"(
+#include "clang_tidy/tests/insert_includes_test_header.h"
+
+#include <set>
+#include <vector>
+
+#include "path/to/a/header.h"
+
+void foo() {
+  int a = 0;
+})";
+
+  EXPECT_EQ(PostCode, runCheckOnCode<CXXSystemIncludeInserterCheck>(
+                          PreCode, "clang_tidy/tests/"
+                                   "insert_includes_test_header.cc"));
+}
+
+TEST(IncludeInserterTest, InsertCXXSystemIncludeBetweenCXXSystemIncludes) {
+  const char *PreCode = R"(
+#include "clang_tidy/tests/insert_includes_test_header.h"
+
+#include <map>
+#include <vector>
+
+#include "path/to/a/header.h"
+
+void foo() {
+  int a = 0;
+})";
+  const char *PostCode = R"(
+#include "clang_tidy/tests/insert_includes_test_header.h"
+
+#include <map>
+#include <set>
+#include <vector>
+
+#include "path/to/a/header.h"
+
+void foo() {
+  int a = 0;
+})";
+
+  EXPECT_EQ(PostCode, runCheckOnCode<CXXSystemIncludeInserterCheck>(
+                          PreCode, "clang_tidy/tests/"
+                                   "insert_includes_test_header.cc"));
+}
+
+TEST(IncludeInserterTest, InsertCXXSystemIncludeAfterMainFileInclude) {
+  const char *PreCode = R"(
+#include "clang_tidy/tests/insert_includes_test_header.h"
+
+#include "path/to/a/header.h"
+
+void foo() {
+  int a = 0;
+})";
+  const char *PostCode = R"(
+#include "clang_tidy/tests/insert_includes_test_header.h"
+
+#include <set>
+
+#include "path/to/a/header.h"
+
+void foo() {
+  int a = 0;
+})";
+
+  EXPECT_EQ(PostCode, runCheckOnCode<CXXSystemIncludeInserterCheck>(
+                          PreCode, "clang_tidy/tests/"
+                                   "insert_includes_test_header.cc"));
+}
+
+TEST(IncludeInserterTest, InsertCXXSystemIncludeAfterCSystemInclude) {
+  const char *PreCode = R"(
+#include "clang_tidy/tests/insert_includes_test_header.h"
+
+#include <stdlib.h>
+
+#include "path/to/a/header.h"
+
+void foo() {
+  int a = 0;
+})";
+  const char *PostCode = R"(
+#include "clang_tidy/tests/insert_includes_test_header.h"
+
+#include <stdlib.h>
+
+#include <set>
+
+#include "path/to/a/header.h"
+
+void foo() {
+  int a = 0;
+})";
+
+  EXPECT_EQ(PostCode, runCheckOnCode<CXXSystemIncludeInserterCheck>(
+                          PreCode, "clang_tidy/tests/"
+                                   "insert_includes_test_header.cc"));
+}
+
+TEST(IncludeInserterTest, InsertCXXSystemIncludeBeforeNonSystemInclude) {
+  const char *PreCode = R"(
+#include "path/to/a/header.h"
+
+void foo() {
+  int a = 0;
+})";
+  const char *PostCode = R"(
+#include <set>
+
+#include "path/to/a/header.h"
+
+void foo() {
+  int a = 0;
+})";
+
+  EXPECT_EQ(PostCode, runCheckOnCode<CXXSystemIncludeInserterCheck>(
+                          PreCode, "devtools/cymbal/clang_tidy/tests/"
+                                   "insert_includes_test_header.cc"));
+}
+
+TEST(IncludeInserterTest, InsertCSystemIncludeBeforeCXXSystemInclude) {
+  const char *PreCode = R"(
+#include <set>
+
+#include "path/to/a/header.h"
+
+void foo() {
+  int a = 0;
+})";
+  const char *PostCode = R"(
+#include <stdlib.h>
+
+#include <set>
+
+#include "path/to/a/header.h"
+
+void foo() {
+  int a = 0;
+})";
+
+  EXPECT_EQ(PostCode, runCheckOnCode<CSystemIncludeInserterCheck>(
+                          PreCode, "devtools/cymbal/clang_tidy/tests/"
+                                   "insert_includes_test_header.cc"));
+}
+
+TEST(IncludeInserterTest, InsertIncludeIfThereWasNoneBefore) {
+  const char *PreCode = R"(
+void foo() {
+  int a = 0;
+})";
+  const char *PostCode = R"(#include <set>
+
+
+void foo() {
+  int a = 0;
+})";
+
+  EXPECT_EQ(PostCode, runCheckOnCode<CXXSystemIncludeInserterCheck>(
+                          PreCode, "devtools/cymbal/clang_tidy/tests/"
+                                   "insert_includes_test_header.cc"));
+}
+
+TEST(IncludeInserterTest, DontInsertDuplicateIncludeEvenIfMiscategorized) {
+  const char *PreCode = R"(
+#include "clang_tidy/tests/insert_includes_test_header.h"
+
+#include <map>
+#include <set>
+#include <vector>
+
+#include "a/header.h"
+#include "path/to/a/header.h"
+#include "path/to/header.h"
+
+void foo() {
+  int a = 0;
+})";
+
+  const char *PostCode = R"(
+#include "clang_tidy/tests/insert_includes_test_header.h"
+
+#include <map>
+#include <set>
+#include <vector>
+
+#include "a/header.h"
+#include "path/to/a/header.h"
+#include "path/to/header.h"
+
+void foo() {
+  int a = 0;
+})";
+
+  EXPECT_EQ(PostCode, runCheckOnCode<EarlyInAlphabetHeaderInserterCheck>(
+                          PreCode, "workspace_folder/clang_tidy/tests/"
+                                   "insert_includes_test_header.cc"));
+}
+
+TEST(IncludeInserterTest, HandleOrderInSubdirectory) {
+  const char *PreCode = R"(
+#include "clang_tidy/tests/insert_includes_test_header.h"
+
+#include <map>
+#include <set>
+#include <vector>
+
+#include "path/to/a/header.h"
+#include "path/to/header.h"
+
+void foo() {
+  int a = 0;
+})";
+
+  const char *PostCode = R"(
+#include "clang_tidy/tests/insert_includes_test_header.h"
+
+#include <map>
+#include <set>
+#include <vector>
+
+#include "a/header.h"
+#include "path/to/a/header.h"
+#include "path/to/header.h"
+
+void foo() {
+  int a = 0;
+})";
+
+  EXPECT_EQ(PostCode, runCheckOnCode<EarlyInAlphabetHeaderInserterCheck>(
+                          PreCode, "workspace_folder/clang_tidy/tests/"
+                                   "insert_includes_test_header.cc"));
+}
+
+} // anonymous namespace
+} // namespace tidy
+} // namespace clang
+
+#endif
diff --git a/unittests/clang-tidy/LLVMModuleTest.cpp b/unittests/clang-tidy/LLVMModuleTest.cpp
new file mode 100644 (file)
index 0000000..8ab207d
--- /dev/null
@@ -0,0 +1,219 @@
+#include "ClangTidyTest.h"
+#include "llvm/HeaderGuardCheck.h"
+#include "llvm/IncludeOrderCheck.h"
+#include "gtest/gtest.h"
+
+using namespace clang::tidy::llvm;
+
+namespace clang {
+namespace tidy {
+namespace test {
+
+// FIXME: It seems this might be incompatible to dos path. Investigating.
+#if !defined(_WIN32)
+static std::string runHeaderGuardCheck(StringRef Code, const Twine &Filename,
+                                       Optional<StringRef> ExpectedWarning) {
+  std::vector<ClangTidyError> Errors;
+  std::string Result = test::runCheckOnCode<LLVMHeaderGuardCheck>(
+      Code, &Errors, Filename, std::string("-xc++-header"));
+  if (Errors.size() != (size_t)ExpectedWarning.hasValue())
+    return "invalid error count";
+  if (ExpectedWarning && *ExpectedWarning != Errors.back().Message.Message)
+    return "expected: '" + ExpectedWarning->str() + "', saw: '" +
+           Errors.back().Message.Message + "'";
+  return Result;
+}
+
+namespace {
+struct WithEndifComment : public LLVMHeaderGuardCheck {
+  WithEndifComment(StringRef Name, ClangTidyContext *Context)
+      : LLVMHeaderGuardCheck(Name, Context) {}
+  bool shouldSuggestEndifComment(StringRef Filename) override { return true; }
+};
+} // namespace
+
+static std::string
+runHeaderGuardCheckWithEndif(StringRef Code, const Twine &Filename,
+                             Optional<StringRef> ExpectedWarning) {
+  std::vector<ClangTidyError> Errors;
+  std::string Result = test::runCheckOnCode<WithEndifComment>(
+      Code, &Errors, Filename, std::string("-xc++-header"));
+  if (Errors.size() != (size_t)ExpectedWarning.hasValue())
+    return "invalid error count";
+  if (ExpectedWarning && *ExpectedWarning != Errors.back().Message.Message)
+    return "expected: '" + ExpectedWarning->str() + "', saw: '" +
+           Errors.back().Message.Message + "'";
+  return Result;
+}
+
+TEST(LLVMHeaderGuardCheckTest, FixHeaderGuards) {
+  EXPECT_EQ("#ifndef LLVM_ADT_FOO_H\n"
+            "#define LLVM_ADT_FOO_H\n"
+            "#endif\n",
+            runHeaderGuardCheck(
+                "#ifndef FOO\n"
+                "#define FOO\n"
+                "#endif\n",
+                "include/llvm/ADT/foo.h",
+                StringRef("header guard does not follow preferred style")));
+
+  // Allow trailing underscores.
+  EXPECT_EQ("#ifndef LLVM_ADT_FOO_H_\n"
+            "#define LLVM_ADT_FOO_H_\n"
+            "#endif\n",
+            runHeaderGuardCheck("#ifndef LLVM_ADT_FOO_H_\n"
+                                "#define LLVM_ADT_FOO_H_\n"
+                                "#endif\n",
+                                "include/llvm/ADT/foo.h", None));
+
+  EXPECT_EQ("#ifndef LLVM_CLANG_C_BAR_H\n"
+            "#define LLVM_CLANG_C_BAR_H\n"
+            "\n"
+            "\n"
+            "#endif\n",
+            runHeaderGuardCheck("", "./include/clang-c/bar.h",
+                                StringRef("header is missing header guard")));
+
+  EXPECT_EQ("#ifndef LLVM_CLANG_LIB_CODEGEN_C_H\n"
+            "#define LLVM_CLANG_LIB_CODEGEN_C_H\n"
+            "\n"
+            "\n"
+            "#endif\n",
+            runHeaderGuardCheck("", "tools/clang/lib/CodeGen/c.h",
+                                StringRef("header is missing header guard")));
+
+  EXPECT_EQ("#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_X_H\n"
+            "#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_X_H\n"
+            "\n"
+            "\n"
+            "#endif\n",
+            runHeaderGuardCheck("", "tools/clang/tools/extra/clang-tidy/x.h",
+                                StringRef("header is missing header guard")));
+
+  EXPECT_EQ(
+      "int foo;\n"
+      "#ifndef LLVM_CLANG_BAR_H\n"
+      "#define LLVM_CLANG_BAR_H\n"
+      "#endif\n",
+      runHeaderGuardCheck("int foo;\n"
+                          "#ifndef LLVM_CLANG_BAR_H\n"
+                          "#define LLVM_CLANG_BAR_H\n"
+                          "#endif\n",
+                          "include/clang/bar.h",
+                          StringRef("code/includes outside of area guarded by "
+                                    "header guard; consider moving it")));
+
+  EXPECT_EQ(
+      "#ifndef LLVM_CLANG_BAR_H\n"
+      "#define LLVM_CLANG_BAR_H\n"
+      "#endif\n"
+      "int foo;\n",
+      runHeaderGuardCheck("#ifndef LLVM_CLANG_BAR_H\n"
+                          "#define LLVM_CLANG_BAR_H\n"
+                          "#endif\n"
+                          "int foo;\n",
+                          "include/clang/bar.h",
+                          StringRef("code/includes outside of area guarded by "
+                                    "header guard; consider moving it")));
+
+  EXPECT_EQ("#ifndef LLVM_CLANG_BAR_H\n"
+            "#define LLVM_CLANG_BAR_H\n"
+            "\n"
+            "int foo;\n"
+            "#ifndef FOOLOLO\n"
+            "#define FOOLOLO\n"
+            "#endif\n"
+            "\n"
+            "#endif\n",
+            runHeaderGuardCheck("int foo;\n"
+                                "#ifndef FOOLOLO\n"
+                                "#define FOOLOLO\n"
+                                "#endif\n",
+                                "include/clang/bar.h",
+                                StringRef("header is missing header guard")));
+
+  // Fix incorrect #endif comments even if we shouldn't add new ones.
+  EXPECT_EQ("#ifndef LLVM_ADT_FOO_H\n"
+            "#define LLVM_ADT_FOO_H\n"
+            "#endif // LLVM_ADT_FOO_H\n",
+            runHeaderGuardCheck(
+                "#ifndef FOO\n"
+                "#define FOO\n"
+                "#endif // FOO\n",
+                "include/llvm/ADT/foo.h",
+                StringRef("header guard does not follow preferred style")));
+
+  EXPECT_EQ("#ifndef LLVM_ADT_FOO_H\n"
+            "#define LLVM_ADT_FOO_H\n"
+            "#endif // LLVM_ADT_FOO_H\n",
+            runHeaderGuardCheckWithEndif(
+                "#ifndef FOO\n"
+                "#define FOO\n"
+                "#endif\n",
+                "include/llvm/ADT/foo.h",
+                StringRef("header guard does not follow preferred style")));
+
+  EXPECT_EQ("#ifndef LLVM_ADT_FOO_H\n"
+            "#define LLVM_ADT_FOO_H\n"
+            "#endif // LLVM_ADT_FOO_H\n",
+            runHeaderGuardCheckWithEndif(
+                "#ifndef LLVM_ADT_FOO_H\n"
+                "#define LLVM_ADT_FOO_H\n"
+                "#endif // LLVM_H\n",
+                "include/llvm/ADT/foo.h",
+                StringRef("#endif for a header guard should reference the "
+                          "guard macro in a comment")));
+
+  EXPECT_EQ("#ifndef LLVM_ADT_FOO_H\n"
+            "#define LLVM_ADT_FOO_H\n"
+            "#endif /* LLVM_ADT_FOO_H */\n",
+            runHeaderGuardCheckWithEndif("#ifndef LLVM_ADT_FOO_H\n"
+                                         "#define LLVM_ADT_FOO_H\n"
+                                         "#endif /* LLVM_ADT_FOO_H */\n",
+                                         "include/llvm/ADT/foo.h", None));
+
+  EXPECT_EQ("#ifndef LLVM_ADT_FOO_H_\n"
+            "#define LLVM_ADT_FOO_H_\n"
+            "#endif // LLVM_ADT_FOO_H_\n",
+            runHeaderGuardCheckWithEndif("#ifndef LLVM_ADT_FOO_H_\n"
+                                         "#define LLVM_ADT_FOO_H_\n"
+                                         "#endif // LLVM_ADT_FOO_H_\n",
+                                         "include/llvm/ADT/foo.h", None));
+
+  EXPECT_EQ("#ifndef LLVM_ADT_FOO_H\n"
+            "#define LLVM_ADT_FOO_H\n"
+            "#endif // LLVM_ADT_FOO_H\n",
+            runHeaderGuardCheckWithEndif(
+                "#ifndef LLVM_ADT_FOO_H_\n"
+                "#define LLVM_ADT_FOO_H_\n"
+                "#endif // LLVM\n",
+                "include/llvm/ADT/foo.h",
+                StringRef("header guard does not follow preferred style")));
+
+  EXPECT_EQ("#ifndef LLVM_ADT_FOO_H\n"
+            "#define LLVM_ADT_FOO_H\n"
+            "#endif \\ \n"
+            "// LLVM_ADT_FOO_H\n",
+            runHeaderGuardCheckWithEndif(
+                "#ifndef LLVM_ADT_FOO_H\n"
+                "#define LLVM_ADT_FOO_H\n"
+                "#endif \\ \n"
+                "// LLVM_ADT_FOO_H\n",
+                "include/llvm/ADT/foo.h",
+                StringRef("backslash and newline separated by space")));
+
+  EXPECT_EQ("#ifndef LLVM_ADT_FOO_H\n"
+            "#define LLVM_ADT_FOO_H\n"
+            "#endif  /* LLVM_ADT_FOO_H\\ \n"
+            " FOO */",
+            runHeaderGuardCheckWithEndif("#ifndef LLVM_ADT_FOO_H\n"
+                                         "#define LLVM_ADT_FOO_H\n"
+                                         "#endif  /* LLVM_ADT_FOO_H\\ \n"
+                                         " FOO */",
+                                         "include/llvm/ADT/foo.h", None));
+}
+#endif
+
+} // namespace test
+} // namespace tidy
+} // namespace clang
diff --git a/unittests/clang-tidy/MiscModuleTest.cpp b/unittests/clang-tidy/MiscModuleTest.cpp
new file mode 100644 (file)
index 0000000..a3f00b8
--- /dev/null
@@ -0,0 +1,39 @@
+#include "ClangTidyTest.h"
+#include "misc/ArgumentCommentCheck.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace tidy {
+namespace test {
+
+using misc::ArgumentCommentCheck;
+
+TEST(ArgumentCommentCheckTest, CorrectComments) {
+  EXPECT_NO_CHANGES(ArgumentCommentCheck,
+                    "void f(int x, int y); void g() { f(/*x=*/0, /*y=*/0); }");
+  EXPECT_NO_CHANGES(ArgumentCommentCheck,
+                    "struct C { C(int x, int y); }; C c(/*x=*/0, /*y=*/0);");
+}
+
+TEST(ArgumentCommentCheckTest, ThisEditDistanceAboveThreshold) {
+  EXPECT_NO_CHANGES(ArgumentCommentCheck,
+                    "void f(int xxx); void g() { f(/*xyz=*/0); }");
+}
+
+TEST(ArgumentCommentCheckTest, OtherEditDistanceAboveThreshold) {
+  EXPECT_EQ("void f(int xxx, int yyy); void g() { f(/*xxx=*/0, 0); }",
+            runCheckOnCode<ArgumentCommentCheck>(
+                "void f(int xxx, int yyy); void g() { f(/*Zxx=*/0, 0); }"));
+  EXPECT_EQ("struct C { C(int xxx, int yyy); }; C c(/*xxx=*/0, 0);",
+            runCheckOnCode<ArgumentCommentCheck>(
+                "struct C { C(int xxx, int yyy); }; C c(/*Zxx=*/0, 0);"));
+}
+
+TEST(ArgumentCommentCheckTest, OtherEditDistanceBelowThreshold) {
+  EXPECT_NO_CHANGES(ArgumentCommentCheck,
+                    "void f(int xxx, int yyy); void g() { f(/*xxy=*/0, 0); }");
+}
+
+} // namespace test
+} // namespace tidy
+} // namespace clang
diff --git a/unittests/clang-tidy/NamespaceAliaserTest.cpp b/unittests/clang-tidy/NamespaceAliaserTest.cpp
new file mode 100644 (file)
index 0000000..71286e2
--- /dev/null
@@ -0,0 +1,123 @@
+//===---- NamespaceAliaserTest.cpp - clang-tidy
+//----------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "../clang-tidy/utils/NamespaceAliaser.h"
+
+#include "ClangTidyTest.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace tidy {
+namespace utils {
+// This checker is for testing only. It can only run on one test case
+// (e.g. with one SourceManager).
+class InsertAliasCheck : public ClangTidyCheck {
+public:
+  InsertAliasCheck(StringRef Name, ClangTidyContext *Context)
+      :ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override {
+    Finder->addMatcher(ast_matchers::callExpr().bind("foo"), this);
+  }
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override {
+    if (!Aliaser)
+      Aliaser.reset(new NamespaceAliaser(*Result.SourceManager));
+
+    const auto *Call = Result.Nodes.getNodeAs<CallExpr>("foo");
+    assert(Call != nullptr && "Did not find node \"foo\"");
+    auto Hint = Aliaser->createAlias(*Result.Context, *Call, "::foo::bar",
+                                     {"b", "some_alias"});
+    if (Hint.hasValue())
+      diag(Call->getLocStart(), "Fix for testing") << Hint.getValue();
+
+    diag(Call->getLocStart(), "insert call")
+        << FixItHint::CreateInsertion(
+               Call->getLocStart(),
+               Aliaser->getNamespaceName(*Result.Context, *Call, "::foo::bar") +
+                   "::");
+  }
+
+private:
+  std::unique_ptr<NamespaceAliaser> Aliaser;
+};
+
+template <typename Check>
+std::string runChecker(StringRef Code, unsigned ExpectedWarningCount) {
+  std::map<StringRef, StringRef> AdditionalFileContents = {{"foo.h",
+                                                            "namespace foo {\n"
+                                                            "namespace bar {\n"
+                                                            "}\n"
+                                                            "void func() { }\n"
+                                                            "}"}};
+  std::vector<ClangTidyError> errors;
+
+  std::string result =
+      test::runCheckOnCode<Check>(Code, &errors, "foo.cc", None,
+                                  ClangTidyOptions(), AdditionalFileContents);
+
+  EXPECT_EQ(ExpectedWarningCount, errors.size());
+  return result;
+}
+
+TEST(NamespaceAliaserTest, AddNewAlias) {
+  EXPECT_EQ("#include \"foo.h\"\n"
+            "void f() {\n"
+            "namespace b = ::foo::bar;"
+            " b::f(); }",
+            runChecker<InsertAliasCheck>("#include \"foo.h\"\n"
+                                         "void f() { f(); }",
+                                         2));
+}
+
+TEST(NamespaceAliaserTest, ReuseAlias) {
+  EXPECT_EQ(
+      "#include \"foo.h\"\n"
+      "void f() { namespace x = foo::bar; x::f(); }",
+      runChecker<InsertAliasCheck>("#include \"foo.h\"\n"
+                                   "void f() { namespace x = foo::bar; f(); }",
+                                   1));
+}
+
+TEST(NamespaceAliaserTest, AddsOnlyOneAlias) {
+  EXPECT_EQ("#include \"foo.h\"\n"
+            "void f() {\n"
+            "namespace b = ::foo::bar;"
+            " b::f(); b::f(); }",
+            runChecker<InsertAliasCheck>("#include \"foo.h\"\n"
+                                         "void f() { f(); f(); }",
+                                         3));
+}
+
+TEST(NamespaceAliaserTest, LocalConflict) {
+  EXPECT_EQ("#include \"foo.h\"\n"
+            "void f() {\n"
+            "namespace some_alias = ::foo::bar;"
+            " namespace b = foo; some_alias::f(); }",
+            runChecker<InsertAliasCheck>("#include \"foo.h\"\n"
+                                         "void f() { namespace b = foo; f(); }",
+                                         2));
+}
+
+TEST(NamespaceAliaserTest, GlobalConflict) {
+  EXPECT_EQ("#include \"foo.h\"\n"
+            "namespace b = foo;\n"
+            "void f() {\n"
+            "namespace some_alias = ::foo::bar;"
+            " some_alias::f(); }",
+            runChecker<InsertAliasCheck>("#include \"foo.h\"\n"
+                                         "namespace b = foo;\n"
+                                         "void f() { f(); }",
+                                         2));
+}
+
+} // namespace utils
+} // namespace tidy
+} // namespace clang
diff --git a/unittests/clang-tidy/OverlappingReplacementsTest.cpp b/unittests/clang-tidy/OverlappingReplacementsTest.cpp
new file mode 100644 (file)
index 0000000..b7c4805
--- /dev/null
@@ -0,0 +1,409 @@
+//===---- OverlappingReplacementsTest.cpp - clang-tidy --------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ClangTidyTest.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace tidy {
+namespace test {
+namespace {
+
+const char BoundDecl[] = "decl";
+const char BoundIf[] = "if";
+
+// We define a reduced set of very small checks that allow to test different
+// overlapping situations (no overlapping, replacements partially overlap, etc),
+// as well as different kinds of diagnostics (one check produces several errors,
+// several replacement ranges in an error, etc).
+class UseCharCheck : public ClangTidyCheck {
+public:
+  UseCharCheck(StringRef CheckName, ClangTidyContext *Context)
+      : ClangTidyCheck(CheckName, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override {
+    using namespace ast_matchers;
+    Finder->addMatcher(varDecl(hasType(isInteger())).bind(BoundDecl), this);
+  }
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override {
+    auto *VD = Result.Nodes.getNodeAs<VarDecl>(BoundDecl);
+    diag(VD->getLocStart(), "use char") << FixItHint::CreateReplacement(
+        CharSourceRange::getTokenRange(VD->getLocStart(), VD->getLocStart()),
+        "char");
+  }
+};
+
+class IfFalseCheck : public ClangTidyCheck {
+public:
+  IfFalseCheck(StringRef CheckName, ClangTidyContext *Context)
+      : ClangTidyCheck(CheckName, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override {
+    using namespace ast_matchers;
+    Finder->addMatcher(ifStmt().bind(BoundIf), this);
+  }
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override {
+    auto *If = Result.Nodes.getNodeAs<IfStmt>(BoundIf);
+    auto *Cond = If->getCond();
+    SourceRange Range = Cond->getSourceRange();
+    if (auto *D = If->getConditionVariable()) {
+      Range = SourceRange(D->getLocStart(), D->getLocEnd());
+    }
+    diag(Range.getBegin(), "the cake is a lie") << FixItHint::CreateReplacement(
+        CharSourceRange::getTokenRange(Range), "false");
+  }
+};
+
+class RefactorCheck : public ClangTidyCheck {
+public:
+  RefactorCheck(StringRef CheckName, ClangTidyContext *Context)
+      : ClangTidyCheck(CheckName, Context), NamePattern("::$") {}
+  RefactorCheck(StringRef CheckName, ClangTidyContext *Context,
+                StringRef NamePattern)
+      : ClangTidyCheck(CheckName, Context), NamePattern(NamePattern) {}
+  virtual std::string newName(StringRef OldName) = 0;
+
+  void registerMatchers(ast_matchers::MatchFinder *Finder) final {
+    using namespace ast_matchers;
+    Finder->addMatcher(varDecl(matchesName(NamePattern)).bind(BoundDecl), this);
+  }
+
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) final {
+    auto *VD = Result.Nodes.getNodeAs<VarDecl>(BoundDecl);
+    std::string NewName = newName(VD->getName());
+
+    auto Diag = diag(VD->getLocation(), "refactor %0 into %1")
+                << VD->getName() << NewName
+                << FixItHint::CreateReplacement(
+                       CharSourceRange::getTokenRange(VD->getLocation(),
+                                                      VD->getLocation()),
+                       NewName);
+
+    class UsageVisitor : public RecursiveASTVisitor<UsageVisitor> {
+    public:
+      UsageVisitor(const ValueDecl *VD, StringRef NewName,
+                   DiagnosticBuilder &Diag)
+          : VD(VD), NewName(NewName), Diag(Diag) {}
+      bool VisitDeclRefExpr(DeclRefExpr *E) {
+        if (const ValueDecl *D = E->getDecl()) {
+          if (VD->getCanonicalDecl() == D->getCanonicalDecl()) {
+            Diag << FixItHint::CreateReplacement(
+                CharSourceRange::getTokenRange(E->getSourceRange()), NewName);
+          }
+        }
+        return RecursiveASTVisitor<UsageVisitor>::VisitDeclRefExpr(E);
+      }
+
+    private:
+      const ValueDecl *VD;
+      StringRef NewName;
+      DiagnosticBuilder &Diag;
+    };
+
+    UsageVisitor(VD, NewName, Diag)
+        .TraverseDecl(Result.Context->getTranslationUnitDecl());
+  }
+
+protected:
+  const std::string NamePattern;
+};
+
+class StartsWithPotaCheck : public RefactorCheck {
+public:
+  StartsWithPotaCheck(StringRef CheckName, ClangTidyContext *Context)
+      : RefactorCheck(CheckName, Context, "::pota") {}
+
+  std::string newName(StringRef OldName) override {
+    return "toma" + OldName.substr(4).str();
+  }
+};
+
+class EndsWithTatoCheck : public RefactorCheck {
+public:
+  EndsWithTatoCheck(StringRef CheckName, ClangTidyContext *Context)
+      : RefactorCheck(CheckName, Context, "tato$") {}
+
+  std::string newName(StringRef OldName) override {
+    return OldName.substr(0, OldName.size() - 4).str() + "melo";
+  }
+};
+
+} // namespace
+
+TEST(OverlappingReplacementsTest, UseCharCheckTest) {
+  const char Code[] =
+      R"(void f() {
+  int a = 0;
+  if (int b = 0) {
+    int c = a;
+  }
+})";
+
+  const char CharFix[] =
+      R"(void f() {
+  char a = 0;
+  if (char b = 0) {
+    char c = a;
+  }
+})";
+  EXPECT_EQ(CharFix, runCheckOnCode<UseCharCheck>(Code));
+}
+
+TEST(OverlappingReplacementsTest, IfFalseCheckTest) {
+  const char Code[] =
+      R"(void f() {
+  int potato = 0;
+  if (int b = 0) {
+    int c = potato;
+  } else if (true) {
+    int d = 0;
+  }
+})";
+
+  const char IfFix[] =
+      R"(void f() {
+  int potato = 0;
+  if (false) {
+    int c = potato;
+  } else if (false) {
+    int d = 0;
+  }
+})";
+  EXPECT_EQ(IfFix, runCheckOnCode<IfFalseCheck>(Code));
+}
+
+TEST(OverlappingReplacementsTest, StartsWithCheckTest) {
+  const char Code[] =
+      R"(void f() {
+  int a = 0;
+  int potato = 0;
+  if (int b = 0) {
+    int c = potato;
+  } else if (true) {
+    int d = 0;
+  }
+})";
+
+  const char StartsFix[] =
+      R"(void f() {
+  int a = 0;
+  int tomato = 0;
+  if (int b = 0) {
+    int c = tomato;
+  } else if (true) {
+    int d = 0;
+  }
+})";
+  EXPECT_EQ(StartsFix, runCheckOnCode<StartsWithPotaCheck>(Code));
+}
+
+TEST(OverlappingReplacementsTest, EndsWithCheckTest) {
+  const char Code[] =
+      R"(void f() {
+  int a = 0;
+  int potato = 0;
+  if (int b = 0) {
+    int c = potato;
+  } else if (true) {
+    int d = 0;
+  }
+})";
+
+  const char EndsFix[] =
+      R"(void f() {
+  int a = 0;
+  int pomelo = 0;
+  if (int b = 0) {
+    int c = pomelo;
+  } else if (true) {
+    int d = 0;
+  }
+})";
+  EXPECT_EQ(EndsFix, runCheckOnCode<EndsWithTatoCheck>(Code));
+}
+
+TEST(OverlappingReplacementTest, ReplacementsDoNotOverlap) {
+  std::string Res;
+  const char Code[] =
+      R"(void f() {
+  int potassium = 0;
+  if (true) {
+    int Potato = potassium;
+  }
+})";
+
+  const char CharIfFix[] =
+      R"(void f() {
+  char potassium = 0;
+  if (false) {
+    char Potato = potassium;
+  }
+})";
+  Res = runCheckOnCode<UseCharCheck, IfFalseCheck>(Code);
+  EXPECT_EQ(CharIfFix, Res);
+
+  const char StartsEndsFix[] =
+      R"(void f() {
+  int tomassium = 0;
+  if (true) {
+    int Pomelo = tomassium;
+  }
+})";
+  Res = runCheckOnCode<StartsWithPotaCheck, EndsWithTatoCheck>(Code);
+  EXPECT_EQ(StartsEndsFix, Res);
+
+  const char CharIfStartsEndsFix[] =
+      R"(void f() {
+  char tomassium = 0;
+  if (false) {
+    char Pomelo = tomassium;
+  }
+})";
+  Res = runCheckOnCode<UseCharCheck, IfFalseCheck, StartsWithPotaCheck,
+                       EndsWithTatoCheck>(Code);
+  EXPECT_EQ(CharIfStartsEndsFix, Res);
+}
+
+TEST(OverlappingReplacementsTest, ReplacementInsideOtherReplacement) {
+  std::string Res;
+  const char Code[] =
+      R"(void f() {
+  if (char potato = 0) {
+  } else if (int a = 0) {
+    char potato = 0;
+    if (potato) potato;
+  }
+})";
+
+  // Apply the UseCharCheck together with the IfFalseCheck.
+  //
+  // The 'If' fix contains the other, so that is the one that has to be applied.
+  // } else if (int a = 0) {
+  //            ^^^ -> char
+  //            ~~~~~~~~~ -> false
+  const char CharIfFix[] =
+      R"(void f() {
+  if (false) {
+  } else if (false) {
+    char potato = 0;
+    if (false) potato;
+  }
+})";
+  Res = runCheckOnCode<UseCharCheck, IfFalseCheck>(Code);
+  EXPECT_EQ(CharIfFix, Res);
+  Res = runCheckOnCode<IfFalseCheck, UseCharCheck>(Code);
+  EXPECT_EQ(CharIfFix, Res);
+
+  // Apply the IfFalseCheck with the StartsWithPotaCheck.
+  //
+  // The 'If' replacement is bigger here.
+  // if (char potato = 0) {
+  //          ^^^^^^ -> tomato
+  //     ~~~~~~~~~~~~~~~ -> false
+  //
+  // But the refactoring is the one that contains the other here:
+  // char potato = 0;
+  //      ^^^^^^ -> tomato
+  // if (potato) potato;
+  //     ^^^^^^  ^^^^^^ -> tomato, tomato
+  //     ~~~~~~ -> false
+  const char IfStartsFix[] =
+      R"(void f() {
+  if (false) {
+  } else if (false) {
+    char tomato = 0;
+    if (tomato) tomato;
+  }
+})";
+  Res = runCheckOnCode<IfFalseCheck, StartsWithPotaCheck>(Code);
+  EXPECT_EQ(IfStartsFix, Res);
+  Res = runCheckOnCode<StartsWithPotaCheck, IfFalseCheck>(Code);
+  EXPECT_EQ(IfStartsFix, Res);
+}
+
+TEST(OverlappingReplacements, TwoReplacementsInsideOne) {
+  std::string Res;
+  const char Code[] =
+      R"(void f() {
+  if (int potato = 0) {
+    int a = 0;
+  }
+})";
+
+  // The two smallest replacements should not be applied.
+  // if (int potato = 0) {
+  //         ^^^^^^ -> tomato
+  //     *** -> char
+  //     ~~~~~~~~~~~~~~ -> false
+  // But other errors from the same checks should not be affected.
+  //   int a = 0;
+  //   *** -> char
+  const char Fix[] =
+      R"(void f() {
+  if (false) {
+    char a = 0;
+  }
+})";
+  Res = runCheckOnCode<UseCharCheck, IfFalseCheck, StartsWithPotaCheck>(Code);
+  EXPECT_EQ(Fix, Res);
+  Res = runCheckOnCode<StartsWithPotaCheck, IfFalseCheck, UseCharCheck>(Code);
+  EXPECT_EQ(Fix, Res);
+}
+
+TEST(OverlappingReplacementsTest,
+     ApplyAtMostOneOfTheChangesWhenPartialOverlapping) {
+  std::string Res;
+  const char Code[] =
+      R"(void f() {
+  if (int potato = 0) {
+    int a = potato;
+  }
+})";
+
+  // These two replacements overlap, but none of them is completely contained
+  // inside the other.
+  // if (int potato = 0) {
+  //         ^^^^^^ -> tomato
+  //     ~~~~~~~~~~~~~~ -> false
+  //   int a = potato;
+  //           ^^^^^^ -> tomato
+  //
+  // The 'StartsWithPotaCheck' fix has endpoints inside the 'IfFalseCheck' fix,
+  // so it is going to be set as inapplicable. The 'if' fix will be applied.
+  const char IfFix[] =
+      R"(void f() {
+  if (false) {
+    int a = potato;
+  }
+})";
+  Res = runCheckOnCode<IfFalseCheck, StartsWithPotaCheck>(Code);
+  EXPECT_EQ(IfFix, Res);
+}
+
+TEST(OverlappingReplacementsTest, TwoErrorsHavePerfectOverlapping) {
+  std::string Res;
+  const char Code[] =
+      R"(void f() {
+  int potato = 0;
+  potato += potato * potato;
+  if (char a = potato) potato;
+})";
+
+  // StartsWithPotaCheck will try to refactor 'potato' into 'tomato', and
+  // EndsWithTatoCheck will try to use 'pomelo'. Both fixes have the same set of
+  // ranges. This is a corner case of one error completely containing another:
+  // the other completely contains the first one as well. Both errors are
+  // discarded.
+
+  Res = runCheckOnCode<StartsWithPotaCheck, EndsWithTatoCheck>(Code);
+  EXPECT_EQ(Code, Res);
+}
+
+} // namespace test
+} // namespace tidy
+} // namespace clang
diff --git a/unittests/clang-tidy/ReadabilityModuleTest.cpp b/unittests/clang-tidy/ReadabilityModuleTest.cpp
new file mode 100644 (file)
index 0000000..e79d024
--- /dev/null
@@ -0,0 +1,499 @@
+#include "ClangTidyTest.h"
+#include "readability/BracesAroundStatementsCheck.h"
+#include "readability/NamespaceCommentCheck.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace tidy {
+namespace test {
+
+using readability::BracesAroundStatementsCheck;
+using readability::NamespaceCommentCheck;
+
+TEST(NamespaceCommentCheckTest, Basic) {
+  EXPECT_EQ("namespace i {\n} // namespace i",
+            runCheckOnCode<NamespaceCommentCheck>("namespace i {\n}"));
+  EXPECT_EQ("namespace {\n} // namespace",
+            runCheckOnCode<NamespaceCommentCheck>("namespace {\n}"));
+  EXPECT_EQ("namespace i { namespace j {\n} // namespace j\n } // namespace i",
+            runCheckOnCode<NamespaceCommentCheck>(
+                "namespace i { namespace j {\n} }"));
+}
+
+TEST(NamespaceCommentCheckTest, SingleLineNamespaces) {
+  EXPECT_EQ(
+      "namespace i { namespace j { } }",
+      runCheckOnCode<NamespaceCommentCheck>("namespace i { namespace j { } }"));
+}
+
+TEST(NamespaceCommentCheckTest, CheckExistingComments) {
+  EXPECT_EQ("namespace i { namespace j {\n"
+            "} /* namespace j */ } // namespace i\n"
+            " /* random comment */",
+            runCheckOnCode<NamespaceCommentCheck>(
+                "namespace i { namespace j {\n"
+                "} /* namespace j */ } /* random comment */"));
+  EXPECT_EQ("namespace {\n"
+            "} // namespace",
+            runCheckOnCode<NamespaceCommentCheck>("namespace {\n"
+                                                  "} // namespace"));
+  EXPECT_EQ("namespace {\n"
+            "} //namespace",
+            runCheckOnCode<NamespaceCommentCheck>("namespace {\n"
+                                                  "} //namespace"));
+  EXPECT_EQ("namespace {\n"
+            "} // anonymous namespace",
+            runCheckOnCode<NamespaceCommentCheck>("namespace {\n"
+                                                  "} // anonymous namespace"));
+  EXPECT_EQ("namespace {\n"
+            "} // Anonymous namespace.",
+            runCheckOnCode<NamespaceCommentCheck>("namespace {\n"
+                                                  "} // Anonymous namespace."));
+  EXPECT_EQ(
+      "namespace q {\n"
+      "} // namespace q",
+      runCheckOnCode<NamespaceCommentCheck>("namespace q {\n"
+                                            "} // anonymous namespace q"));
+  EXPECT_EQ(
+      "namespace My_NameSpace123 {\n"
+      "} // namespace My_NameSpace123",
+      runCheckOnCode<NamespaceCommentCheck>("namespace My_NameSpace123 {\n"
+                                            "} // namespace My_NameSpace123"));
+  EXPECT_EQ(
+      "namespace My_NameSpace123 {\n"
+      "} //namespace My_NameSpace123",
+      runCheckOnCode<NamespaceCommentCheck>("namespace My_NameSpace123 {\n"
+                                            "} //namespace My_NameSpace123"));
+  EXPECT_EQ("namespace My_NameSpace123 {\n"
+            "} //  end namespace   My_NameSpace123",
+            runCheckOnCode<NamespaceCommentCheck>(
+                "namespace My_NameSpace123 {\n"
+                "} //  end namespace   My_NameSpace123"));
+  // Understand comments only on the same line.
+  EXPECT_EQ("namespace {\n"
+            "} // namespace\n"
+            "// namespace",
+            runCheckOnCode<NamespaceCommentCheck>("namespace {\n"
+                                                  "}\n"
+                                                  "// namespace"));
+}
+
+TEST(NamespaceCommentCheckTest, FixWrongComments) {
+  EXPECT_EQ("namespace i { namespace jJ0_ {\n"
+            "} // namespace jJ0_\n"
+            " } // namespace i\n"
+            " /* random comment */",
+            runCheckOnCode<NamespaceCommentCheck>(
+                "namespace i { namespace jJ0_ {\n"
+                "} /* namespace qqq */ } /* random comment */"));
+  EXPECT_EQ("namespace {\n"
+            "} // namespace",
+            runCheckOnCode<NamespaceCommentCheck>("namespace {\n"
+                                                  "} // namespace asdf"));
+  // Remove unknown line comments. These are likely to be an unrecognized form
+  // of a namespace ending comment.
+  EXPECT_EQ("namespace {\n"
+            "} // namespace",
+            runCheckOnCode<NamespaceCommentCheck>("namespace {\n"
+                                                  "} // random text"));
+}
+
+TEST(BracesAroundStatementsCheckTest, IfWithComments) {
+  EXPECT_EQ("int main() {\n"
+            "  if (false /*dummy token*/) {\n"
+            "    // comment\n"
+            "    return -1; /**/\n"
+            "}\n"
+            "  if (false) {\n"
+            "    return -1; // comment\n"
+            "}\n"
+            "  if (false) {\n"
+            "    return -1; \n"
+            "}/* multi-line \n comment */\n"
+            "}",
+            runCheckOnCode<BracesAroundStatementsCheck>(
+                "int main() {\n"
+                "  if (false /*dummy token*/)\n"
+                "    // comment\n"
+                "    return -1; /**/\n"
+                "  if (false)\n"
+                "    return -1; // comment\n"
+                "  if (false)\n"
+                "    return -1; /* multi-line \n comment */\n"
+                "}"));
+  EXPECT_EQ("int main() {\n"
+            "  if (false /*dummy token*/) {\n"
+            "    // comment\n"
+            "    return -1 /**/ ;\n"
+            "}\n"
+            "}",
+            runCheckOnCode<BracesAroundStatementsCheck>(
+                "int main() {\n"
+                "  if (false /*dummy token*/)\n"
+                "    // comment\n"
+                "    return -1 /**/ ;\n"
+                "}"));
+}
+
+TEST(BracesAroundStatementsCheckTest, If) {
+  EXPECT_NO_CHANGES(BracesAroundStatementsCheck, "int main() {\n"
+                                                 "  if (false) {\n"
+                                                 "    return -1;\n"
+                                                 "  }\n"
+                                                 "}");
+  EXPECT_NO_CHANGES(BracesAroundStatementsCheck, "int main() {\n"
+                                                 "  if (auto Cond = false) {\n"
+                                                 "    return -1;\n"
+                                                 "  }\n"
+                                                 "}");
+  EXPECT_NO_CHANGES(BracesAroundStatementsCheck, "int main() {\n"
+                                                 "  if (false) {\n"
+                                                 "    return -1;\n"
+                                                 "  } else {\n"
+                                                 "    return -2;\n"
+                                                 "  }\n"
+                                                 "}");
+  EXPECT_EQ("int main() {\n"
+            "  if (false) {\n"
+            "    return -1;\n"
+            "}\n"
+            "}",
+            runCheckOnCode<BracesAroundStatementsCheck>("int main() {\n"
+                                                        "  if (false)\n"
+                                                        "    return -1;\n"
+                                                        "}"));
+  EXPECT_EQ("int main() {\n"
+            "  if (auto Cond = false /**/ ) {\n"
+            "    return -1;\n"
+            "}\n"
+            "}",
+            runCheckOnCode<BracesAroundStatementsCheck>(
+                "int main() {\n"
+                "  if (auto Cond = false /**/ )\n"
+                "    return -1;\n"
+                "}"));
+  // FIXME: Consider adding braces before EMPTY_MACRO and after the statement.
+  EXPECT_NO_CHANGES(BracesAroundStatementsCheck,
+                    "#define EMPTY_MACRO\n"
+                    "int main() {\n"
+                    "  if (auto Cond = false EMPTY_MACRO /**/ ) EMPTY_MACRO\n"
+                    "    return -1;\n"
+                    "}");
+  EXPECT_EQ("int main() {\n"
+            "  if (true) { return -1/**/ ;\n"
+            "}\n"
+            "}",
+            runCheckOnCode<BracesAroundStatementsCheck>(
+                "int main() {\n"
+                "  if (true) return -1/**/ ;\n"
+                "}"));
+  EXPECT_EQ("int main() {\n"
+            "  if (false) {\n"
+            "    return -1;\n"
+            "  } else {\n"
+            "    return -2;\n"
+            "}\n"
+            "}",
+            runCheckOnCode<BracesAroundStatementsCheck>("int main() {\n"
+                                                        "  if (false)\n"
+                                                        "    return -1;\n"
+                                                        "  else\n"
+                                                        "    return -2;\n"
+                                                        "}"));
+  EXPECT_EQ("int main() {\n"
+            "  if (false) {\n"
+            "    return -1;\n"
+            "  } else if (1 == 2) {\n"
+            "    return -2;\n"
+            "  } else {\n"
+            "    return -3;\n"
+            "}\n"
+            "}",
+            runCheckOnCode<BracesAroundStatementsCheck>("int main() {\n"
+                                                        "  if (false)\n"
+                                                        "    return -1;\n"
+                                                        "  else if (1 == 2)\n"
+                                                        "    return -2;\n"
+                                                        "  else\n"
+                                                        "    return -3;\n"
+                                                        "}"));
+  EXPECT_EQ("int main() {\n"
+            "  if (false) {\n"
+            "    return -1;\n"
+            "  } else if (1 == 2) {\n"
+            "    return -2;\n"
+            "  } else {\n"
+            "    return -3;\n"
+            "}\n"
+            "}",
+            runCheckOnCode<BracesAroundStatementsCheck>("int main() {\n"
+                                                        "  if (false)\n"
+                                                        "    return -1;\n"
+                                                        "  else if (1 == 2) {\n"
+                                                        "    return -2;\n"
+                                                        "  } else\n"
+                                                        "    return -3;\n"
+                                                        "}"));
+}
+
+TEST(BracesAroundStatementsCheckTest, IfElseWithShortStatements) {
+  ClangTidyOptions Options;
+  Options.CheckOptions["test-check-0.ShortStatementLines"] = "1";
+
+  EXPECT_EQ("int main() {\n"
+            "  if (true) return 1;\n"
+            "  if (false) { return -1;\n"
+            "  } else if (1 == 2) { return -2;\n"
+            "  } else { return -3;\n"
+            "}\n"
+            "}",
+            runCheckOnCode<BracesAroundStatementsCheck>(
+                "int main() {\n"
+                "  if (true) return 1;\n"
+                "  if (false) return -1;\n"
+                "  else if (1 == 2) return -2;\n"
+                "  else return -3;\n"
+                "}",
+                nullptr, "input.cc", None, Options));
+
+  // If the last else is an else-if, we also force it.
+  EXPECT_EQ("int main() {\n"
+            "  if (false) { return -1;\n"
+            "  } else if (1 == 2) { return -2;\n"
+            "}\n"
+            "}",
+            runCheckOnCode<BracesAroundStatementsCheck>(
+                "int main() {\n"
+                "  if (false) return -1;\n"
+                "  else if (1 == 2) return -2;\n"
+                "}",
+                nullptr, "input.cc", None, Options));
+}
+
+TEST(BracesAroundStatementsCheckTest, For) {
+  EXPECT_NO_CHANGES(BracesAroundStatementsCheck, "int main() {\n"
+                                                 "  for (;;) {\n"
+                                                 "    ;\n"
+                                                 "  }\n"
+                                                 "}");
+  EXPECT_EQ("int main() {\n"
+            "  for (;;) {\n"
+            "    ;\n"
+            "}\n"
+            "}",
+            runCheckOnCode<BracesAroundStatementsCheck>("int main() {\n"
+                                                        "  for (;;)\n"
+                                                        "    ;\n"
+                                                        "}"));
+  EXPECT_EQ("int main() {\n"
+            "  for (;;) {\n"
+            "    /**/ ;\n"
+            "}\n"
+            "}",
+            runCheckOnCode<BracesAroundStatementsCheck>("int main() {\n"
+                                                        "  for (;;)\n"
+                                                        "    /**/ ;\n"
+                                                        "}"));
+  EXPECT_EQ("int main() {\n"
+            "  for (;;) {\n"
+            "    return -1 /**/ ;\n"
+            "}\n"
+            "}",
+            runCheckOnCode<BracesAroundStatementsCheck>("int main() {\n"
+                                                        "  for (;;)\n"
+                                                        "    return -1 /**/ ;\n"
+                                                        "}"));
+}
+
+TEST(BracesAroundStatementsCheckTest, ForRange) {
+  EXPECT_NO_CHANGES(BracesAroundStatementsCheck, "int main() {\n"
+                                                 "  int arr[4];\n"
+                                                 "  for (int i : arr) {\n"
+                                                 "    ;\n"
+                                                 "  }\n"
+                                                 "}");
+  EXPECT_EQ("int main() {\n"
+            "  int arr[4];\n"
+            "  for (int i : arr) {\n"
+            "    ;\n"
+            "}\n"
+            "  for (int i : arr) {\n"
+            "    return -1 ;\n"
+            "}\n"
+            "}",
+            runCheckOnCode<BracesAroundStatementsCheck>("int main() {\n"
+                                                        "  int arr[4];\n"
+                                                        "  for (int i : arr)\n"
+                                                        "    ;\n"
+                                                        "  for (int i : arr)\n"
+                                                        "    return -1 ;\n"
+                                                        "}"));
+}
+
+TEST(BracesAroundStatementsCheckTest, DoWhile) {
+  EXPECT_NO_CHANGES(BracesAroundStatementsCheck, "int main() {\n"
+                                                 "  do {\n"
+                                                 "    ;\n"
+                                                 "  } while (false);\n"
+                                                 "}");
+  EXPECT_EQ("int main() {\n"
+            "  do {\n"
+            "    ;\n"
+            "  } while (false);\n"
+            "}",
+            runCheckOnCode<BracesAroundStatementsCheck>("int main() {\n"
+                                                        "  do\n"
+                                                        "    ;\n"
+                                                        "  while (false);\n"
+                                                        "}"));
+}
+
+TEST(BracesAroundStatementsCheckTest, While) {
+  EXPECT_NO_CHANGES(BracesAroundStatementsCheck, "int main() {\n"
+                                                 "  while (false) {\n"
+                                                 "    ;\n"
+                                                 "  }\n"
+                                                 "}");
+  EXPECT_EQ("int main() {\n"
+            "  while (false) {\n"
+            "    ;\n"
+            "}\n"
+            "}",
+            runCheckOnCode<BracesAroundStatementsCheck>("int main() {\n"
+                                                        "  while (false)\n"
+                                                        "    ;\n"
+                                                        "}"));
+  EXPECT_EQ("int main() {\n"
+            "  while (auto Cond = false) {\n"
+            "    ;\n"
+            "}\n"
+            "}",
+            runCheckOnCode<BracesAroundStatementsCheck>(
+                "int main() {\n"
+                "  while (auto Cond = false)\n"
+                "    ;\n"
+                "}"));
+  EXPECT_EQ("int main() {\n"
+            "  while (false /*dummy token*/) {\n"
+            "    ;\n"
+            "}\n"
+            "}",
+            runCheckOnCode<BracesAroundStatementsCheck>(
+                "int main() {\n"
+                "  while (false /*dummy token*/)\n"
+                "    ;\n"
+                "}"));
+  EXPECT_EQ("int main() {\n"
+            "  while (false) {\n"
+            "    break;\n"
+            "}\n"
+            "}",
+            runCheckOnCode<BracesAroundStatementsCheck>("int main() {\n"
+                                                        "  while (false)\n"
+                                                        "    break;\n"
+                                                        "}"));
+  EXPECT_EQ("int main() {\n"
+            "  while (false) {\n"
+            "    break /**/;\n"
+            "}\n"
+            "}",
+            runCheckOnCode<BracesAroundStatementsCheck>("int main() {\n"
+                                                        "  while (false)\n"
+                                                        "    break /**/;\n"
+                                                        "}"));
+  EXPECT_EQ("int main() {\n"
+            "  while (false) {\n"
+            "    /**/;\n"
+            "}\n"
+            "}",
+            runCheckOnCode<BracesAroundStatementsCheck>("int main() {\n"
+                                                        "  while (false)\n"
+                                                        "    /**/;\n"
+                                                        "}"));
+}
+
+TEST(BracesAroundStatementsCheckTest, Nested) {
+  EXPECT_EQ("int main() {\n"
+            "  do { if (true) {}} while (false);\n"
+            "}",
+            runCheckOnCode<BracesAroundStatementsCheck>(
+                "int main() {\n"
+                "  do if (true) {}while (false);\n"
+                "}"));
+  EXPECT_EQ("int main() {\n"
+            "  do { if (true) {}} while (false);\n"
+            "}",
+            runCheckOnCode<BracesAroundStatementsCheck>(
+                "int main() {\n"
+                "  do if (true) {}while (false);\n"
+                "}"));
+  EXPECT_EQ(
+      "int main() {\n"
+      "  if (true) {\n"
+      "    // comment\n"
+      "    if (false) {\n"
+      "      // comment\n"
+      "      /**/ ; // comment\n"
+      "    }\n"
+      "}\n"
+      "}",
+      runCheckOnCode<BracesAroundStatementsCheck>("int main() {\n"
+                                                  "  if (true)\n"
+                                                  "    // comment\n"
+                                                  "    if (false) {\n"
+                                                  "      // comment\n"
+                                                  "      /**/ ; // comment\n"
+                                                  "    }\n"
+                                                  "}"));
+}
+
+TEST(BracesAroundStatementsCheckTest, Macros) {
+  EXPECT_NO_CHANGES(BracesAroundStatementsCheck,
+                    "#define IF(COND) if (COND) return -1;\n"
+                    "int main() {\n"
+                    "  IF(false)\n"
+                    "}");
+  EXPECT_NO_CHANGES(BracesAroundStatementsCheck,
+                    "#define FOR(COND) for (COND) return -1;\n"
+                    "int main() {\n"
+                    "  FOR(;;)\n"
+                    "}");
+  EXPECT_EQ("#define DO_IT ++i\n"
+            "int i = 0;\n"
+            "int main() {\n"
+            "  if (false) {\n"
+            "    DO_IT;\n"
+            "  } else if (1 == 2) {\n"
+            "    DO_IT;\n"
+            "  } else {\n"
+            "    DO_IT;\n"
+            "}\n"
+            "}",
+            runCheckOnCode<BracesAroundStatementsCheck>("#define DO_IT ++i\n"
+                                                        "int i = 0;\n"
+                                                        "int main() {\n"
+                                                        "  if (false)\n"
+                                                        "    DO_IT;\n"
+                                                        "  else if (1 == 2)\n"
+                                                        "    DO_IT;\n"
+                                                        "  else\n"
+                                                        "    DO_IT;\n"
+                                                        "}"));
+}
+
+#define EXPECT_NO_CHANGES_WITH_OPTS(Check, Opts, Code)                         \
+  EXPECT_EQ(Code, runCheckOnCode<Check>(Code, nullptr, "input.cc", None, Opts))
+TEST(BracesAroundStatementsCheckTest, ImplicitCastInReturn) {
+  ClangTidyOptions Opts;
+  Opts.CheckOptions["test-check-0.ShortStatementLines"] = "1";
+
+  EXPECT_NO_CHANGES_WITH_OPTS(BracesAroundStatementsCheck, Opts,
+                              "const char *f() {\n"
+                              "  if (true) return \"\";\n"
+                              "  return \"abc\";\n"
+                              "}\n");
+}
+
+} // namespace test
+} // namespace tidy
+} // namespace clang
diff --git a/unittests/clang-tidy/UsingInserterTest.cpp b/unittests/clang-tidy/UsingInserterTest.cpp
new file mode 100644 (file)
index 0000000..9e465a0
--- /dev/null
@@ -0,0 +1,114 @@
+//===---- UsingInserterTest.cpp - clang-tidy ----------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "../clang-tidy/utils/UsingInserter.h"
+
+#include "ClangTidyTest.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace tidy {
+namespace utils {
+
+// Replace all function calls with calls to foo::func. Inserts using
+// declarations as necessary. This checker is for testing only. It
+// can only run on one test case (e.g. wih one SourceManager).
+class InsertUsingCheck : public clang::tidy::ClangTidyCheck {
+public:
+  InsertUsingCheck(StringRef Name, ClangTidyContext *Context)
+      :ClangTidyCheck(Name, Context) {}
+  void registerMatchers(clang::ast_matchers::MatchFinder *Finder) override {
+    Finder->addMatcher(clang::ast_matchers::callExpr().bind("foo"), this);
+  }
+  void
+  check(const clang::ast_matchers::MatchFinder::MatchResult &Result) override {
+    if (!Inserter)
+      Inserter.reset(new UsingInserter(*Result.SourceManager));
+
+    const auto *Call = Result.Nodes.getNodeAs<clang::CallExpr>("foo");
+    assert(Call != nullptr && "Did not find node \"foo\"");
+    auto Hint =
+        Inserter->createUsingDeclaration(*Result.Context, *Call, "::foo::func");
+
+    if (Hint.hasValue())
+      diag(Call->getLocStart(), "Fix for testing") << Hint.getValue();
+
+    diag(Call->getLocStart(), "insert call")
+        << clang::FixItHint::CreateReplacement(
+               Call->getCallee()->getSourceRange(),
+               Inserter->getShortName(*Result.Context, *Call, "::foo::func"));
+  }
+
+private:
+  std::unique_ptr<UsingInserter> Inserter;
+};
+
+template <typename Check>
+std::string runChecker(StringRef Code, unsigned ExpectedWarningCount) {
+  std::map<StringRef, StringRef> AdditionalFileContents = {{"foo.h",
+                                                            "namespace foo {\n"
+                                                            "namespace bar {\n"
+                                                            "}\n"
+                                                            "void func() { }\n"
+                                                            "}"}};
+  std::vector<ClangTidyError> errors;
+
+  std::string result =
+      test::runCheckOnCode<Check>(Code, &errors, "foo.cc", None,
+                                  ClangTidyOptions(), AdditionalFileContents);
+
+  EXPECT_EQ(ExpectedWarningCount, errors.size());
+  return result;
+}
+
+TEST(UsingInserterTest, ReusesExisting) {
+  EXPECT_EQ("#include \"foo.h\"\n"
+            "namespace {"
+            "using ::foo::func;\n"
+            "void f() { func(); }"
+            "}",
+            runChecker<InsertUsingCheck>("#include \"foo.h\"\n"
+                                         "namespace {"
+                                         "using ::foo::func;\n"
+                                         "void f() { f(); }"
+                                         "}",
+                                         1));
+}
+
+TEST(UsingInserterTest, ReusesExistingGlobal) {
+  EXPECT_EQ("#include \"foo.h\"\n"
+            "using ::foo::func;\n"
+            "namespace {"
+            "void f() { func(); }"
+            "}",
+            runChecker<InsertUsingCheck>("#include \"foo.h\"\n"
+                                         "using ::foo::func;\n"
+                                         "namespace {"
+                                         "void f() { f(); }"
+                                         "}",
+                                         1));
+}
+
+TEST(UsingInserterTest, AvoidsConflict) {
+  EXPECT_EQ("#include \"foo.h\"\n"
+            "namespace {"
+            "void f() { int func; ::foo::func(); }"
+            "}",
+            runChecker<InsertUsingCheck>("#include \"foo.h\"\n"
+                                         "namespace {"
+                                         "void f() { int func; f(); }"
+                                         "}",
+                                         1));
+}
+
+} // namespace utils
+} // namespace tidy
+} // namespace clang
diff --git a/unittests/clangd/CMakeLists.txt b/unittests/clangd/CMakeLists.txt
new file mode 100644 (file)
index 0000000..38ae280
--- /dev/null
@@ -0,0 +1,24 @@
+set(LLVM_LINK_COMPONENTS
+  support
+  )
+
+get_filename_component(CLANGD_SOURCE_DIR
+  ${CMAKE_CURRENT_SOURCE_DIR}/../../clangd REALPATH)
+include_directories(
+  ${CLANGD_SOURCE_DIR}
+  )
+
+add_extra_unittest(ClangdTests
+  ClangdTests.cpp
+  )
+
+target_link_libraries(ClangdTests
+  clangBasic
+  clangDaemon
+  clangFormat
+  clangFrontend
+  clangSema
+  clangTooling
+  clangToolingCore
+  LLVMSupport
+  )
diff --git a/unittests/clangd/ClangdTests.cpp b/unittests/clangd/ClangdTests.cpp
new file mode 100644 (file)
index 0000000..99b9bcd
--- /dev/null
@@ -0,0 +1,536 @@
+//===-- ClangdTests.cpp - Clangd unit tests ---------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ClangdServer.h"
+#include "clang/Basic/VirtualFileSystem.h"
+#include "clang/Config/config.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/Regex.h"
+#include "gtest/gtest.h"
+#include <algorithm>
+#include <iostream>
+#include <string>
+#include <vector>
+
+namespace clang {
+namespace vfs {
+
+/// An implementation of vfs::FileSystem that only allows access to
+/// files and folders inside a set of whitelisted directories.
+///
+/// FIXME(ibiryukov): should it also emulate access to parents of whitelisted
+/// directories with only whitelisted contents?
+class FilteredFileSystem : public vfs::FileSystem {
+public:
+  /// The paths inside \p WhitelistedDirs should be absolute
+  FilteredFileSystem(std::vector<std::string> WhitelistedDirs,
+                     IntrusiveRefCntPtr<vfs::FileSystem> InnerFS)
+      : WhitelistedDirs(std::move(WhitelistedDirs)), InnerFS(InnerFS) {
+    assert(std::all_of(WhitelistedDirs.begin(), WhitelistedDirs.end(),
+                       [](const std::string &Path) -> bool {
+                         return llvm::sys::path::is_absolute(Path);
+                       }) &&
+           "Not all WhitelistedDirs are absolute");
+  }
+
+  virtual llvm::ErrorOr<Status> status(const Twine &Path) {
+    if (!isInsideWhitelistedDir(Path))
+      return llvm::errc::no_such_file_or_directory;
+    return InnerFS->status(Path);
+  }
+
+  virtual llvm::ErrorOr<std::unique_ptr<File>>
+  openFileForRead(const Twine &Path) {
+    if (!isInsideWhitelistedDir(Path))
+      return llvm::errc::no_such_file_or_directory;
+    return InnerFS->openFileForRead(Path);
+  }
+
+  llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
+  getBufferForFile(const Twine &Name, int64_t FileSize = -1,
+                   bool RequiresNullTerminator = true,
+                   bool IsVolatile = false) {
+    if (!isInsideWhitelistedDir(Name))
+      return llvm::errc::no_such_file_or_directory;
+    return InnerFS->getBufferForFile(Name, FileSize, RequiresNullTerminator,
+                                     IsVolatile);
+  }
+
+  virtual directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) {
+    if (!isInsideWhitelistedDir(Dir)) {
+      EC = llvm::errc::no_such_file_or_directory;
+      return directory_iterator();
+    }
+    return InnerFS->dir_begin(Dir, EC);
+  }
+
+  virtual std::error_code setCurrentWorkingDirectory(const Twine &Path) {
+    return InnerFS->setCurrentWorkingDirectory(Path);
+  }
+
+  virtual llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const {
+    return InnerFS->getCurrentWorkingDirectory();
+  }
+
+  bool exists(const Twine &Path) {
+    if (!isInsideWhitelistedDir(Path))
+      return false;
+    return InnerFS->exists(Path);
+  }
+
+  std::error_code makeAbsolute(SmallVectorImpl<char> &Path) const {
+    return InnerFS->makeAbsolute(Path);
+  }
+
+private:
+  bool isInsideWhitelistedDir(const Twine &InputPath) const {
+    SmallString<128> Path;
+    InputPath.toVector(Path);
+
+    if (makeAbsolute(Path))
+      return false;
+
+    for (const auto &Dir : WhitelistedDirs) {
+      if (Path.startswith(Dir))
+        return true;
+    }
+    return false;
+  }
+
+  std::vector<std::string> WhitelistedDirs;
+  IntrusiveRefCntPtr<vfs::FileSystem> InnerFS;
+};
+
+/// Create a vfs::FileSystem that has access only to temporary directories
+/// (obtained by calling system_temp_directory).
+IntrusiveRefCntPtr<vfs::FileSystem> getTempOnlyFS() {
+  llvm::SmallString<128> TmpDir1;
+  llvm::sys::path::system_temp_directory(/*erasedOnReboot=*/false, TmpDir1);
+  llvm::SmallString<128> TmpDir2;
+  llvm::sys::path::system_temp_directory(/*erasedOnReboot=*/true, TmpDir2);
+
+  std::vector<std::string> TmpDirs;
+  TmpDirs.push_back(TmpDir1.str());
+  if (TmpDir1 != TmpDir2)
+    TmpDirs.push_back(TmpDir2.str());
+  return new vfs::FilteredFileSystem(std::move(TmpDirs),
+                                     vfs::getRealFileSystem());
+}
+} // namespace vfs
+
+namespace clangd {
+namespace {
+
+class ErrorCheckingDiagConsumer : public DiagnosticsConsumer {
+public:
+  void onDiagnosticsReady(PathRef File,
+                          Tagged<std::vector<DiagWithFixIts>> Diagnostics) override {
+    bool HadError = false;
+    for (const auto &DiagAndFixIts : Diagnostics.Value) {
+      // FIXME: severities returned by clangd should have a descriptive
+      // diagnostic severity enum
+      const int ErrorSeverity = 1;
+      HadError = DiagAndFixIts.Diag.severity == ErrorSeverity;
+    }
+
+    std::lock_guard<std::mutex> Lock(Mutex);
+    HadErrorInLastDiags = HadError;
+    LastVFSTag = Diagnostics.Tag;
+  }
+
+  bool hadErrorInLastDiags() {
+    std::lock_guard<std::mutex> Lock(Mutex);
+    return HadErrorInLastDiags;
+  }
+
+  VFSTag lastVFSTag() {
+    return LastVFSTag;
+  }
+
+private:
+  std::mutex Mutex;
+  bool HadErrorInLastDiags = false;
+  VFSTag LastVFSTag = VFSTag();
+};
+
+class MockCompilationDatabase : public GlobalCompilationDatabase {
+public:
+  MockCompilationDatabase(bool AddFreestandingFlag) {
+    // We have to add -ffreestanding to VFS-specific tests to avoid errors on
+    // implicit includes of stdc-predef.h.
+    if (AddFreestandingFlag)
+      ExtraClangFlags.push_back("-ffreestanding");
+  }
+
+  std::vector<tooling::CompileCommand>
+  getCompileCommands(PathRef File) override {
+    if (ExtraClangFlags.empty())
+      return {};
+
+    std::vector<std::string> CommandLine;
+    CommandLine.reserve(3 + ExtraClangFlags.size());
+    CommandLine.insert(CommandLine.end(), {"clang", "-fsyntax-only"});
+    CommandLine.insert(CommandLine.end(), ExtraClangFlags.begin(),
+                       ExtraClangFlags.end());
+    CommandLine.push_back(File.str());
+
+    return {tooling::CompileCommand(llvm::sys::path::parent_path(File),
+                                    llvm::sys::path::filename(File),
+                                    CommandLine, "")};
+  }
+
+  std::vector<std::string> ExtraClangFlags;
+};
+
+class MockFSProvider : public FileSystemProvider {
+public:
+  Tagged<IntrusiveRefCntPtr<vfs::FileSystem>>
+  getTaggedFileSystem(PathRef File) override {
+    IntrusiveRefCntPtr<vfs::InMemoryFileSystem> MemFS(
+        new vfs::InMemoryFileSystem);
+    if (ExpectedFile)
+      EXPECT_EQ(*ExpectedFile, File);
+
+    for (auto &FileAndContents : Files)
+      MemFS->addFile(FileAndContents.first(), time_t(),
+                     llvm::MemoryBuffer::getMemBuffer(FileAndContents.second,
+                                                      FileAndContents.first()));
+
+    auto OverlayFS = IntrusiveRefCntPtr<vfs::OverlayFileSystem>(
+        new vfs::OverlayFileSystem(vfs::getTempOnlyFS()));
+    OverlayFS->pushOverlay(std::move(MemFS));
+    return make_tagged(OverlayFS, Tag);
+  }
+
+  llvm::Optional<SmallString<32>> ExpectedFile;
+  llvm::StringMap<std::string> Files;
+  VFSTag Tag = VFSTag();
+};
+
+/// Replaces all patterns of the form 0x123abc with spaces
+std::string replacePtrsInDump(std::string const &Dump) {
+  llvm::Regex RE("0x[0-9a-fA-F]+");
+  llvm::SmallVector<StringRef, 1> Matches;
+  llvm::StringRef Pending = Dump;
+
+  std::string Result;
+  while (RE.match(Pending, &Matches)) {
+    assert(Matches.size() == 1 && "Exactly one match expected");
+    auto MatchPos = Matches[0].data() - Pending.data();
+
+    Result += Pending.take_front(MatchPos);
+    Pending = Pending.drop_front(MatchPos + Matches[0].size());
+  }
+  Result += Pending;
+
+  return Result;
+}
+
+std::string dumpASTWithoutMemoryLocs(ClangdServer &Server, PathRef File) {
+  auto DumpWithMemLocs = Server.dumpAST(File);
+  return replacePtrsInDump(DumpWithMemLocs);
+}
+
+} // namespace
+
+class ClangdVFSTest : public ::testing::Test {
+protected:
+  SmallString<16> getVirtualTestRoot() {
+#ifdef LLVM_ON_WIN32
+    return SmallString<16>("C:\\clangd-test");
+#else
+    return SmallString<16>("/clangd-test");
+#endif
+  }
+
+  llvm::SmallString<32> getVirtualTestFilePath(PathRef File) {
+    assert(llvm::sys::path::is_relative(File) && "FileName should be relative");
+
+    llvm::SmallString<32> Path;
+    llvm::sys::path::append(Path, getVirtualTestRoot(), File);
+    return Path;
+  }
+
+  std::string parseSourceAndDumpAST(
+      PathRef SourceFileRelPath, StringRef SourceContents,
+      std::vector<std::pair<PathRef, StringRef>> ExtraFiles = {},
+      bool ExpectErrors = false) {
+    MockFSProvider FS;
+    ErrorCheckingDiagConsumer DiagConsumer;
+    MockCompilationDatabase CDB(/*AddFreestandingFlag=*/true);
+    ClangdServer Server(CDB, DiagConsumer, FS,
+                        /*RunSynchronously=*/false);
+    for (const auto &FileWithContents : ExtraFiles)
+      FS.Files[getVirtualTestFilePath(FileWithContents.first)] =
+          FileWithContents.second;
+
+    auto SourceFilename = getVirtualTestFilePath(SourceFileRelPath);
+
+    FS.ExpectedFile = SourceFilename;
+    Server.addDocument(SourceFilename, SourceContents);
+
+    auto Result = dumpASTWithoutMemoryLocs(Server, SourceFilename);
+    EXPECT_EQ(ExpectErrors, DiagConsumer.hadErrorInLastDiags());
+    return Result;
+  }
+};
+
+TEST_F(ClangdVFSTest, Parse) {
+  // FIXME: figure out a stable format for AST dumps, so that we can check the
+  // output of the dump itself is equal to the expected one, not just that it's
+  // different.
+  auto Empty = parseSourceAndDumpAST("foo.cpp", "", {});
+  auto OneDecl = parseSourceAndDumpAST("foo.cpp", "int a;", {});
+  auto SomeDecls = parseSourceAndDumpAST("foo.cpp", "int a; int b; int c;", {});
+  EXPECT_NE(Empty, OneDecl);
+  EXPECT_NE(Empty, SomeDecls);
+  EXPECT_NE(SomeDecls, OneDecl);
+
+  auto Empty2 = parseSourceAndDumpAST("foo.cpp", "");
+  auto OneDecl2 = parseSourceAndDumpAST("foo.cpp", "int a;");
+  auto SomeDecls2 = parseSourceAndDumpAST("foo.cpp", "int a; int b; int c;");
+  EXPECT_EQ(Empty, Empty2);
+  EXPECT_EQ(OneDecl, OneDecl2);
+  EXPECT_EQ(SomeDecls, SomeDecls2);
+}
+
+TEST_F(ClangdVFSTest, ParseWithHeader) {
+  parseSourceAndDumpAST("foo.cpp", "#include \"foo.h\"", {},
+                        /*ExpectErrors=*/true);
+  parseSourceAndDumpAST("foo.cpp", "#include \"foo.h\"", {{"foo.h", ""}},
+                        /*ExpectErrors=*/false);
+
+  const auto SourceContents = R"cpp(
+#include "foo.h"
+int b = a;
+)cpp";
+  parseSourceAndDumpAST("foo.cpp", SourceContents, {{"foo.h", ""}},
+                        /*ExpectErrors=*/true);
+  parseSourceAndDumpAST("foo.cpp", SourceContents, {{"foo.h", "int a;"}},
+                        /*ExpectErrors=*/false);
+}
+
+TEST_F(ClangdVFSTest, Reparse) {
+  MockFSProvider FS;
+  ErrorCheckingDiagConsumer DiagConsumer;
+  MockCompilationDatabase CDB(/*AddFreestandingFlag=*/true);
+  ClangdServer Server(CDB, DiagConsumer, FS,
+                      /*RunSynchronously=*/false);
+
+  const auto SourceContents = R"cpp(
+#include "foo.h"
+int b = a;
+)cpp";
+
+  auto FooCpp = getVirtualTestFilePath("foo.cpp");
+  auto FooH = getVirtualTestFilePath("foo.h");
+
+  FS.Files[FooH] = "int a;";
+  FS.Files[FooCpp] = SourceContents;
+  FS.ExpectedFile = FooCpp;
+
+  Server.addDocument(FooCpp, SourceContents);
+  auto DumpParse1 = dumpASTWithoutMemoryLocs(Server, FooCpp);
+  EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags());
+
+  Server.addDocument(FooCpp, "");
+  auto DumpParseEmpty = dumpASTWithoutMemoryLocs(Server, FooCpp);
+  EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags());
+
+  Server.addDocument(FooCpp, SourceContents);
+  auto DumpParse2 = dumpASTWithoutMemoryLocs(Server, FooCpp);
+  EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags());
+
+  EXPECT_EQ(DumpParse1, DumpParse2);
+  EXPECT_NE(DumpParse1, DumpParseEmpty);
+}
+
+TEST_F(ClangdVFSTest, ReparseOnHeaderChange) {
+  MockFSProvider FS;
+  ErrorCheckingDiagConsumer DiagConsumer;
+  MockCompilationDatabase CDB(/*AddFreestandingFlag=*/true);
+
+  ClangdServer Server(CDB, DiagConsumer, FS,
+                      /*RunSynchronously=*/false);
+
+  const auto SourceContents = R"cpp(
+#include "foo.h"
+int b = a;
+)cpp";
+
+  auto FooCpp = getVirtualTestFilePath("foo.cpp");
+  auto FooH = getVirtualTestFilePath("foo.h");
+
+  FS.Files[FooH] = "int a;";
+  FS.Files[FooCpp] = SourceContents;
+  FS.ExpectedFile = FooCpp;
+
+  Server.addDocument(FooCpp, SourceContents);
+  auto DumpParse1 = dumpASTWithoutMemoryLocs(Server, FooCpp);
+  EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags());
+
+  FS.Files[FooH] = "";
+  Server.forceReparse(FooCpp);
+  auto DumpParseDifferent = dumpASTWithoutMemoryLocs(Server, FooCpp);
+  EXPECT_TRUE(DiagConsumer.hadErrorInLastDiags());
+
+  FS.Files[FooH] = "int a;";
+  Server.forceReparse(FooCpp);
+  auto DumpParse2 = dumpASTWithoutMemoryLocs(Server, FooCpp);
+  EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags());
+
+  EXPECT_EQ(DumpParse1, DumpParse2);
+  EXPECT_NE(DumpParse1, DumpParseDifferent);
+}
+
+TEST_F(ClangdVFSTest, CheckVersions) {
+  MockFSProvider FS;
+  ErrorCheckingDiagConsumer DiagConsumer;
+  MockCompilationDatabase CDB(/*AddFreestandingFlag=*/true);
+  ClangdServer Server(CDB, DiagConsumer, FS,
+                      /*RunSynchronously=*/true);
+
+  auto FooCpp = getVirtualTestFilePath("foo.cpp");
+  const auto SourceContents = "int a;";
+  FS.Files[FooCpp] = SourceContents;
+  FS.ExpectedFile = FooCpp;
+
+  FS.Tag = "123";
+  Server.addDocument(FooCpp, SourceContents);
+  EXPECT_EQ(DiagConsumer.lastVFSTag(), FS.Tag);
+  EXPECT_EQ(Server.codeComplete(FooCpp, Position{0, 0}).Tag, FS.Tag);
+
+  FS.Tag = "321";
+  Server.addDocument(FooCpp, SourceContents);
+  EXPECT_EQ(DiagConsumer.lastVFSTag(), FS.Tag);
+  EXPECT_EQ(Server.codeComplete(FooCpp, Position{0, 0}).Tag, FS.Tag);
+}
+
+// Only enable this test on Unix
+#ifdef LLVM_ON_UNIX
+TEST_F(ClangdVFSTest, SearchLibDir) {
+  // Checks that searches for GCC installation is done through vfs.
+  MockFSProvider FS;
+  ErrorCheckingDiagConsumer DiagConsumer;
+  MockCompilationDatabase CDB(/*AddFreestandingFlag=*/true);
+  CDB.ExtraClangFlags.insert(CDB.ExtraClangFlags.end(),
+                             {"-xc++", "-target", "x86_64-linux-unknown",
+                              "-m64", "-stdlib=libstdc++"});
+  ClangdServer Server(CDB, DiagConsumer, FS,
+                      /*RunSynchronously=*/true);
+
+  // Just a random gcc version string
+  SmallString<8> Version("4.9.3");
+
+  // A lib dir for gcc installation
+  SmallString<64> LibDir("/usr/lib/gcc/x86_64-linux-gnu");
+  llvm::sys::path::append(LibDir, Version);
+
+  // Put crtbegin.o into LibDir/64 to trick clang into thinking there's a gcc
+  // installation there.
+  SmallString<64> DummyLibFile;
+  llvm::sys::path::append(DummyLibFile, LibDir, "64", "crtbegin.o");
+  FS.Files[DummyLibFile] = "";
+
+  SmallString<64> IncludeDir("/usr/include/c++");
+  llvm::sys::path::append(IncludeDir, Version);
+
+  SmallString<64> StringPath;
+  llvm::sys::path::append(StringPath, IncludeDir, "string");
+  FS.Files[StringPath] = "class mock_string {};";
+
+  auto FooCpp = getVirtualTestFilePath("foo.cpp");
+  const auto SourceContents = R"cpp(
+#include <string>
+mock_string x;
+)cpp";
+  FS.Files[FooCpp] = SourceContents;
+
+  Server.addDocument(FooCpp, SourceContents);
+  EXPECT_FALSE(DiagConsumer.hadErrorInLastDiags());
+
+  const auto SourceContentsWithError = R"cpp(
+#include <string>
+std::string x;
+)cpp";
+  Server.addDocument(FooCpp, SourceContentsWithError);
+  EXPECT_TRUE(DiagConsumer.hadErrorInLastDiags());
+}
+#endif // LLVM_ON_UNIX
+
+class ClangdCompletionTest : public ClangdVFSTest {
+protected:
+  bool ContainsItem(std::vector<CompletionItem> const &Items, StringRef Name) {
+    for (const auto &Item : Items) {
+      if (Item.insertText == Name)
+        return true;
+    }
+    return false;
+  }
+};
+
+TEST_F(ClangdCompletionTest, CheckContentsOverride) {
+  MockFSProvider FS;
+  ErrorCheckingDiagConsumer DiagConsumer;
+  MockCompilationDatabase CDB(/*AddFreestandingFlag=*/true);
+
+  ClangdServer Server(CDB, DiagConsumer, FS,
+                      /*RunSynchronously=*/false);
+
+  auto FooCpp = getVirtualTestFilePath("foo.cpp");
+  const auto SourceContents = R"cpp(
+int aba;
+int b =   ;
+)cpp";
+
+  const auto OverridenSourceContents = R"cpp(
+int cbc;
+int b =   ;
+)cpp";
+  // Complete after '=' sign. We need to be careful to keep the SourceContents'
+  // size the same.
+  // We complete on the 3rd line (2nd in zero-based numbering), because raw
+  // string literal of the SourceContents starts with a newline(it's easy to
+  // miss).
+  Position CompletePos = {2, 8};
+  FS.Files[FooCpp] = SourceContents;
+  FS.ExpectedFile = FooCpp;
+
+  Server.addDocument(FooCpp, SourceContents);
+
+  {
+    auto CodeCompletionResults1 =
+        Server.codeComplete(FooCpp, CompletePos, None).Value;
+    EXPECT_TRUE(ContainsItem(CodeCompletionResults1, "aba"));
+    EXPECT_FALSE(ContainsItem(CodeCompletionResults1, "cbc"));
+  }
+
+  {
+    auto CodeCompletionResultsOverriden =
+        Server
+            .codeComplete(FooCpp, CompletePos,
+                          StringRef(OverridenSourceContents))
+            .Value;
+    EXPECT_TRUE(ContainsItem(CodeCompletionResultsOverriden, "cbc"));
+    EXPECT_FALSE(ContainsItem(CodeCompletionResultsOverriden, "aba"));
+  }
+
+  {
+    auto CodeCompletionResults2 =
+        Server.codeComplete(FooCpp, CompletePos, None).Value;
+    EXPECT_TRUE(ContainsItem(CodeCompletionResults2, "aba"));
+    EXPECT_FALSE(ContainsItem(CodeCompletionResults2, "cbc"));
+  }
+}
+
+} // namespace clangd
+} // namespace clang
diff --git a/unittests/include-fixer/CMakeLists.txt b/unittests/include-fixer/CMakeLists.txt
new file mode 100644 (file)
index 0000000..caab1cb
--- /dev/null
@@ -0,0 +1,30 @@
+set(LLVM_LINK_COMPONENTS
+  support
+  )
+
+get_filename_component(INCLUDE_FIXER_SOURCE_DIR
+  ${CMAKE_CURRENT_SOURCE_DIR}/../../include-fixer REALPATH)
+include_directories(
+  ${INCLUDE_FIXER_SOURCE_DIR}
+  )
+
+# We'd like to clang/unittests/Tooling/RewriterTestContext.h in the test.
+include_directories(${CLANG_SOURCE_DIR})
+
+add_extra_unittest(IncludeFixerTests
+  IncludeFixerTest.cpp
+  FuzzySymbolIndexTests.cpp
+  )
+
+target_link_libraries(IncludeFixerTests
+  clangBasic
+  clangFormat
+  clangFrontend
+  clangIncludeFixer
+  clangRewrite
+  clangTooling
+  clangToolingCore
+  findAllSymbols
+  )
+
+add_subdirectory(find-all-symbols)
diff --git a/unittests/include-fixer/FuzzySymbolIndexTests.cpp b/unittests/include-fixer/FuzzySymbolIndexTests.cpp
new file mode 100644 (file)
index 0000000..17679c5
--- /dev/null
@@ -0,0 +1,61 @@
+//===-- FuzzySymbolIndexTests.cpp - Fuzzy symbol index unit tests ---------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "FuzzySymbolIndex.h"
+#include "gmock/gmock.h"
+#include "llvm/Support/Regex.h"
+#include "gtest/gtest.h"
+
+using testing::ElementsAre;
+using testing::Not;
+
+namespace clang {
+namespace include_fixer {
+namespace {
+
+TEST(FuzzySymbolIndexTest, Tokenize) {
+  EXPECT_THAT(FuzzySymbolIndex::tokenize("URLHandlerCallback"),
+              ElementsAre("url", "handler", "callback"));
+  EXPECT_THAT(FuzzySymbolIndex::tokenize("snake_case11"),
+              ElementsAre("snake", "case", "11"));
+  EXPECT_THAT(FuzzySymbolIndex::tokenize("__$42!!BOB\nbob"),
+              ElementsAre("42", "bob", "bob"));
+}
+
+MATCHER_P(MatchesSymbol, Identifier, "") {
+  llvm::Regex Pattern("^" + arg);
+  std::string err;
+  if (!Pattern.isValid(err)) {
+    *result_listener << "invalid regex: " << err;
+    return false;
+  }
+  auto Tokens = FuzzySymbolIndex::tokenize(Identifier);
+  std::string Target = llvm::join(Tokens.begin(), Tokens.end(), " ");
+  *result_listener << "matching against '" << Target << "'";
+  return llvm::Regex("^" + arg).match(Target);
+}
+
+TEST(FuzzySymbolIndexTest, QueryRegexp) {
+  auto QueryRegexp = [](const std::string &query) {
+    return FuzzySymbolIndex::queryRegexp(FuzzySymbolIndex::tokenize(query));
+  };
+  EXPECT_THAT(QueryRegexp("uhc"), MatchesSymbol("URLHandlerCallback"));
+  EXPECT_THAT(QueryRegexp("urhaca"), MatchesSymbol("URLHandlerCallback"));
+  EXPECT_THAT(QueryRegexp("uhcb"), Not(MatchesSymbol("URLHandlerCallback")))
+      << "Non-prefix";
+  EXPECT_THAT(QueryRegexp("uc"), Not(MatchesSymbol("URLHandlerCallback")))
+      << "Skip token";
+
+  EXPECT_THAT(QueryRegexp("uptr"), MatchesSymbol("unique_ptr"));
+  EXPECT_THAT(QueryRegexp("UniP"), MatchesSymbol("unique_ptr"));
+}
+
+} // namespace
+} // namespace include_fixer
+} // namespace clang
diff --git a/unittests/include-fixer/IncludeFixerTest.cpp b/unittests/include-fixer/IncludeFixerTest.cpp
new file mode 100644 (file)
index 0000000..8454669
--- /dev/null
@@ -0,0 +1,370 @@
+//===-- IncludeFixerTest.cpp - Include fixer unit tests -------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "InMemorySymbolIndex.h"
+#include "IncludeFixer.h"
+#include "SymbolIndexManager.h"
+#include "unittests/Tooling/RewriterTestContext.h"
+#include "clang/Tooling/Tooling.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace include_fixer {
+namespace {
+
+using find_all_symbols::SymbolInfo;
+using find_all_symbols::SymbolAndSignals;
+
+static bool runOnCode(tooling::ToolAction *ToolAction, StringRef Code,
+                      StringRef FileName,
+                      const std::vector<std::string> &ExtraArgs) {
+  llvm::IntrusiveRefCntPtr<vfs::InMemoryFileSystem> InMemoryFileSystem(
+      new vfs::InMemoryFileSystem);
+  llvm::IntrusiveRefCntPtr<FileManager> Files(
+      new FileManager(FileSystemOptions(), InMemoryFileSystem));
+  // FIXME: Investigate why -fms-compatibility breaks tests.
+  std::vector<std::string> Args = {"include_fixer", "-fsyntax-only",
+                                   "-fno-ms-compatibility", FileName};
+  Args.insert(Args.end(), ExtraArgs.begin(), ExtraArgs.end());
+  tooling::ToolInvocation Invocation(
+      Args, ToolAction, Files.get(),
+      std::make_shared<PCHContainerOperations>());
+
+  InMemoryFileSystem->addFile(FileName, 0,
+                              llvm::MemoryBuffer::getMemBuffer(Code));
+
+  InMemoryFileSystem->addFile("foo.h", 0,
+                              llvm::MemoryBuffer::getMemBuffer("\n"));
+  InMemoryFileSystem->addFile("dir/bar.h", 0,
+                              llvm::MemoryBuffer::getMemBuffer("\n"));
+  InMemoryFileSystem->addFile("dir/otherdir/qux.h", 0,
+                              llvm::MemoryBuffer::getMemBuffer("\n"));
+  InMemoryFileSystem->addFile("header.h", 0,
+                              llvm::MemoryBuffer::getMemBuffer("bar b;"));
+  return Invocation.run();
+}
+
+static std::string runIncludeFixer(
+    StringRef Code,
+    const std::vector<std::string> &ExtraArgs = std::vector<std::string>()) {
+  std::vector<SymbolAndSignals> Symbols = {
+      {SymbolInfo("string", SymbolInfo::SymbolKind::Class, "<string>",
+                  {{SymbolInfo::ContextType::Namespace, "std"}}),
+       SymbolInfo::Signals{}},
+      {SymbolInfo("sting", SymbolInfo::SymbolKind::Class, "\"sting\"",
+                  {{SymbolInfo::ContextType::Namespace, "std"}}),
+       SymbolInfo::Signals{}},
+      {SymbolInfo("foo", SymbolInfo::SymbolKind::Class,
+                  "\"dir/otherdir/qux.h\"",
+                  {{SymbolInfo::ContextType::Namespace, "b"},
+                   {SymbolInfo::ContextType::Namespace, "a"}}),
+       SymbolInfo::Signals{}},
+      {SymbolInfo("bar", SymbolInfo::SymbolKind::Class, "\"bar.h\"",
+                  {{SymbolInfo::ContextType::Namespace, "b"},
+                   {SymbolInfo::ContextType::Namespace, "a"}}),
+       SymbolInfo::Signals{}},
+      {SymbolInfo("bar", SymbolInfo::SymbolKind::Class, "\"bar2.h\"",
+                  {{SymbolInfo::ContextType::Namespace, "c"},
+                   {SymbolInfo::ContextType::Namespace, "a"}}),
+       SymbolInfo::Signals{}},
+      {SymbolInfo("Green", SymbolInfo::SymbolKind::Class, "\"color.h\"",
+                  {{SymbolInfo::ContextType::EnumDecl, "Color"},
+                   {SymbolInfo::ContextType::Namespace, "b"},
+                   {SymbolInfo::ContextType::Namespace, "a"}}),
+       SymbolInfo::Signals{}},
+      {SymbolInfo("Vector", SymbolInfo::SymbolKind::Class, "\"Vector.h\"",
+                  {{SymbolInfo::ContextType::Namespace, "__a"},
+                   {SymbolInfo::ContextType::Namespace, "a"}}),
+       SymbolInfo::Signals{/*Seen=*/2, 0}},
+      {SymbolInfo("Vector", SymbolInfo::SymbolKind::Class, "\"Vector.h\"",
+                  {{SymbolInfo::ContextType::Namespace, "a"}}),
+       SymbolInfo::Signals{/*Seen=*/2, 0}},
+      {SymbolInfo("StrCat", SymbolInfo::SymbolKind::Class, "\"strcat.h\"",
+                  {{SymbolInfo::ContextType::Namespace, "str"}}),
+       SymbolInfo::Signals{}},
+      {SymbolInfo("str", SymbolInfo::SymbolKind::Class, "\"str.h\"", {}),
+       SymbolInfo::Signals{}},
+      {SymbolInfo("foo2", SymbolInfo::SymbolKind::Class, "\"foo2.h\"", {}),
+       SymbolInfo::Signals{}},
+  };
+  auto SymbolIndexMgr = llvm::make_unique<SymbolIndexManager>();
+  SymbolIndexMgr->addSymbolIndex(
+      [=]() { return llvm::make_unique<InMemorySymbolIndex>(Symbols); });
+
+  std::vector<IncludeFixerContext> FixerContexts;
+  IncludeFixerActionFactory Factory(*SymbolIndexMgr, FixerContexts, "llvm");
+  std::string FakeFileName = "input.cc";
+  runOnCode(&Factory, Code, FakeFileName, ExtraArgs);
+  assert(FixerContexts.size() == 1);
+  if (FixerContexts.front().getHeaderInfos().empty())
+    return Code;
+  auto Replaces = createIncludeFixerReplacements(Code, FixerContexts.front());
+  EXPECT_TRUE(static_cast<bool>(Replaces))
+      << llvm::toString(Replaces.takeError()) << "\n";
+  if (!Replaces)
+    return "";
+  RewriterTestContext Context;
+  FileID ID = Context.createInMemoryFile(FakeFileName, Code);
+  tooling::applyAllReplacements(*Replaces, Context.Rewrite);
+  return Context.getRewrittenText(ID);
+}
+
+TEST(IncludeFixer, Typo) {
+  EXPECT_EQ("#include <string>\nstd::string foo;\n",
+            runIncludeFixer("std::string foo;\n"));
+
+  EXPECT_EQ("// comment\n#include \"foo.h\"\n#include <string>\n"
+            "std::string foo;\n#include \"dir/bar.h\"\n",
+            runIncludeFixer("// comment\n#include \"foo.h\"\nstd::string foo;\n"
+                            "#include \"dir/bar.h\"\n"));
+
+  EXPECT_EQ("#include \"foo.h\"\n#include <string>\nstd::string foo;\n",
+            runIncludeFixer("#include \"foo.h\"\nstd::string foo;\n"));
+
+  EXPECT_EQ(
+      "#include \"foo.h\"\n#include <string>\nstd::string::size_type foo;\n",
+      runIncludeFixer("#include \"foo.h\"\nstd::string::size_type foo;\n"));
+
+  EXPECT_EQ("#include <string>\nstd::string foo;\n",
+            runIncludeFixer("string foo;\n"));
+
+  // Should not match std::string.
+  EXPECT_EQ("::string foo;\n", runIncludeFixer("::string foo;\n"));
+}
+
+TEST(IncludeFixer, IncompleteType) {
+  EXPECT_EQ(
+      "#include \"foo.h\"\n#include <string>\n"
+      "namespace std {\nclass string;\n}\nstd::string foo;\n",
+      runIncludeFixer("#include \"foo.h\"\n"
+                      "namespace std {\nclass string;\n}\nstring foo;\n"));
+
+  EXPECT_EQ("#include <string>\n"
+            "class string;\ntypedef string foo;\nfoo f;\n",
+            runIncludeFixer("class string;\ntypedef string foo;\nfoo f;\n"));
+}
+
+TEST(IncludeFixer, MinimizeInclude) {
+  std::vector<std::string> IncludePath = {"-Idir/"};
+  EXPECT_EQ("#include \"otherdir/qux.h\"\na::b::foo bar;\n",
+            runIncludeFixer("a::b::foo bar;\n", IncludePath));
+
+  IncludePath = {"-isystemdir"};
+  EXPECT_EQ("#include <otherdir/qux.h>\na::b::foo bar;\n",
+            runIncludeFixer("a::b::foo bar;\n", IncludePath));
+
+  IncludePath = {"-iquotedir"};
+  EXPECT_EQ("#include \"otherdir/qux.h\"\na::b::foo bar;\n",
+            runIncludeFixer("a::b::foo bar;\n", IncludePath));
+
+  IncludePath = {"-Idir", "-Idir/otherdir"};
+  EXPECT_EQ("#include \"qux.h\"\na::b::foo bar;\n",
+            runIncludeFixer("a::b::foo bar;\n", IncludePath));
+}
+
+TEST(IncludeFixer, NestedName) {
+  EXPECT_EQ("#include \"dir/otherdir/qux.h\"\n"
+            "int x = a::b::foo(0);\n",
+            runIncludeFixer("int x = a::b::foo(0);\n"));
+
+  // FIXME: Handle simple macros.
+  EXPECT_EQ("#define FOO a::b::foo\nint x = FOO;\n",
+            runIncludeFixer("#define FOO a::b::foo\nint x = FOO;\n"));
+  EXPECT_EQ("#define FOO(x) a::##x\nint x = FOO(b::foo);\n",
+            runIncludeFixer("#define FOO(x) a::##x\nint x = FOO(b::foo);\n"));
+
+  // The empty namespace is cleaned up by clang-format after include-fixer
+  // finishes.
+  EXPECT_EQ("#include \"dir/otherdir/qux.h\"\n"
+            "\nint a = a::b::foo(0);\n",
+            runIncludeFixer("namespace a {}\nint a = a::b::foo(0);\n"));
+}
+
+TEST(IncludeFixer, MultipleMissingSymbols) {
+  EXPECT_EQ("#include <string>\nstd::string bar;\nstd::sting foo;\n",
+            runIncludeFixer("std::string bar;\nstd::sting foo;\n"));
+}
+
+TEST(IncludeFixer, ScopedNamespaceSymbols) {
+  EXPECT_EQ("#include \"bar.h\"\nnamespace a {\nb::bar b;\n}",
+            runIncludeFixer("namespace a {\nb::bar b;\n}"));
+  EXPECT_EQ("#include \"bar.h\"\nnamespace A {\na::b::bar b;\n}",
+            runIncludeFixer("namespace A {\na::b::bar b;\n}"));
+  EXPECT_EQ("#include \"bar.h\"\nnamespace a {\nvoid func() { b::bar b; }\n}",
+            runIncludeFixer("namespace a {\nvoid func() { b::bar b; }\n}"));
+  EXPECT_EQ("namespace A { c::b::bar b; }\n",
+            runIncludeFixer("namespace A { c::b::bar b; }\n"));
+  // FIXME: The header should not be added here. Remove this after we support
+  // full match.
+  EXPECT_EQ("#include \"bar.h\"\nnamespace A {\na::b::bar b;\n}",
+            runIncludeFixer("namespace A {\nb::bar b;\n}"));
+
+  // Finds candidates for "str::StrCat".
+  EXPECT_EQ("#include \"strcat.h\"\nnamespace foo2 {\nstr::StrCat b;\n}",
+            runIncludeFixer("namespace foo2 {\nstr::StrCat b;\n}"));
+  // str::StrCat2 doesn't exist.
+  // In these two cases, StrCat2 is a nested class of class str.
+  EXPECT_EQ("#include \"str.h\"\nnamespace foo2 {\nstr::StrCat2 b;\n}",
+            runIncludeFixer("namespace foo2 {\nstr::StrCat2 b;\n}"));
+  EXPECT_EQ("#include \"str.h\"\nnamespace ns {\nstr::StrCat2 b;\n}",
+            runIncludeFixer("namespace ns {\nstr::StrCat2 b;\n}"));
+}
+
+TEST(IncludeFixer, EnumConstantSymbols) {
+  EXPECT_EQ("#include \"color.h\"\nint test = a::b::Green;\n",
+            runIncludeFixer("int test = a::b::Green;\n"));
+}
+
+TEST(IncludeFixer, IgnoreSymbolFromHeader) {
+  std::string Code = "#include \"header.h\"";
+  EXPECT_EQ(Code, runIncludeFixer(Code));
+}
+
+// FIXME: add test cases for inserting and sorting multiple headers when
+// include-fixer supports multiple headers insertion.
+TEST(IncludeFixer, InsertAndSortSingleHeader) {
+  // Insert one header.
+  std::string Code = "#include \"a.h\"\n"
+                     "#include \"foo.h\"\n"
+                     "\n"
+                     "namespace a {\nb::bar b;\n}\n";
+  std::string Expected = "#include \"a.h\"\n"
+                         "#include \"bar.h\"\n"
+                         "#include \"foo.h\"\n"
+                         "\n"
+                         "namespace a {\nb::bar b;\n}\n";
+  EXPECT_EQ(Expected, runIncludeFixer(Code));
+}
+
+TEST(IncludeFixer, DoNotDeleteMatchedSymbol) {
+  EXPECT_EQ("#include \"Vector.h\"\na::Vector v;",
+            runIncludeFixer("a::Vector v;"));
+}
+
+TEST(IncludeFixer, FixNamespaceQualifiers) {
+  EXPECT_EQ("#include \"bar.h\"\na::b::bar b;\n",
+            runIncludeFixer("b::bar b;\n"));
+  EXPECT_EQ("#include \"bar.h\"\na::b::bar b;\n",
+            runIncludeFixer("a::b::bar b;\n"));
+  EXPECT_EQ("#include \"bar.h\"\na::b::bar b;\n",
+            runIncludeFixer("bar b;\n"));
+  EXPECT_EQ("#include \"bar.h\"\nnamespace a {\nb::bar b;\n}\n",
+            runIncludeFixer("namespace a {\nb::bar b;\n}\n"));
+  EXPECT_EQ("#include \"bar.h\"\nnamespace a {\nb::bar b;\n}\n",
+            runIncludeFixer("namespace a {\nbar b;\n}\n"));
+  EXPECT_EQ("#include \"bar.h\"\nnamespace a {\nnamespace b{\nbar b;\n}\n}\n",
+            runIncludeFixer("namespace a {\nnamespace b{\nbar b;\n}\n}\n"));
+  EXPECT_EQ("c::b::bar b;\n",
+            runIncludeFixer("c::b::bar b;\n"));
+  EXPECT_EQ("#include \"bar.h\"\nnamespace d {\na::b::bar b;\n}\n",
+            runIncludeFixer("namespace d {\nbar b;\n}\n"));
+  EXPECT_EQ("#include \"bar2.h\"\nnamespace c {\na::c::bar b;\n}\n",
+            runIncludeFixer("namespace c {\nbar b;\n}\n"));
+
+  // Test common qualifers reduction.
+  EXPECT_EQ(
+      "#include \"bar.h\"\nnamespace a {\nnamespace d {\nb::bar b;\n}\n}\n",
+      runIncludeFixer("namespace a {\nnamespace d {\nbar b;\n}\n}\n"));
+  EXPECT_EQ(
+      "#include \"bar.h\"\nnamespace d {\nnamespace a {\na::b::bar b;\n}\n}\n",
+      runIncludeFixer("namespace d {\nnamespace a {\nbar b;\n}\n}\n"));
+
+  // Test nested classes.
+  EXPECT_EQ("#include \"bar.h\"\nnamespace d {\na::b::bar::t b;\n}\n",
+            runIncludeFixer("namespace d {\nbar::t b;\n}\n"));
+  EXPECT_EQ("#include \"bar.h\"\nnamespace c {\na::b::bar::t b;\n}\n",
+            runIncludeFixer("namespace c {\nbar::t b;\n}\n"));
+  EXPECT_EQ("#include \"bar.h\"\nnamespace a {\nb::bar::t b;\n}\n",
+            runIncludeFixer("namespace a {\nbar::t b;\n}\n"));
+
+  EXPECT_EQ("#include \"color.h\"\nint test = a::b::Green;\n",
+            runIncludeFixer("int test = Green;\n"));
+  EXPECT_EQ("#include \"color.h\"\nnamespace d {\nint test = a::b::Green;\n}\n",
+            runIncludeFixer("namespace d {\nint test = Green;\n}\n"));
+  EXPECT_EQ("#include \"color.h\"\nnamespace a {\nint test = b::Green;\n}\n",
+            runIncludeFixer("namespace a {\nint test = Green;\n}\n"));
+
+  // Test global scope operator.
+  EXPECT_EQ("#include \"bar.h\"\n::a::b::bar b;\n",
+            runIncludeFixer("::a::b::bar b;\n"));
+  EXPECT_EQ("#include \"bar.h\"\nnamespace a {\n::a::b::bar b;\n}\n",
+            runIncludeFixer("namespace a {\n::a::b::bar b;\n}\n"));
+}
+
+TEST(IncludeFixer, FixNamespaceQualifiersForAllInstances) {
+  const char TestCode[] = R"(
+namespace a {
+bar b;
+int func1() {
+  bar a;
+                                                             bar *p = new bar();
+  return 0;
+}
+} // namespace a
+
+namespace a {
+bar func2() {
+  bar f;
+  return f;
+}
+} // namespace a
+
+// Non-fixed cases:
+void f() {
+  bar b;
+}
+
+namespace a {
+namespace c {
+  bar b;
+} // namespace c
+} // namespace a
+)";
+
+  const char ExpectedCode[] = R"(
+#include "bar.h"
+namespace a {
+b::bar b;
+int func1() {
+  b::bar a;
+  b::bar *p = new b::bar();
+  return 0;
+}
+} // namespace a
+
+namespace a {
+b::bar func2() {
+  b::bar f;
+  return f;
+}
+} // namespace a
+
+// Non-fixed cases:
+void f() {
+  bar b;
+}
+
+namespace a {
+namespace c {
+  bar b;
+} // namespace c
+} // namespace a
+)";
+
+  EXPECT_EQ(ExpectedCode, runIncludeFixer(TestCode));
+}
+
+TEST(IncludeFixer, DontAddQualifiersForMissingCompleteType) {
+  EXPECT_EQ("#include \"bar.h\"\nclass bar;\nvoid f() {\nbar* b;\nb->f();\n}",
+            runIncludeFixer("class bar;\nvoid f() {\nbar* b;\nb->f();\n}"));
+}
+
+} // namespace
+} // namespace include_fixer
+} // namespace clang
diff --git a/unittests/include-fixer/find-all-symbols/CMakeLists.txt b/unittests/include-fixer/find-all-symbols/CMakeLists.txt
new file mode 100644 (file)
index 0000000..5cd29d0
--- /dev/null
@@ -0,0 +1,23 @@
+set(LLVM_LINK_COMPONENTS
+  support
+  )
+
+get_filename_component(INCLUDE_FIXER_SOURCE_DIR
+  ${CMAKE_CURRENT_SOURCE_DIR}/../../../include-fixer/find-all-symbols REALPATH)
+include_directories(
+  ${INCLUDE_FIXER_SOURCE_DIR}
+  )
+
+add_extra_unittest(FindAllSymbolsTests
+  FindAllSymbolsTests.cpp
+  )
+
+target_link_libraries(FindAllSymbolsTests
+  clangAST
+  clangASTMatchers
+  clangBasic
+  clangFrontend
+  clangLex
+  clangTooling
+  findAllSymbols
+  )
diff --git a/unittests/include-fixer/find-all-symbols/FindAllSymbolsTests.cpp b/unittests/include-fixer/find-all-symbols/FindAllSymbolsTests.cpp
new file mode 100644 (file)
index 0000000..760a253
--- /dev/null
@@ -0,0 +1,578 @@
+//===-- FindAllSymbolsTests.cpp - find all symbols unit tests ---*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "FindAllSymbolsAction.h"
+#include "HeaderMapCollector.h"
+#include "SymbolInfo.h"
+#include "SymbolReporter.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Basic/FileSystemOptions.h"
+#include "clang/Basic/VirtualFileSystem.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/PCHContainerOperations.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/IntrusiveRefCntPtr.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "gtest/gtest.h"
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace clang {
+namespace find_all_symbols {
+
+static const char HeaderName[] = "symbols.h";
+
+class TestSymbolReporter : public SymbolReporter {
+public:
+  ~TestSymbolReporter() override {}
+
+  void reportSymbols(llvm::StringRef FileName,
+                     const SymbolInfo::SignalMap &NewSymbols) override {
+    for (const auto &Entry : NewSymbols)
+      Symbols[Entry.first] += Entry.second;
+  }
+
+  int seen(const SymbolInfo &Symbol) const {
+    auto it = Symbols.find(Symbol);
+    return it == Symbols.end() ? 0 : it->second.Seen;
+  }
+
+  int used(const SymbolInfo &Symbol) const {
+    auto it = Symbols.find(Symbol);
+    return it == Symbols.end() ? 0 : it->second.Used;
+  }
+
+private:
+  SymbolInfo::SignalMap Symbols;
+};
+
+class FindAllSymbolsTest : public ::testing::Test {
+public:
+  int seen(const SymbolInfo &Symbol) { return Reporter.seen(Symbol); }
+
+  int used(const SymbolInfo &Symbol) { return Reporter.used(Symbol); }
+
+  bool runFindAllSymbols(StringRef HeaderCode, StringRef MainCode) {
+    llvm::IntrusiveRefCntPtr<vfs::InMemoryFileSystem> InMemoryFileSystem(
+        new vfs::InMemoryFileSystem);
+    llvm::IntrusiveRefCntPtr<FileManager> Files(
+        new FileManager(FileSystemOptions(), InMemoryFileSystem));
+
+    std::string FileName = "symbol.cc";
+
+    const std::string InternalHeader = "internal/internal_header.h";
+    const std::string TopHeader = "<top>";
+    // Test .inc header path. The header for `IncHeaderClass` should be
+    // internal.h, which will eventually be mapped to <top>.
+    std::string IncHeader = "internal/private.inc";
+    std::string IncHeaderCode = "class IncHeaderClass {};";
+
+    HeaderMapCollector::RegexHeaderMap RegexMap = {
+        {R"(internal_.*\.h$)", TopHeader.c_str()},
+    };
+
+    std::string InternalCode =
+        "#include \"private.inc\"\nclass Internal {};";
+    SymbolInfo InternalSymbol("Internal", SymbolInfo::SymbolKind::Class,
+                              TopHeader, {});
+    SymbolInfo IncSymbol("IncHeaderClass", SymbolInfo::SymbolKind::Class,
+                         TopHeader, {});
+    InMemoryFileSystem->addFile(
+        IncHeader, 0, llvm::MemoryBuffer::getMemBuffer(IncHeaderCode));
+    InMemoryFileSystem->addFile(InternalHeader, 0,
+                                llvm::MemoryBuffer::getMemBuffer(InternalCode));
+
+    std::unique_ptr<tooling::FrontendActionFactory> Factory(
+        new FindAllSymbolsActionFactory(&Reporter, &RegexMap));
+
+    tooling::ToolInvocation Invocation(
+        {std::string("find_all_symbols"), std::string("-fsyntax-only"),
+         std::string("-std=c++11"), FileName},
+        Factory->create(), Files.get(),
+        std::make_shared<PCHContainerOperations>());
+
+    InMemoryFileSystem->addFile(HeaderName, 0,
+                                llvm::MemoryBuffer::getMemBuffer(HeaderCode));
+
+    std::string Content = "#include\"" + std::string(HeaderName) +
+                          "\"\n"
+                          "#include \"" +
+                          InternalHeader + "\"";
+#if !defined(_MSC_VER) && !defined(__MINGW32__)
+    // Test path cleaning for both decls and macros.
+    const std::string DirtyHeader = "./internal/./a/b.h";
+    Content += "\n#include \"" + DirtyHeader + "\"";
+    const std::string CleanHeader = "internal/a/b.h";
+    const std::string DirtyHeaderContent =
+        "#define INTERNAL 1\nclass ExtraInternal {};";
+    InMemoryFileSystem->addFile(
+        DirtyHeader, 0, llvm::MemoryBuffer::getMemBuffer(DirtyHeaderContent));
+    SymbolInfo DirtyMacro("INTERNAL", SymbolInfo::SymbolKind::Macro,
+                          CleanHeader, {});
+    SymbolInfo DirtySymbol("ExtraInternal", SymbolInfo::SymbolKind::Class,
+                           CleanHeader, {});
+#endif // _MSC_VER && __MINGW32__
+    Content += "\n" + MainCode.str();
+    InMemoryFileSystem->addFile(FileName, 0,
+                                llvm::MemoryBuffer::getMemBuffer(Content));
+    Invocation.run();
+    EXPECT_EQ(1, seen(InternalSymbol));
+    EXPECT_EQ(1, seen(IncSymbol));
+#if !defined(_MSC_VER) && !defined(__MINGW32__)
+    EXPECT_EQ(1, seen(DirtySymbol));
+    EXPECT_EQ(1, seen(DirtyMacro));
+#endif  // _MSC_VER && __MINGW32__
+    return true;
+  }
+
+protected:
+  TestSymbolReporter Reporter;
+};
+
+TEST_F(FindAllSymbolsTest, VariableSymbols) {
+  static const char Header[] = R"(
+      extern int xargc;
+      namespace na {
+      static bool SSSS = false;
+      namespace nb { const long long *XXXX; }
+      })";
+  static const char Main[] = R"(
+      auto y = &na::nb::XXXX;
+      int main() { if (na::SSSS) return xargc; }
+  )";
+  runFindAllSymbols(Header, Main);
+
+  SymbolInfo Symbol =
+      SymbolInfo("xargc", SymbolInfo::SymbolKind::Variable, HeaderName, {});
+  EXPECT_EQ(1, seen(Symbol));
+  EXPECT_EQ(1, used(Symbol));
+
+  Symbol = SymbolInfo("SSSS", SymbolInfo::SymbolKind::Variable, HeaderName,
+                      {{SymbolInfo::ContextType::Namespace, "na"}});
+  EXPECT_EQ(1, seen(Symbol));
+  EXPECT_EQ(1, used(Symbol));
+
+  Symbol = SymbolInfo("XXXX", SymbolInfo::SymbolKind::Variable, HeaderName,
+                      {{SymbolInfo::ContextType::Namespace, "nb"},
+                       {SymbolInfo::ContextType::Namespace, "na"}});
+  EXPECT_EQ(1, seen(Symbol));
+  EXPECT_EQ(1, used(Symbol));
+}
+
+TEST_F(FindAllSymbolsTest, ExternCSymbols) {
+  static const char Header[] = R"(
+      extern "C" {
+      int C_Func() { return 0; }
+      struct C_struct {
+        int Member;
+      };
+      })";
+  static const char Main[] = R"(
+      C_struct q() {
+        int(*ptr)() = C_Func;
+        return {0};
+      }
+  )";
+  runFindAllSymbols(Header, Main);
+
+  SymbolInfo Symbol =
+      SymbolInfo("C_Func", SymbolInfo::SymbolKind::Function, HeaderName, {});
+  EXPECT_EQ(1, seen(Symbol));
+  EXPECT_EQ(1, used(Symbol));
+
+  Symbol =
+      SymbolInfo("C_struct", SymbolInfo::SymbolKind::Class, HeaderName, {});
+  EXPECT_EQ(1, seen(Symbol));
+  EXPECT_EQ(1, used(Symbol));
+}
+
+TEST_F(FindAllSymbolsTest, CXXRecordSymbols) {
+  static const char Header[] = R"(
+      struct Glob {};
+      struct A; // Not a defintion, ignored.
+      class NOP; // Not a defintion, ignored
+      namespace na {
+      struct A {
+        struct AAAA {};
+        int x;
+        int y;
+        void f() {}
+      };
+      };  //
+      )";
+  static const char Main[] = R"(
+      static Glob glob;
+      static na::A::AAAA* a;
+  )";
+  runFindAllSymbols(Header, Main);
+
+  SymbolInfo Symbol =
+      SymbolInfo("Glob", SymbolInfo::SymbolKind::Class, HeaderName, {});
+  EXPECT_EQ(1, seen(Symbol));
+  EXPECT_EQ(1, used(Symbol));
+
+  Symbol = SymbolInfo("A", SymbolInfo::SymbolKind::Class, HeaderName,
+                      {{SymbolInfo::ContextType::Namespace, "na"}});
+  EXPECT_EQ(1, seen(Symbol));
+  EXPECT_EQ(1, used(Symbol));
+
+  Symbol = SymbolInfo("AAA", SymbolInfo::SymbolKind::Class, HeaderName,
+                      {{SymbolInfo::ContextType::Record, "A"},
+                       {SymbolInfo::ContextType::Namespace, "na"}});
+  EXPECT_EQ(0, seen(Symbol));
+  EXPECT_EQ(0, used(Symbol));
+}
+
+TEST_F(FindAllSymbolsTest, CXXRecordSymbolsTemplate) {
+  static const char Header[] = R"(
+      template <typename T>
+      struct T_TEMP {
+        template <typename _Tp1>
+        struct rebind { typedef T_TEMP<_Tp1> other; };
+      };
+      // Ignore specialization.
+      template class T_TEMP<char>;
+
+      template <typename T>
+      class Observer {
+      };
+      // Ignore specialization.
+      template <> class Observer<int> {};
+      )";
+  static const char Main[] = R"(
+      extern T_TEMP<int>::rebind<char> weirdo;
+  )";
+  runFindAllSymbols(Header, Main);
+
+  SymbolInfo Symbol =
+      SymbolInfo("T_TEMP", SymbolInfo::SymbolKind::Class, HeaderName, {});
+  EXPECT_EQ(1, seen(Symbol));
+  EXPECT_EQ(1, used(Symbol));
+}
+
+TEST_F(FindAllSymbolsTest, DontIgnoreTemplatePartialSpecialization) {
+  static const char Code[] = R"(
+      template<class> class Class; // undefined
+      template<class R, class... ArgTypes>
+      class Class<R(ArgTypes...)> {
+      };
+
+      template<class T> void f() {};
+      template<> void f<int>() {};
+      )";
+  runFindAllSymbols(Code, "");
+  SymbolInfo Symbol =
+      SymbolInfo("Class", SymbolInfo::SymbolKind::Class, HeaderName, {});
+  EXPECT_EQ(1, seen(Symbol));
+  Symbol = SymbolInfo("f", SymbolInfo::SymbolKind::Function, HeaderName, {});
+  EXPECT_EQ(1, seen(Symbol));
+}
+
+TEST_F(FindAllSymbolsTest, FunctionSymbols) {
+  static const char Header[] = R"(
+      namespace na {
+      int gg(int);
+      int f(const int &a) { int Local; static int StaticLocal; return 0; }
+      static void SSSFFF() {}
+      }  // namespace na
+      namespace na {
+      namespace nb {
+      template<typename T>
+      void fun(T t) {};
+      } // namespace nb
+      } // namespace na";
+      )";
+  static const char Main[] = R"(
+      int(*gg)(int) = &na::gg;
+      int main() {
+        (void)na::SSSFFF;
+        na::nb::fun(0);
+        return na::f(gg(0));
+      }
+  )";
+  runFindAllSymbols(Header, Main);
+
+  SymbolInfo Symbol =
+      SymbolInfo("gg", SymbolInfo::SymbolKind::Function, HeaderName,
+                 {{SymbolInfo::ContextType::Namespace, "na"}});
+  EXPECT_EQ(1, seen(Symbol));
+  EXPECT_EQ(1, used(Symbol));
+
+  Symbol = SymbolInfo("f", SymbolInfo::SymbolKind::Function, HeaderName,
+                      {{SymbolInfo::ContextType::Namespace, "na"}});
+  EXPECT_EQ(1, seen(Symbol));
+  EXPECT_EQ(1, used(Symbol));
+
+  Symbol = SymbolInfo("SSSFFF", SymbolInfo::SymbolKind::Function, HeaderName,
+                      {{SymbolInfo::ContextType::Namespace, "na"}});
+  EXPECT_EQ(1, seen(Symbol));
+  EXPECT_EQ(1, used(Symbol));
+
+  Symbol = SymbolInfo("fun", SymbolInfo::SymbolKind::Function, HeaderName,
+                      {{SymbolInfo::ContextType::Namespace, "nb"},
+                       {SymbolInfo::ContextType::Namespace, "na"}});
+  EXPECT_EQ(1, seen(Symbol));
+  EXPECT_EQ(1, used(Symbol));
+}
+
+TEST_F(FindAllSymbolsTest, NamespaceTest) {
+  static const char Header[] = R"(
+      int X1;
+      namespace { int X2; }
+      namespace { namespace { int X3; } }
+      namespace { namespace nb { int X4; } }
+      namespace na { inline namespace __1 { int X5; } }
+      )";
+  static const char Main[] = R"(
+      using namespace nb;
+      int main() {
+        X1 = X2;
+        X3 = X4;
+        (void)na::X5;
+      }
+  )";
+  runFindAllSymbols(Header, Main);
+
+  SymbolInfo Symbol =
+      SymbolInfo("X1", SymbolInfo::SymbolKind::Variable, HeaderName, {});
+  EXPECT_EQ(1, seen(Symbol));
+  EXPECT_EQ(1, used(Symbol));
+
+  Symbol = SymbolInfo("X2", SymbolInfo::SymbolKind::Variable, HeaderName,
+                      {{SymbolInfo::ContextType::Namespace, ""}});
+  EXPECT_EQ(1, seen(Symbol));
+  EXPECT_EQ(1, used(Symbol));
+
+  Symbol = SymbolInfo("X3", SymbolInfo::SymbolKind::Variable, HeaderName,
+                      {{SymbolInfo::ContextType::Namespace, ""},
+                       {SymbolInfo::ContextType::Namespace, ""}});
+  EXPECT_EQ(1, seen(Symbol));
+  EXPECT_EQ(1, used(Symbol));
+
+  Symbol = SymbolInfo("X4", SymbolInfo::SymbolKind::Variable, HeaderName,
+                      {{SymbolInfo::ContextType::Namespace, "nb"},
+                       {SymbolInfo::ContextType::Namespace, ""}});
+  EXPECT_EQ(1, seen(Symbol));
+  EXPECT_EQ(1, used(Symbol));
+
+  Symbol = SymbolInfo("X5", SymbolInfo::SymbolKind::Variable, HeaderName,
+                      {{SymbolInfo::ContextType::Namespace, "na"}});
+  EXPECT_EQ(1, seen(Symbol));
+  EXPECT_EQ(1, used(Symbol));
+}
+
+TEST_F(FindAllSymbolsTest, DecayedTypeTest) {
+  static const char Header[] = "void DecayedFunc(int x[], int y[10]) {}";
+  static const char Main[] = R"(int main() { DecayedFunc(nullptr, nullptr); })";
+  runFindAllSymbols(Header, Main);
+  SymbolInfo Symbol = SymbolInfo(
+      "DecayedFunc", SymbolInfo::SymbolKind::Function, HeaderName, {});
+  EXPECT_EQ(1, seen(Symbol));
+  EXPECT_EQ(1, used(Symbol));
+}
+
+TEST_F(FindAllSymbolsTest, CTypedefTest) {
+  static const char Header[] = R"(
+      typedef unsigned size_t_;
+      typedef struct { int x; } X;
+      using XX = X;
+      )";
+  static const char Main[] = R"(
+      size_t_ f;
+      template<typename T> struct vector{};
+      vector<X> list;
+      void foo(const XX&){}
+  )";
+  runFindAllSymbols(Header, Main);
+
+  SymbolInfo Symbol = SymbolInfo("size_t_", SymbolInfo::SymbolKind::TypedefName,
+                                 HeaderName, {});
+  EXPECT_EQ(1, seen(Symbol));
+  EXPECT_EQ(1, used(Symbol));
+
+  Symbol = SymbolInfo("X", SymbolInfo::SymbolKind::TypedefName, HeaderName, {});
+  EXPECT_EQ(1, seen(Symbol));
+  EXPECT_EQ(1, used(Symbol));
+
+  Symbol =
+      SymbolInfo("XX", SymbolInfo::SymbolKind::TypedefName, HeaderName, {});
+  EXPECT_EQ(1, seen(Symbol));
+  EXPECT_EQ(1, used(Symbol));
+}
+
+TEST_F(FindAllSymbolsTest, EnumTest) {
+  static const char Header[] = R"(
+      enum Glob_E { G1, G2 };
+      enum class Altitude { high='h', low='l'};
+      enum { A1, A2 };
+      class A {
+      public:
+        enum A_ENUM { X1, X2 };
+      };
+      enum DECL : int;
+      )";
+  static const char Main[] = R"(
+      static auto flags = G1 | G2;
+      static auto alt = Altitude::high;
+      static auto nested = A::X1;
+      extern DECL whatever;
+      static auto flags2 = A1 | A2;
+  )";
+  runFindAllSymbols(Header, Main);
+
+  SymbolInfo Symbol =
+      SymbolInfo("Glob_E", SymbolInfo::SymbolKind::EnumDecl, HeaderName, {});
+  EXPECT_EQ(1, seen(Symbol));
+  EXPECT_EQ(0, used(Symbol));
+
+  Symbol =
+      SymbolInfo("G1", SymbolInfo::SymbolKind::EnumConstantDecl, HeaderName,
+                 {{SymbolInfo::ContextType::EnumDecl, "Glob_E"}});
+  EXPECT_EQ(1, seen(Symbol));
+  EXPECT_EQ(1, used(Symbol));
+
+  Symbol =
+      SymbolInfo("G2", SymbolInfo::SymbolKind::EnumConstantDecl, HeaderName,
+                 {{SymbolInfo::ContextType::EnumDecl, "Glob_E"}});
+  EXPECT_EQ(1, seen(Symbol));
+  EXPECT_EQ(1, used(Symbol));
+
+  Symbol =
+      SymbolInfo("Altitude", SymbolInfo::SymbolKind::EnumDecl, HeaderName, {});
+  EXPECT_EQ(1, seen(Symbol));
+  EXPECT_EQ(1, used(Symbol));
+  Symbol =
+      SymbolInfo("high", SymbolInfo::SymbolKind::EnumConstantDecl, HeaderName,
+                 {{SymbolInfo::ContextType::EnumDecl, "Altitude"}});
+  EXPECT_EQ(0, seen(Symbol));
+  EXPECT_EQ(0, used(Symbol));
+
+  Symbol = SymbolInfo("A1", SymbolInfo::SymbolKind::EnumConstantDecl,
+                      HeaderName, {{SymbolInfo::ContextType::EnumDecl, ""}});
+  EXPECT_EQ(1, seen(Symbol));
+  EXPECT_EQ(1, used(Symbol));
+  Symbol = SymbolInfo("A2", SymbolInfo::SymbolKind::EnumConstantDecl,
+                      HeaderName, {{SymbolInfo::ContextType::EnumDecl, ""}});
+  EXPECT_EQ(1, seen(Symbol));
+  EXPECT_EQ(1, used(Symbol));
+  Symbol = SymbolInfo("", SymbolInfo::SymbolKind::EnumDecl, HeaderName, {});
+  EXPECT_EQ(0, seen(Symbol));
+  EXPECT_EQ(0, used(Symbol));
+
+  Symbol = SymbolInfo("A_ENUM", SymbolInfo::SymbolKind::EnumDecl, HeaderName,
+                      {{SymbolInfo::ContextType::Record, "A"}});
+  EXPECT_EQ(0, seen(Symbol));
+  EXPECT_EQ(0, used(Symbol));
+
+  Symbol = SymbolInfo("X1", SymbolInfo::SymbolKind::EnumDecl, HeaderName,
+                      {{SymbolInfo::ContextType::EnumDecl, "A_ENUM"},
+                       {SymbolInfo::ContextType::Record, "A"}});
+  EXPECT_EQ(0, seen(Symbol));
+
+  Symbol = SymbolInfo("DECL", SymbolInfo::SymbolKind::EnumDecl, HeaderName, {});
+  EXPECT_EQ(0, seen(Symbol));
+}
+
+TEST_F(FindAllSymbolsTest, IWYUPrivatePragmaTest) {
+  static const char Header[] = R"(
+    // IWYU pragma: private, include "bar.h"
+    struct Bar {
+    };
+  )";
+  static const char Main[] = R"(
+    Bar bar;
+  )";
+  runFindAllSymbols(Header, Main);
+
+  SymbolInfo Symbol =
+      SymbolInfo("Bar", SymbolInfo::SymbolKind::Class, "bar.h", {});
+  EXPECT_EQ(1, seen(Symbol));
+  EXPECT_EQ(1, used(Symbol));
+}
+
+TEST_F(FindAllSymbolsTest, MacroTest) {
+  static const char Header[] = R"(
+    #define X
+    #define Y 1
+    #define MAX(X, Y) ((X) > (Y) ? (X) : (Y))
+  )";
+  static const char Main[] = R"(
+    #ifdef X
+    int main() { return MAX(0,Y); }
+    #endif
+  )";
+  runFindAllSymbols(Header, Main);
+  SymbolInfo Symbol =
+      SymbolInfo("X", SymbolInfo::SymbolKind::Macro, HeaderName, {});
+  EXPECT_EQ(1, seen(Symbol));
+  EXPECT_EQ(1, used(Symbol));
+
+  Symbol = SymbolInfo("Y", SymbolInfo::SymbolKind::Macro, HeaderName, {});
+  EXPECT_EQ(1, seen(Symbol));
+  EXPECT_EQ(1, used(Symbol));
+
+  Symbol = SymbolInfo("MAX", SymbolInfo::SymbolKind::Macro, HeaderName, {});
+  EXPECT_EQ(1, seen(Symbol));
+  EXPECT_EQ(1, used(Symbol));
+}
+
+TEST_F(FindAllSymbolsTest, MacroTestWithIWYU) {
+  static const char Header[] = R"(
+    // IWYU pragma: private, include "bar.h"
+    #define X 1
+    #define Y 1
+    #define MAX(X, Y) ((X) > (Y) ? (X) : (Y))
+  )";
+  static const char Main[] = R"(
+    #ifdef X
+    int main() { return MAX(0,Y); }
+    #endif
+  )";
+  runFindAllSymbols(Header, Main);
+  SymbolInfo Symbol =
+      SymbolInfo("X", SymbolInfo::SymbolKind::Macro, "bar.h", {});
+  EXPECT_EQ(1, seen(Symbol));
+  EXPECT_EQ(1, used(Symbol));
+
+  Symbol = SymbolInfo("Y", SymbolInfo::SymbolKind::Macro, "bar.h", {});
+  EXPECT_EQ(1, seen(Symbol));
+  EXPECT_EQ(1, used(Symbol));
+
+  Symbol = SymbolInfo("MAX", SymbolInfo::SymbolKind::Macro, "bar.h", {});
+  EXPECT_EQ(1, seen(Symbol));
+  EXPECT_EQ(1, used(Symbol));
+}
+
+TEST_F(FindAllSymbolsTest, NoFriendTest) {
+  static const char Header[] = R"(
+    class WorstFriend {
+      friend void Friend();
+      friend class BestFriend;
+    };
+  )";
+  runFindAllSymbols(Header, "");
+  SymbolInfo Symbol =
+      SymbolInfo("WorstFriend", SymbolInfo::SymbolKind::Class, HeaderName, {});
+  EXPECT_EQ(1, seen(Symbol));
+
+  Symbol =
+      SymbolInfo("Friend", SymbolInfo::SymbolKind::Function, HeaderName, {});
+  EXPECT_EQ(0, seen(Symbol));
+
+  Symbol =
+      SymbolInfo("BestFriend", SymbolInfo::SymbolKind::Class, HeaderName, {});
+  EXPECT_EQ(0, seen(Symbol));
+}
+
+} // namespace find_all_symbols
+} // namespace clang
diff --git a/unittests/include/common/VirtualFileHelper.h b/unittests/include/common/VirtualFileHelper.h
new file mode 100644 (file)
index 0000000..5fa4d53
--- /dev/null
@@ -0,0 +1,82 @@
+//===--- VirtualFileHelper.h ------------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \brief This file defines an utility class for tests that needs a source
+/// manager for a virtual file with customizable content.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef CLANG_MODERNIZE_VIRTUAL_FILE_HELPER_H
+#define CLANG_MODERNIZE_VIRTUAL_FILE_HELPER_H
+
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/DiagnosticOptions.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Frontend/TextDiagnosticPrinter.h"
+
+namespace clang {
+
+/// \brief Class that provides easy access to a SourceManager and that allows to
+/// map virtual files conveniently.
+class VirtualFileHelper {
+  struct VirtualFile {
+    std::string FileName;
+    std::string Code;
+  };
+
+public:
+  VirtualFileHelper()
+      : DiagOpts(new DiagnosticOptions()),
+        Diagnostics(IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs),
+                    &*DiagOpts),
+        DiagnosticPrinter(llvm::outs(), &*DiagOpts),
+        Files((FileSystemOptions())) {}
+
+  /// \brief Create a virtual file \p FileName, with content \p Code.
+  void mapFile(llvm::StringRef FileName, llvm::StringRef Code) {
+    VirtualFile VF = { FileName, Code };
+    VirtualFiles.push_back(VF);
+  }
+
+  /// \brief Create a new \c SourceManager with the virtual files and contents
+  /// mapped to it.
+  SourceManager &getNewSourceManager() {
+    Sources.reset(new SourceManager(Diagnostics, Files));
+    mapVirtualFiles(*Sources);
+    return *Sources;
+  }
+
+  /// \brief Map the virtual file contents in the given \c SourceManager.
+  void mapVirtualFiles(SourceManager &SM) const {
+    for (llvm::SmallVectorImpl<VirtualFile>::const_iterator
+             I = VirtualFiles.begin(),
+             E = VirtualFiles.end();
+         I != E; ++I) {
+      std::unique_ptr<llvm::MemoryBuffer> Buf =
+          llvm::MemoryBuffer::getMemBuffer(I->Code);
+      const FileEntry *Entry = SM.getFileManager().getVirtualFile(
+          I->FileName, Buf->getBufferSize(), /*ModificationTime=*/0);
+      SM.overrideFileContents(Entry, std::move(Buf));
+    }
+  }
+
+private:
+  IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts;
+  DiagnosticsEngine Diagnostics;
+  TextDiagnosticPrinter DiagnosticPrinter;
+  FileManager Files;
+  // most tests don't need more than one file
+  llvm::SmallVector<VirtualFile, 1> VirtualFiles;
+  std::unique_ptr<SourceManager> Sources;
+};
+
+} // end namespace clang
+
+#endif // CLANG_MODERNIZE_VIRTUAL_FILE_HELPER_H