Import llvm-toolchain-3.9_3.9.1.orig-clang-tools-extra.tar.bz2
authorSylvestre Ledru <sylvestre@debian.org>
Wed, 14 Dec 2016 08:22:02 +0000 (08:22 +0000)
committerSylvestre Ledru <sylvestre@debian.org>
Wed, 14 Dec 2016 08:22:02 +0000 (08:22 +0000)
[dgit import orig llvm-toolchain-3.9_3.9.1.orig-clang-tools-extra.tar.bz2]

944 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]
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-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-rename/CMakeLists.txt [new file with mode: 0644]
clang-rename/RenamingAction.cpp [new file with mode: 0644]
clang-rename/RenamingAction.h [new file with mode: 0644]
clang-rename/USRFinder.cpp [new file with mode: 0644]
clang-rename/USRFinder.h [new file with mode: 0644]
clang-rename/USRFindingAction.cpp [new file with mode: 0644]
clang-rename/USRFindingAction.h [new file with mode: 0644]
clang-rename/USRLocFinder.cpp [new file with mode: 0644]
clang-rename/USRLocFinder.h [new file with mode: 0644]
clang-rename/tool/CMakeLists.txt [new file with mode: 0644]
clang-rename/tool/ClangRename.cpp [new file with mode: 0644]
clang-rename/tool/clang-rename.py [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/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/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/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/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/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/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/MemsetZeroLengthCheck.cpp [new file with mode: 0644]
clang-tidy/google/MemsetZeroLengthCheck.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/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/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/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/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/PointerAndIntegralOperationCheck.cpp [new file with mode: 0644]
clang-tidy/misc/PointerAndIntegralOperationCheck.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/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/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/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/ShrinkToFitCheck.cpp [new file with mode: 0644]
clang-tidy/modernize/ShrinkToFitCheck.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/UseDefaultCheck.cpp [new file with mode: 0644]
clang-tidy/modernize/UseDefaultCheck.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/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/UseUsingCheck.cpp [new file with mode: 0644]
clang-tidy/modernize/UseUsingCheck.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/PerformanceTidyModule.cpp [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/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/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/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/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/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/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/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]
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/boost-use-to-string.rst [new file with mode: 0644]
docs/clang-tidy/checks/cert-dcl03-c.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-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-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-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-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/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-memset.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/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-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-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-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-pointer-and-integral-operation.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-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-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-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-shrink-to-fit.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.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-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-using.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-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-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-named-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-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/clang-tidy/tools/dump_check_docs.py [new file with mode: 0755]
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/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/tool/CMakeLists.txt [new file with mode: 0644]
include-fixer/tool/ClangIncludeFixer.cpp [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/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-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-rename/ClassFindByName.cpp [new file with mode: 0644]
test/clang-rename/ClassNameInFunctionDefenition.cpp [new file with mode: 0644]
test/clang-rename/ClassReplacements.cpp [new file with mode: 0644]
test/clang-rename/ClassSimpleRenaming.cpp [new file with mode: 0644]
test/clang-rename/ComplicatedClassType.cpp [new file with mode: 0644]
test/clang-rename/ConstCastExpr.cpp [new file with mode: 0644]
test/clang-rename/ConstructExpr.cpp [new file with mode: 0644]
test/clang-rename/CtorFindByDeclaration.cpp [new file with mode: 0644]
test/clang-rename/CtorFindByDefinition.cpp [new file with mode: 0644]
test/clang-rename/CtorInitializer.cpp [new file with mode: 0644]
test/clang-rename/DeclRefExpr.cpp [new file with mode: 0644]
test/clang-rename/DtorDeclaration.cpp [new file with mode: 0644]
test/clang-rename/DtorDefinition.cpp [new file with mode: 0644]
test/clang-rename/DynamicCastExpr.cpp [new file with mode: 0644]
test/clang-rename/Field.cpp [new file with mode: 0644]
test/clang-rename/FunctionMacro.cpp [new file with mode: 0644]
test/clang-rename/MemberExprMacro.cpp [new file with mode: 0644]
test/clang-rename/Namespace.cpp [new file with mode: 0644]
test/clang-rename/NoNewName.cpp [new file with mode: 0644]
test/clang-rename/ReinterpretCastExpr.cpp [new file with mode: 0644]
test/clang-rename/StaticCastExpr.cpp [new file with mode: 0644]
test/clang-rename/TemplateTypename.cpp [new file with mode: 0644]
test/clang-rename/UserDefinedConversion.cpp [new file with mode: 0644]
test/clang-rename/UserDefinedConversionFindByTypeDeclaration.cpp [new file with mode: 0644]
test/clang-rename/Variable.cpp [new file with mode: 0644]
test/clang-rename/VariableMacro.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/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/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.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-use-auto/containers.h [new file with mode: 0644]
test/clang-tidy/Inputs/overlapping/o.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/basic.cpp [new file with mode: 0644]
test/clang-tidy/boost-use-to-string.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-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/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-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/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/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-memset-zero-length.cpp [new file with mode: 0644]
test/clang-tidy/google-runtime-references.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.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.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-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-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.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-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-pointer-and-integral-operation-cxx98.cpp [new file with mode: 0644]
test/clang-tidy/misc-pointer-and-integral-operation.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-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-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.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.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.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.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-shrink-to-fit.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.cpp [new file with mode: 0644]
test/clang-tidy/modernize-use-default-copy.cpp [new file with mode: 0644]
test/clang-tidy/modernize-use-default-delayed.cpp [new file with mode: 0644]
test/clang-tidy/modernize-use-default.cpp [new file with mode: 0644]
test/clang-tidy/modernize-use-emplace.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-using.cpp [new file with mode: 0644]
test/clang-tidy/nolint.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-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.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-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-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-named-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-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/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/prefix_variable.cpp [new file with mode: 0644]
test/include-fixer/ranking.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/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/clang-apply-replacements/CMakeLists.txt [new file with mode: 0644]
unittests/clang-apply-replacements/ReformattingTest.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-rename/CMakeLists.txt [new file with mode: 0644]
unittests/clang-rename/USRLocFindingTest.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/OverlappingReplacementsTest.cpp [new file with mode: 0644]
unittests/clang-tidy/ReadabilityModuleTest.cpp [new file with mode: 0644]
unittests/include-fixer/CMakeLists.txt [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..171e9ee
--- /dev/null
@@ -0,0 +1,25 @@
+add_subdirectory(clang-apply-replacements)
+add_subdirectory(clang-rename)
+add_subdirectory(modularize)
+if(CLANG_ENABLE_STATIC_ANALYZER)
+add_subdirectory(clang-tidy)
+endif()
+
+add_subdirectory(clang-query)
+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..3f4fa1f
--- /dev/null
@@ -0,0 +1,62 @@
+==============================================================================
+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
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/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..bab1927
--- /dev/null
@@ -0,0 +1,140 @@
+//===-- 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/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 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.
+/// \param[out] TURFiles 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 &TURFiles,
+                                 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 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);
+
+/// \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..4603212
--- /dev/null
@@ -0,0 +1,260 @@
+//===-- 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/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 & TURFiles,
+                                 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;
+
+    TURFiles.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;
+}
+
+/// \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";
+    }
+  }
+}
+
+/// \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;
+    tooling::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 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 (!tooling::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 (Rewriter::const_buffer_iterator BufferI = Rewrites.buffer_begin(),
+                                       BufferE = Rewrites.buffer_end();
+       BufferI != BufferE; ++BufferI) {
+    const char *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..5bc06bb
--- /dev/null
@@ -0,0 +1,279 @@
+//===-- 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 files (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 (!tooling::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)
+    FormatStyle = format::getStyle(FormatStyleOpt, FormatStyleConfig, "LLVM");
+
+  TUReplacements TUs;
+  TUReplacementFiles TURFiles;
+
+  std::error_code ErrorCode =
+      collectReplacementsFromDirectory(Directory, TUs, TURFiles, 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(TURFiles, Diagnostics));
+
+  FileManager Files((FileSystemOptions()));
+  SourceManager SM(Diagnostics, Files);
+
+  FileToReplacementsMap GroupedReplacements;
+  if (!mergeAndDeduplicate(TUs, 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;
+    const char *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-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..74eb6ea
--- /dev/null
@@ -0,0 +1,150 @@
+//===---- 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 (std::vector<BoundNodes>::iterator MI = Matches.begin(),
+                                           ME = Matches.end();
+         MI != ME; ++MI) {
+      OS << "\nMatch #" << ++MatchCount << ":\n\n";
+
+      for (BoundNodes::IDToNodeMap::const_iterator 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(
+                R.getBegin(), DiagnosticsEngine::Note,
+                "\"" + BI->first + "\" binds here",
+                CharSourceRange::getTokenRange(R),
+                None, &AST->getSourceManager());
+          }
+          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..109336a
--- /dev/null
@@ -0,0 +1,140 @@
+//===--- 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..e25b697
--- /dev/null
@@ -0,0 +1,286 @@
+//===---- 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 (std::vector<MatcherCompletion>::iterator 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..a43f84d
--- /dev/null
@@ -0,0 +1,72 @@
+//===--- 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..e4abcbb
--- /dev/null
@@ -0,0 +1,120 @@
+//===---- 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 (cl::list<std::string>::iterator I = Commands.begin(),
+                                         E = Commands.end();
+         I != E; ++I) {
+      QueryRef Q = QueryParser::parse(I->c_str(), QS);
+      if (!Q->run(llvm::outs(), QS))
+        return 1;
+    }
+  } else if (!CommandFiles.empty()) {
+    for (cl::list<std::string>::iterator 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.c_str(), 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-rename/CMakeLists.txt b/clang-rename/CMakeLists.txt
new file mode 100644 (file)
index 0000000..1513c36
--- /dev/null
@@ -0,0 +1,17 @@
+set(LLVM_LINK_COMPONENTS support)
+
+add_clang_library(clangRename
+  USRFinder.cpp
+  USRFindingAction.cpp
+  USRLocFinder.cpp
+  RenamingAction.cpp
+
+  LINK_LIBS
+  clangAST
+  clangBasic
+  clangIndex
+  clangLex
+  clangToolingCore
+  )
+
+add_subdirectory(tool)
diff --git a/clang-rename/RenamingAction.cpp b/clang-rename/RenamingAction.cpp
new file mode 100644 (file)
index 0000000..720190f
--- /dev/null
@@ -0,0 +1,88 @@
+//===--- tools/extra/clang-rename/RenamingAction.cpp - Clang rename tool --===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Provides an action to rename every symbol at a point.
+///
+//===----------------------------------------------------------------------===//
+
+#include "RenamingAction.h"
+#include "USRLocFinder.h"
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendAction.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Tooling/CommonOptionsParser.h"
+#include "clang/Tooling/Refactoring.h"
+#include "clang/Tooling/Tooling.h"
+#include <string>
+#include <vector>
+
+using namespace llvm;
+
+namespace clang {
+namespace rename {
+
+class RenamingASTConsumer : public ASTConsumer {
+public:
+  RenamingASTConsumer(const std::string &NewName,
+                      const std::string &PrevName,
+                      const std::vector<std::string> &USRs,
+                      tooling::Replacements &Replaces,
+                      bool PrintLocations)
+      : NewName(NewName), PrevName(PrevName), USRs(USRs), Replaces(Replaces),
+        PrintLocations(PrintLocations) {
+  }
+
+  void HandleTranslationUnit(ASTContext &Context) override {
+    const auto &SourceMgr = Context.getSourceManager();
+    std::vector<SourceLocation> RenamingCandidates;
+    std::vector<SourceLocation> NewCandidates;
+
+    for (const auto &USR : USRs) {
+      NewCandidates = getLocationsOfUSR(USR, PrevName,
+                                        Context.getTranslationUnitDecl());
+      RenamingCandidates.insert(RenamingCandidates.end(), NewCandidates.begin(),
+                                NewCandidates.end());
+      NewCandidates.clear();
+    }
+
+    auto PrevNameLen = PrevName.length();
+    if (PrintLocations)
+      for (const auto &Loc : RenamingCandidates) {
+        FullSourceLoc FullLoc(Loc, SourceMgr);
+        errs() << "clang-rename: renamed at: " << SourceMgr.getFilename(Loc)
+               << ":" << FullLoc.getSpellingLineNumber() << ":"
+               << FullLoc.getSpellingColumnNumber() << "\n";
+        Replaces.insert(tooling::Replacement(SourceMgr, Loc, PrevNameLen,
+                                             NewName));
+      }
+    else
+      for (const auto &Loc : RenamingCandidates)
+        Replaces.insert(tooling::Replacement(SourceMgr, Loc, PrevNameLen,
+                                             NewName));
+  }
+
+private:
+  const std::string &NewName, &PrevName;
+  const std::vector<std::string> &USRs;
+  tooling::Replacements &Replaces;
+  bool PrintLocations;
+};
+
+std::unique_ptr<ASTConsumer> RenamingAction::newASTConsumer() {
+  return llvm::make_unique<RenamingASTConsumer>(NewName, PrevName, USRs,
+                                                Replaces, PrintLocations);
+}
+
+} // namespace rename
+} // namespace clang
diff --git a/clang-rename/RenamingAction.h b/clang-rename/RenamingAction.h
new file mode 100644 (file)
index 0000000..d52f21d
--- /dev/null
@@ -0,0 +1,47 @@
+//===--- tools/extra/clang-rename/RenamingAction.h - Clang rename tool ----===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Provides an action to rename every symbol at a point.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_RENAMING_ACTION_H_
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_RENAMING_ACTION_H_
+
+#include "clang/Tooling/Refactoring.h"
+
+namespace clang {
+class ASTConsumer;
+class CompilerInstance;
+
+namespace rename {
+
+class RenamingAction {
+public:
+  RenamingAction(const std::string &NewName, const std::string &PrevName,
+                 const std::vector<std::string> &USRs,
+                 tooling::Replacements &Replaces, bool PrintLocations = false)
+      : NewName(NewName), PrevName(PrevName), USRs(USRs), Replaces(Replaces),
+        PrintLocations(PrintLocations) {
+  }
+
+  std::unique_ptr<ASTConsumer> newASTConsumer();
+
+private:
+  const std::string &NewName, &PrevName;
+  const std::vector<std::string> &USRs;
+  tooling::Replacements &Replaces;
+  bool PrintLocations;
+};
+
+}
+}
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_RENAMING_ACTION_H_
diff --git a/clang-rename/USRFinder.cpp b/clang-rename/USRFinder.cpp
new file mode 100644 (file)
index 0000000..7e99e70
--- /dev/null
@@ -0,0 +1,195 @@
+//===--- tools/extra/clang-rename/USRFinder.cpp - Clang rename tool -------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file Implements a recursive AST visitor that finds the USR of a symbol at a
+/// point.
+///
+//===----------------------------------------------------------------------===//
+
+#include "USRFinder.h"
+#include "clang/AST/AST.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/Index/USRGeneration.h"
+#include "clang/Lex/Lexer.h"
+#include "llvm/ADT/SmallVector.h"
+
+using namespace llvm;
+
+namespace clang {
+namespace rename {
+
+// NamedDeclFindingASTVisitor recursively visits each AST node to find the
+// symbol underneath the cursor.
+// FIXME: move to seperate .h/.cc file if this gets too large.
+namespace {
+class NamedDeclFindingASTVisitor
+    : public clang::RecursiveASTVisitor<NamedDeclFindingASTVisitor> {
+public:
+  // \brief Finds the NamedDecl at a point in the source.
+  // \param Point the location in the source to search for the NamedDecl.
+  explicit NamedDeclFindingASTVisitor(const SourceManager &SourceMgr,
+                                      const SourceLocation Point)
+      : Result(nullptr), SourceMgr(SourceMgr),
+        Point(Point) {
+  }
+
+  // \brief Finds the NamedDecl for a name in the source.
+  // \param Name the fully qualified name.
+  explicit NamedDeclFindingASTVisitor(const SourceManager &SourceMgr,
+                                      const std::string &Name)
+      : Result(nullptr), SourceMgr(SourceMgr),
+        Name(Name) {
+  }
+
+  // Declaration visitors:
+
+  // \brief Checks if the point falls within the NameDecl. This covers every
+  // declaration of a named entity that we may come across. Usually, just
+  // checking if the point lies within the length of the name of the declaration
+  // and the start location is sufficient.
+  bool VisitNamedDecl(const NamedDecl *Decl) {
+    return setResult(Decl, Decl->getLocation(),
+                     Decl->getNameAsString().length());
+  }
+
+  // Expression visitors:
+
+  bool VisitDeclRefExpr(const DeclRefExpr *Expr) {
+    // Check the namespace specifier first.
+    if (!checkNestedNameSpecifierLoc(Expr->getQualifierLoc()))
+      return false;
+
+    const auto *Decl = Expr->getFoundDecl();
+    return setResult(Decl, Expr->getLocation(),
+                     Decl->getNameAsString().length());
+  }
+
+  bool VisitMemberExpr(const MemberExpr *Expr) {
+    const auto *Decl = Expr->getFoundDecl().getDecl();
+    return setResult(Decl, Expr->getMemberLoc(),
+                     Decl->getNameAsString().length());
+  }
+
+  // Other:
+
+  const NamedDecl *getNamedDecl() {
+    return Result;
+  }
+
+private:
+  // \brief Determines if a namespace qualifier contains the point.
+  // \returns false on success and sets Result.
+  bool checkNestedNameSpecifierLoc(NestedNameSpecifierLoc NameLoc) {
+    while (NameLoc) {
+      const auto *Decl = NameLoc.getNestedNameSpecifier()->getAsNamespace();
+      if (Decl && !setResult(Decl, NameLoc.getLocalBeginLoc(),
+                             Decl->getNameAsString().length()))
+        return false;
+      NameLoc = NameLoc.getPrefix();
+    }
+    return true;
+  }
+
+  // \brief Sets Result to Decl if the Point is within Start and End.
+  // \returns false on success.
+  bool setResult(const NamedDecl *Decl, SourceLocation Start,
+                 SourceLocation End) {
+    if (Name.empty()) {
+      // Offset is used to find the declaration.
+      if (!Start.isValid() || !Start.isFileID() || !End.isValid() ||
+          !End.isFileID() || !isPointWithin(Start, End)) {
+        return true;
+      }
+    } else {
+      // Fully qualified name is used to find the declaration.
+      if (Name != Decl->getQualifiedNameAsString()) {
+        return true;
+      }
+    }
+    Result = Decl;
+    return false;
+  }
+
+  // \brief Sets Result to Decl if Point is within Loc and Loc + Offset.
+  // \returns false on success.
+  bool setResult(const NamedDecl *Decl, SourceLocation Loc,
+                 unsigned Offset) {
+    // FIXME: Add test for Offset == 0. Add test for Offset - 1 (vs -2 etc).
+    return Offset == 0 ||
+           setResult(Decl, Loc, Loc.getLocWithOffset(Offset - 1));
+  }
+
+  // \brief Determines if the Point is within Start and End.
+  bool isPointWithin(const SourceLocation Start, const SourceLocation End) {
+    // FIXME: Add tests for Point == End.
+    return Point == Start || Point == End ||
+           (SourceMgr.isBeforeInTranslationUnit(Start, Point) &&
+            SourceMgr.isBeforeInTranslationUnit(Point, End));
+  }
+
+  const NamedDecl *Result;
+  const SourceManager &SourceMgr;
+  const SourceLocation Point; // The location to find the NamedDecl.
+  const std::string Name;
+};
+} // namespace
+
+const NamedDecl *getNamedDeclAt(const ASTContext &Context,
+                                const SourceLocation Point) {
+  const auto &SourceMgr = Context.getSourceManager();
+  const auto SearchFile = SourceMgr.getFilename(Point);
+
+  NamedDeclFindingASTVisitor Visitor(SourceMgr, Point);
+
+  // We only want to search the decls that exist in the same file as the point.
+  auto Decls = Context.getTranslationUnitDecl()->decls();
+  for (auto &CurrDecl : Decls) {
+    const auto FileLoc = CurrDecl->getLocStart();
+    const auto FileName = SourceMgr.getFilename(FileLoc);
+    // FIXME: Add test.
+    if (FileName == SearchFile) {
+      Visitor.TraverseDecl(CurrDecl);
+      if (const NamedDecl *Result = Visitor.getNamedDecl()) {
+        return Result;
+      }
+    }
+  }
+
+  return nullptr;
+}
+
+const NamedDecl *getNamedDeclFor(const ASTContext &Context,
+                                 const std::string &Name) {
+  const auto &SourceMgr = Context.getSourceManager();
+  NamedDeclFindingASTVisitor Visitor(SourceMgr, Name);
+  auto Decls = Context.getTranslationUnitDecl()->decls();
+
+  for (auto &CurrDecl : Decls) {
+    Visitor.TraverseDecl(CurrDecl);
+    if (const NamedDecl *Result = Visitor.getNamedDecl()) {
+      return Result;
+    }
+  }
+
+  return nullptr;
+}
+
+std::string getUSRForDecl(const Decl *Decl) {
+  llvm::SmallVector<char, 128> Buff;
+
+  // FIXME: Add test for the nullptr case.
+  if (Decl == nullptr || index::generateUSRForDecl(Decl, Buff))
+    return "";
+
+  return std::string(Buff.data(), Buff.size());
+}
+
+} // namespace rename
+} // namespace clang
diff --git a/clang-rename/USRFinder.h b/clang-rename/USRFinder.h
new file mode 100644 (file)
index 0000000..78985b6
--- /dev/null
@@ -0,0 +1,45 @@
+//===--- tools/extra/clang-rename/USRFinder.h - Clang rename tool ---------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Methods for determining the USR of a symbol at a location in source
+/// code.
+///
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_FINDER_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_FINDER_H
+
+#include <string>
+
+namespace clang {
+class ASTContext;
+class Decl;
+class SourceLocation;
+class NamedDecl;
+
+namespace rename {
+
+// Given an AST context and a point, returns a NamedDecl identifying the symbol
+// at the point. Returns null if nothing is found at the point.
+const NamedDecl *getNamedDeclAt(const ASTContext &Context,
+                                const SourceLocation Point);
+
+// Given an AST context and a fully qualified name, returns a NamedDecl
+// identifying the symbol with a matching name. Returns null if nothing is
+// found for the name.
+const NamedDecl *getNamedDeclFor(const ASTContext &Context,
+                                 const std::string &Name);
+
+// Converts a Decl into a USR.
+std::string getUSRForDecl(const Decl *Decl);
+
+}
+}
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_FINDER_H
diff --git a/clang-rename/USRFindingAction.cpp b/clang-rename/USRFindingAction.cpp
new file mode 100644 (file)
index 0000000..0ff0be6
--- /dev/null
@@ -0,0 +1,122 @@
+//===--- tools/extra/clang-rename/USRFindingAction.cpp - Clang rename tool ===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Provides an action to rename every symbol at a point.
+///
+//===----------------------------------------------------------------------===//
+
+#include "USRFindingAction.h"
+#include "USRFinder.h"
+#include "clang/AST/AST.h"
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendAction.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Tooling/CommonOptionsParser.h"
+#include "clang/Tooling/Refactoring.h"
+#include "clang/Tooling/Tooling.h"
+#include <string>
+#include <vector>
+
+using namespace llvm;
+
+namespace clang {
+namespace rename {
+
+// Get the USRs for the constructors of the class.
+static std::vector<std::string> getAllConstructorUSRs(
+    const CXXRecordDecl *Decl) {
+  std::vector<std::string> USRs;
+
+  // We need to get the definition of the record (as opposed to any forward
+  // declarations) in order to find the constructor and destructor.
+  const auto *RecordDecl = Decl->getDefinition();
+
+  // Iterate over all the constructors and add their USRs.
+  for (const auto *CtorDecl : RecordDecl->ctors())
+    USRs.push_back(getUSRForDecl(CtorDecl));
+
+  // Ignore destructors. GetLocationsOfUSR will find the declaration of and
+  // explicit calls to a destructor through TagTypeLoc (and it is better for the
+  // purpose of renaming).
+  //
+  // For example, in the following code segment,
+  //  1  class C {
+  //  2    ~C();
+  //  3  };
+  // At line 3, there is a NamedDecl starting from '~' and a TagTypeLoc starting
+  // from 'C'.
+
+  return USRs;
+}
+
+struct NamedDeclFindingConsumer : public ASTConsumer {
+  void HandleTranslationUnit(ASTContext &Context) override {
+    const auto &SourceMgr = Context.getSourceManager();
+    // The file we look for the USR in will always be the main source file.
+    const auto Point = SourceMgr.getLocForStartOfFile(
+        SourceMgr.getMainFileID()).getLocWithOffset(SymbolOffset);
+    if (!Point.isValid())
+      return;
+    const NamedDecl *FoundDecl = nullptr;
+    if (OldName.empty()) {
+      FoundDecl = getNamedDeclAt(Context, Point);
+    } else {
+      FoundDecl = getNamedDeclFor(Context, OldName);
+    }
+    if (FoundDecl == nullptr) {
+      FullSourceLoc FullLoc(Point, SourceMgr);
+      errs() << "clang-rename: could not find symbol at "
+             << SourceMgr.getFilename(Point) << ":"
+             << FullLoc.getSpellingLineNumber() << ":"
+             << FullLoc.getSpellingColumnNumber() << " (offset " << SymbolOffset
+             << ").\n";
+      return;
+    }
+
+    // If the decl is a constructor or destructor, we want to instead take the
+    // decl of the parent record.
+    if (const auto *CtorDecl = dyn_cast<CXXConstructorDecl>(FoundDecl))
+      FoundDecl = CtorDecl->getParent();
+    else if (const auto *DtorDecl = dyn_cast<CXXDestructorDecl>(FoundDecl))
+      FoundDecl = DtorDecl->getParent();
+
+    // If the decl is in any way relatedpp to a class, we want to make sure we
+    // search for the constructor and destructor as well as everything else.
+    if (const auto *Record = dyn_cast<CXXRecordDecl>(FoundDecl))
+      *USRs = getAllConstructorUSRs(Record);
+
+    USRs->push_back(getUSRForDecl(FoundDecl));
+    *SpellingName = FoundDecl->getNameAsString();
+  }
+
+  unsigned SymbolOffset;
+  std::string OldName;
+  std::string *SpellingName;
+  std::vector<std::string> *USRs;
+};
+
+std::unique_ptr<ASTConsumer>
+USRFindingAction::newASTConsumer() {
+  std::unique_ptr<NamedDeclFindingConsumer> Consumer(
+      new NamedDeclFindingConsumer);
+  SpellingName = "";
+  Consumer->SymbolOffset = SymbolOffset;
+  Consumer->OldName = OldName;
+  Consumer->USRs = &USRs;
+  Consumer->SpellingName = &SpellingName;
+  return std::move(Consumer);
+}
+
+} // namespace rename
+} // namespace clang
diff --git a/clang-rename/USRFindingAction.h b/clang-rename/USRFindingAction.h
new file mode 100644 (file)
index 0000000..60a439e
--- /dev/null
@@ -0,0 +1,47 @@
+//===--- tools/extra/clang-rename/USRFindingAction.h - Clang rename tool --===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Provides an action to find all relevant USRs at a point.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_FINDING_ACTION_H_
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_FINDING_ACTION_H_
+
+#include "clang/Frontend/FrontendAction.h"
+
+namespace clang {
+class ASTConsumer;
+class CompilerInstance;
+class NamedDecl;
+
+namespace rename {
+
+struct USRFindingAction {
+  USRFindingAction(unsigned Offset, const std::string &Name)
+      : SymbolOffset(Offset), OldName(Name) {}
+  std::unique_ptr<ASTConsumer> newASTConsumer();
+
+  // \brief get the spelling of the USR(s) as it would appear in source files.
+  const std::string &getUSRSpelling() { return SpellingName; }
+
+  const std::vector<std::string> &getUSRs() { return USRs; }
+
+private:
+  unsigned SymbolOffset;
+  std::string OldName;
+  std::string SpellingName;
+  std::vector<std::string> USRs;
+};
+
+} // namespace rename
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_FINDING_ACTION_H_
diff --git a/clang-rename/USRLocFinder.cpp b/clang-rename/USRLocFinder.cpp
new file mode 100644 (file)
index 0000000..5979ecd
--- /dev/null
@@ -0,0 +1,220 @@
+//===--- tools/extra/clang-rename/USRLocFinder.cpp - Clang rename tool ----===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Mehtods for finding all instances of a USR. Our strategy is very
+/// simple; we just compare the USR at every relevant AST node with the one
+/// provided.
+///
+//===----------------------------------------------------------------------===//
+
+#include "USRLocFinder.h"
+#include "USRFinder.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Index/USRGeneration.h"
+#include "clang/Lex/Lexer.h"
+#include "llvm/ADT/SmallVector.h"
+
+using namespace llvm;
+
+namespace clang {
+namespace rename {
+
+namespace {
+// \brief This visitor recursively searches for all instances of a USR in a
+// translation unit and stores them for later usage.
+class USRLocFindingASTVisitor
+    : public clang::RecursiveASTVisitor<USRLocFindingASTVisitor> {
+public:
+  explicit USRLocFindingASTVisitor(StringRef USR, StringRef PrevName)
+      : USR(USR), PrevName(PrevName) {}
+
+  // Declaration visitors:
+
+  bool VisitNamedDecl(const NamedDecl *Decl) {
+    if (getUSRForDecl(Decl) == USR) {
+      LocationsFound.push_back(Decl->getLocation());
+    }
+    return true;
+  }
+
+  bool VisitVarDecl(clang::VarDecl *Decl) {
+    clang::QualType Type = Decl->getType();
+    const clang::RecordDecl *RecordDecl = Type->getPointeeCXXRecordDecl();
+    if (RecordDecl) {
+      if (getUSRForDecl(RecordDecl) == USR) {
+        // The declaration refers to a type that is to be renamed.
+        LocationsFound.push_back(Decl->getTypeSpecStartLoc());
+      }
+    }
+    return true;
+  }
+
+  bool VisitCXXConstructorDecl(clang::CXXConstructorDecl *ConstructorDecl) {
+    const ASTContext &Context = ConstructorDecl->getASTContext();
+    for (auto &Initializer : ConstructorDecl->inits()) {
+      if (Initializer->getSourceOrder() == -1) {
+        // Ignore implicit initializers.
+        continue;
+      }
+
+      if (const clang::FieldDecl *FieldDecl = Initializer->getAnyMember()) {
+        if (getUSRForDecl(FieldDecl) == USR) {
+          // The initializer refers to a field that is to be renamed.
+          SourceLocation Location = Initializer->getSourceLocation();
+          StringRef TokenName = Lexer::getSourceText(
+              CharSourceRange::getTokenRange(Location),
+              Context.getSourceManager(), Context.getLangOpts());
+          if (TokenName == PrevName) {
+            // The token of the source location we find actually has the old
+            // name.
+            LocationsFound.push_back(Initializer->getSourceLocation());
+          }
+        }
+      }
+    }
+
+    if (getUSRForDecl(ConstructorDecl) == USR) {
+      // This takes care of the class name part of a non-inline ctor definition.
+      LocationsFound.push_back(ConstructorDecl->getLocStart());
+    }
+    return true;
+  }
+
+  bool VisitCXXDestructorDecl(clang::CXXDestructorDecl *DestructorDecl) {
+    if (getUSRForDecl(DestructorDecl->getParent()) == USR) {
+      // Handles "~Foo" from "Foo::~Foo".
+      SourceLocation Location = DestructorDecl->getLocation();
+      const ASTContext &Context = DestructorDecl->getASTContext();
+      StringRef LLVM_ATTRIBUTE_UNUSED TokenName = Lexer::getSourceText(
+          CharSourceRange::getTokenRange(Location), Context.getSourceManager(),
+          Context.getLangOpts());
+      // 1 is the length of the "~" string that is not to be touched by the
+      // rename.
+      assert(TokenName.startswith("~"));
+      LocationsFound.push_back(Location.getLocWithOffset(1));
+
+      if (DestructorDecl->isThisDeclarationADefinition()) {
+        // Handles "Foo" from "Foo::~Foo".
+        LocationsFound.push_back(DestructorDecl->getLocStart());
+      }
+    }
+
+    return true;
+  }
+
+  // Expression visitors:
+
+  bool VisitDeclRefExpr(const DeclRefExpr *Expr) {
+    const auto *Decl = Expr->getFoundDecl();
+
+    checkNestedNameSpecifierLoc(Expr->getQualifierLoc());
+    if (getUSRForDecl(Decl) == USR) {
+      const SourceManager &Manager = Decl->getASTContext().getSourceManager();
+      SourceLocation Location = Manager.getSpellingLoc(Expr->getLocation());
+      LocationsFound.push_back(Location);
+    }
+
+    return true;
+  }
+
+  bool VisitMemberExpr(const MemberExpr *Expr) {
+    const auto *Decl = Expr->getFoundDecl().getDecl();
+    if (getUSRForDecl(Decl) == USR) {
+      const SourceManager &Manager = Decl->getASTContext().getSourceManager();
+      SourceLocation Location = Manager.getSpellingLoc(Expr->getMemberLoc());
+      LocationsFound.push_back(Location);
+    }
+    return true;
+  }
+
+  bool VisitCXXConstructExpr(const CXXConstructExpr *Expr) {
+    CXXConstructorDecl *Decl = Expr->getConstructor();
+
+    if (getUSRForDecl(Decl) == USR) {
+      // This takes care of 'new <name>' expressions.
+      LocationsFound.push_back(Expr->getLocation());
+    }
+
+    return true;
+  }
+
+  bool VisitCXXStaticCastExpr(clang::CXXStaticCastExpr *Expr) {
+    return handleCXXNamedCastExpr(Expr);
+  }
+
+  bool VisitCXXDynamicCastExpr(clang::CXXDynamicCastExpr *Expr) {
+    return handleCXXNamedCastExpr(Expr);
+  }
+
+  bool VisitCXXReinterpretCastExpr(clang::CXXReinterpretCastExpr *Expr) {
+    return handleCXXNamedCastExpr(Expr);
+  }
+
+  bool VisitCXXConstCastExpr(clang::CXXConstCastExpr *Expr) {
+    return handleCXXNamedCastExpr(Expr);
+  }
+
+  // Non-visitors:
+
+  // \brief Returns a list of unique locations. Duplicate or overlapping
+  // locations are erroneous and should be reported!
+  const std::vector<clang::SourceLocation> &getLocationsFound() const {
+    return LocationsFound;
+  }
+
+private:
+  // Namespace traversal:
+  void checkNestedNameSpecifierLoc(NestedNameSpecifierLoc NameLoc) {
+    while (NameLoc) {
+      const auto *Decl = NameLoc.getNestedNameSpecifier()->getAsNamespace();
+      if (Decl && getUSRForDecl(Decl) == USR)
+        LocationsFound.push_back(NameLoc.getLocalBeginLoc());
+      NameLoc = NameLoc.getPrefix();
+    }
+  }
+
+  bool handleCXXNamedCastExpr(clang::CXXNamedCastExpr *Expr) {
+    clang::QualType Type = Expr->getType();
+    // See if this a cast of a pointer.
+    const RecordDecl *Decl = Type->getPointeeCXXRecordDecl();
+    if (!Decl) {
+      // See if this is a cast of a reference.
+      Decl = Type->getAsCXXRecordDecl();
+    }
+
+    if (Decl && getUSRForDecl(Decl) == USR) {
+      SourceLocation Location =
+          Expr->getTypeInfoAsWritten()->getTypeLoc().getBeginLoc();
+      LocationsFound.push_back(Location);
+    }
+
+    return true;
+  }
+
+  // All the locations of the USR were found.
+  const std::string USR;
+  // Old name that is renamed.
+  const std::string PrevName;
+  std::vector<clang::SourceLocation> LocationsFound;
+};
+} // namespace
+
+std::vector<SourceLocation> getLocationsOfUSR(StringRef USR, StringRef PrevName,
+                                              Decl *Decl) {
+  USRLocFindingASTVisitor Visitor(USR, PrevName);
+
+  Visitor.TraverseDecl(Decl);
+  return Visitor.getLocationsFound();
+}
+
+} // namespace rename
+} // namespace clang
diff --git a/clang-rename/USRLocFinder.h b/clang-rename/USRLocFinder.h
new file mode 100644 (file)
index 0000000..e70af59
--- /dev/null
@@ -0,0 +1,34 @@
+//===--- tools/extra/clang-rename/USRLocFinder.h - Clang rename tool ------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Provides functionality for finding all instances of a USR in a given
+/// AST.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_LOC_FINDER_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_LOC_FINDER_H
+
+#include "clang/AST/AST.h"
+#include "llvm/ADT/StringRef.h"
+#include <string>
+#include <vector>
+
+namespace clang {
+namespace rename {
+
+// FIXME: make this an AST matcher. Wouldn't that be awesome??? I agree!
+std::vector<SourceLocation>
+getLocationsOfUSR(llvm::StringRef USR, llvm::StringRef PrevName, Decl *Decl);
+
+} // namespace rename
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_LOC_FINDER_H
diff --git a/clang-rename/tool/CMakeLists.txt b/clang-rename/tool/CMakeLists.txt
new file mode 100644 (file)
index 0000000..d19073f
--- /dev/null
@@ -0,0 +1,12 @@
+add_clang_executable(clang-rename ClangRename.cpp)
+
+target_link_libraries(clang-rename
+  clangBasic
+  clangFrontend
+  clangRename
+  clangRewrite
+  clangTooling
+  clangToolingCore
+  )
+
+install(TARGETS clang-rename RUNTIME DESTINATION bin)
diff --git a/clang-rename/tool/ClangRename.cpp b/clang-rename/tool/ClangRename.cpp
new file mode 100644 (file)
index 0000000..7475211
--- /dev/null
@@ -0,0 +1,179 @@
+//===--- tools/extra/clang-rename/ClangRename.cpp - Clang rename 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 implements a clang-rename tool that automatically finds and
+/// renames symbols in C++ code.
+///
+//===----------------------------------------------------------------------===//
+
+#include "../USRFindingAction.h"
+#include "../RenamingAction.h"
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/TargetInfo.h"
+#include "clang/Basic/TargetOptions.h"
+#include "clang/Frontend/CommandLineSourceLoc.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendAction.h"
+#include "clang/Frontend/TextDiagnosticPrinter.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Parse/ParseAST.h"
+#include "clang/Parse/Parser.h"
+#include "clang/Rewrite/Core/Rewriter.h"
+#include "clang/Tooling/CommonOptionsParser.h"
+#include "clang/Tooling/Refactoring.h"
+#include "clang/Tooling/ReplacementsYaml.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/IntrusiveRefCntPtr.h"
+#include "llvm/Support/Host.h"
+#include <string>
+
+using namespace llvm;
+
+cl::OptionCategory ClangRenameCategory("Clang-rename options");
+
+static cl::opt<std::string>
+NewName(
+    "new-name",
+    cl::desc("The new name to change the symbol to."),
+    cl::cat(ClangRenameCategory));
+static cl::opt<unsigned>
+SymbolOffset(
+    "offset",
+    cl::desc("Locates the symbol by offset as opposed to <line>:<column>."),
+    cl::cat(ClangRenameCategory));
+static cl::opt<std::string>
+OldName(
+    "old-name",
+    cl::desc("The fully qualified name of the symbol, if -offset is not used."),
+    cl::cat(ClangRenameCategory));
+static cl::opt<bool>
+Inplace(
+    "i",
+    cl::desc("Overwrite edited <file>s."),
+    cl::cat(ClangRenameCategory));
+static cl::opt<bool>
+PrintName(
+    "pn",
+    cl::desc("Print the found symbol's name prior to renaming to stderr."),
+    cl::cat(ClangRenameCategory));
+static cl::opt<bool>
+PrintLocations(
+    "pl",
+    cl::desc("Print the locations affected by renaming to stderr."),
+    cl::cat(ClangRenameCategory));
+static cl::opt<std::string>
+ExportFixes(
+    "export-fixes",
+    cl::desc("YAML file to store suggested fixes in."),
+    cl::value_desc("filename"),
+    cl::cat(ClangRenameCategory));
+
+#define CLANG_RENAME_VERSION "0.0.1"
+
+static void PrintVersion() {
+  outs() << "clang-rename version " << CLANG_RENAME_VERSION << '\n';
+}
+
+using namespace clang;
+
+const char RenameUsage[] = "A tool to rename symbols in C/C++ code.\n\
+clang-rename renames every occurrence of a symbol found at <offset> in\n\
+<source0>. If -i is specified, the edited files are overwritten to disk.\n\
+Otherwise, the results are written to stdout.\n";
+
+int main(int argc, const char **argv) {
+  cl::SetVersionPrinter(PrintVersion);
+  tooling::CommonOptionsParser OP(argc, argv, ClangRenameCategory, RenameUsage);
+
+  // Check the arguments for correctness.
+
+  if (NewName.empty()) {
+    errs() << "clang-rename: no new name provided.\n\n";
+    exit(1);
+  }
+
+  // Get the USRs.
+  auto Files = OP.getSourcePathList();
+  tooling::RefactoringTool Tool(OP.getCompilations(), Files);
+  rename::USRFindingAction USRAction(SymbolOffset, OldName);
+
+  // Find the USRs.
+  Tool.run(tooling::newFrontendActionFactory(&USRAction).get());
+  const auto &USRs = USRAction.getUSRs();
+  const auto &PrevName = USRAction.getUSRSpelling();
+
+  if (PrevName.empty()) {
+    // An error should have already been printed.
+    exit(1);
+  }
+
+  if (PrintName) {
+    errs() << "clang-rename: found name: " << PrevName << '\n';
+  }
+
+  // Perform the renaming.
+  rename::RenamingAction RenameAction(NewName, PrevName, USRs,
+                                      Tool.getReplacements(), PrintLocations);
+  auto Factory = tooling::newFrontendActionFactory(&RenameAction);
+  int ExitCode;
+
+  if (Inplace) {
+    ExitCode = Tool.runAndSave(Factory.get());
+  } else {
+    ExitCode = Tool.run(Factory.get());
+
+    if (!ExportFixes.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';
+        exit(1);
+      }
+
+      // Export replacements.
+      tooling::TranslationUnitReplacements TUR;
+      const tooling::Replacements &Replacements = Tool.getReplacements();
+      TUR.Replacements.insert(TUR.Replacements.end(), Replacements.begin(),
+                              Replacements.end());
+
+      yaml::Output YAML(OS);
+      YAML << TUR;
+      OS.close();
+      exit(0);
+    }
+
+    // Write every file to stdout. Right now we just barf the files without any
+    // indication of which files start where, other than that we print the files
+    // in the same order we see them.
+    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);
+      auto ID = Sources.translateFile(Entry);
+      Rewrite.getEditBuffer(ID).write(outs());
+    }
+  }
+
+  exit(ExitCode);
+}
diff --git a/clang-rename/tool/clang-rename.py b/clang-rename/tool/clang-rename.py
new file mode 100644 (file)
index 0000000..1c9082d
--- /dev/null
@@ -0,0 +1,61 @@
+'''
+Minimal clang-rename integration with Vim.
+
+Before installing make sure one of the following is satisfied:
+
+* clang-rename is in your PATH
+* `g:clang_rename_path` in ~/.vimrc points to valid clang-rename executable
+* `binary` in clang-rename.py points to valid to clang-rename executable
+
+To install, simply put this into your ~/.vimrc
+
+    map ,cr :pyf <path-to>/clang-rename.py<cr>
+
+IMPORTANT NOTE: Before running the tool, make sure you saved the file.
+
+All you have to do now is to place a cursor on a variable/function/class which
+you would like to rename and press ',cr'. You will be prompted for a new name if
+the cursor points to a valid symbol.
+'''
+
+import vim
+import subprocess
+import sys
+
+def main():
+    binary = 'clang-rename'
+    if vim.eval('exists("g:clang_rename_path")') == "1":
+        binary = vim.eval('g:clang_rename_path')
+
+    # Get arguments for clang-rename binary.
+    offset = int(vim.eval('line2byte(line("."))+col(".")')) - 2
+    if offset < 0:
+        print >> sys.stderr, '''Couldn\'t determine cursor position.
+                                Is your file empty?'''
+        return
+    filename = vim.current.buffer.name
+
+    new_name_request_message = 'type new name:'
+    new_name = vim.eval("input('{}\n')".format(new_name_request_message))
+
+    # Call clang-rename.
+    command = [binary,
+               filename,
+               '-i',
+               '-offset', str(offset),
+               '-new-name', str(new_name)]
+    # FIXME: make it possible to run the tool on unsaved file.
+    p = subprocess.Popen(command,
+                         stdout=subprocess.PIPE,
+                         stderr=subprocess.PIPE)
+    stdout, stderr = p.communicate()
+
+    if stderr:
+        print stderr
+
+    # Reload all buffers in Vim.
+    vim.command("bufdo edit")
+
+
+if __name__ == '__main__':
+    main()
diff --git a/clang-tidy/CMakeLists.txt b/clang-tidy/CMakeLists.txt
new file mode 100644 (file)
index 0000000..8ac6124
--- /dev/null
@@ -0,0 +1,39 @@
+set(LLVM_LINK_COMPONENTS
+  Support
+  )
+
+add_clang_library(clangTidy
+  ClangTidy.cpp
+  ClangTidyModule.cpp
+  ClangTidyDiagnosticConsumer.cpp
+  ClangTidyOptions.cpp
+
+  DEPENDS
+  ClangSACheckers
+
+  LINK_LIBS
+  clangAST
+  clangASTMatchers
+  clangBasic
+  clangFrontend
+  clangLex
+  clangRewrite
+  clangSema
+  clangStaticAnalyzerCore
+  clangStaticAnalyzerFrontend
+  clangTooling
+  clangToolingCore
+  )
+
+add_subdirectory(tool)
+add_subdirectory(plugin)
+add_subdirectory(boost)
+add_subdirectory(cert)
+add_subdirectory(llvm)
+add_subdirectory(cppcoreguidelines)
+add_subdirectory(google)
+add_subdirectory(misc)
+add_subdirectory(modernize)
+add_subdirectory(performance)
+add_subdirectory(readability)
+add_subdirectory(utils)
diff --git a/clang-tidy/ClangTidy.cpp b/clang-tidy/ClangTidy.cpp
new file mode 100644 (file)
index 0000000..95ea0eb
--- /dev/null
@@ -0,0 +1,522 @@
+//===--- 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/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/Frontend/AnalysisConsumer.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;
+
+template class llvm::Registry<clang::tidy::ClangTidyModule>;
+
+namespace clang {
+namespace tidy {
+
+namespace {
+static const char *AnalyzerCheckNamePrefix = "clang-analyzer-";
+
+static const StringRef StaticAnalyzerChecks[] = {
+#define GET_CHECKERS
+#define CHECKER(FULLNAME, CLASS, DESCFILE, HELPTEXT, GROUPINDEX, HIDDEN)       \
+  FULLNAME,
+#include "clang/StaticAnalyzer/Checkers/Checkers.inc"
+#undef CHECKER
+#undef GET_CHECKERS
+};
+
+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(bool ApplyFixes)
+      : Files(FileSystemOptions()), DiagOpts(new DiagnosticOptions()),
+        DiagPrinter(new TextDiagnosticPrinter(llvm::outs(), &*DiagOpts)),
+        Diags(IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), &*DiagOpts,
+              DiagPrinter),
+        SourceMgr(Diags, Files), Rewrite(SourceMgr, LangOpts),
+        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 ClangTidyMessage &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.CheckName;
+      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 tooling::Replacement &Fix : Error.Fix) {
+        // 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;
+        if (Fix.isApplicable()) {
+          SmallString<128> FixAbsoluteFilePath = Fix.getFilePath();
+          Files.makeAbsolutePath(FixAbsoluteFilePath);
+          FixLoc = getLocation(FixAbsoluteFilePath, Fix.getOffset());
+          SourceLocation FixEndLoc = FixLoc.getLocWithOffset(Fix.getLength());
+          Range = SourceRange(FixLoc, FixEndLoc);
+          Diag << FixItHint::CreateReplacement(Range, Fix.getReplacementText());
+        }
+
+        ++TotalFixes;
+        if (ApplyFixes) {
+          bool Success = Fix.isApplicable() && Fix.apply(Rewrite);
+          if (Success)
+            ++AppliedFixes;
+          FixLocations.push_back(std::make_pair(FixLoc, Success));
+        }
+      }
+    }
+    for (auto Fix : FixLocations) {
+      Diags.Report(Fix.first, Fix.second ? diag::note_fixit_applied
+                                         : diag::note_fixit_failed);
+    }
+    for (const ClangTidyMessage &Note : Error.Notes)
+      reportNote(Note);
+  }
+
+  void Finish() {
+    // FIXME: Run clang-format on changes.
+    if (ApplyFixes && TotalFixes > 0) {
+      llvm::errs() << "clang-tidy applied " << AppliedFixes << " of "
+                   << TotalFixes << " suggested fixes.\n";
+      Rewrite.overwriteChangedFiles();
+    }
+  }
+
+  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.createFileID(File, SourceLocation(), SrcMgr::C_User);
+    return SourceMgr.getLocForStartOfFile(ID).getLocWithOffset(Offset);
+  }
+
+  void reportNote(const ClangTidyMessage &Message) {
+    SourceLocation Loc = getLocation(Message.FilePath, Message.FileOffset);
+    DiagnosticBuilder Diag =
+        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;
+  Rewriter Rewrite;
+  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;
+  }
+}
+
+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";
+
+  GlobList &Filter = Context.getChecksFilter();
+  AnalyzerOptions->CheckersControlList = getCheckersControlList(Filter);
+  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;
+  GlobList &Filter = Context.getChecksFilter();
+  for (const auto &CheckFactory : *CheckFactories) {
+    if (Filter.contains(CheckFactory.first))
+      CheckNames.push_back(CheckFactory.first);
+  }
+
+  for (const auto &AnalyzerCheck : getCheckersControlList(Filter))
+    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;
+}
+
+ClangTidyASTConsumerFactory::CheckersList
+ClangTidyASTConsumerFactory::getCheckersControlList(GlobList &Filter) {
+  CheckersList List;
+
+  bool AnalyzerChecksEnabled = false;
+  for (StringRef CheckName : StaticAnalyzerChecks) {
+    std::string Checker((AnalyzerCheckNamePrefix + CheckName).str());
+    AnalyzerChecksEnabled =
+        AnalyzerChecksEnabled ||
+        (!CheckName.startswith("debug") && Filter.contains(Checker));
+  }
+
+  if (AnalyzerChecksEnabled) {
+    // Run our regex against all possible static analyzer checkers.  Note that
+    // debug checkers print values / run programs to visualize the CFG and are
+    // thus not applicable to clang-tidy in general.
+    //
+    // Always add all core checkers if any other static analyzer checks are
+    // enabled. This is currently necessary, as other path sensitive checks
+    // rely on the core checkers.
+    for (StringRef CheckName : StaticAnalyzerChecks) {
+      std::string Checker((AnalyzerCheckNamePrefix + CheckName).str());
+
+      if (CheckName.startswith("core") ||
+          (!CheckName.startswith("debug") && Filter.contains(Checker)))
+        List.push_back(std::make_pair(CheckName, true));
+    }
+  }
+  return List;
+}
+
+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();
+}
+
+ClangTidyStats
+runClangTidy(std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider,
+             const tooling::CompilationDatabase &Compilations,
+             ArrayRef<std::string> InputFiles,
+             std::vector<ClangTidyError> *Errors, ProfileData *Profile) {
+  ClangTool Tool(Compilations, InputFiles);
+  clang::tidy::ClangTidyContext Context(std::move(OptionsProvider));
+
+  // 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;
+        if (Opts.ExtraArgsBefore)
+          AdjustedArgs = *Opts.ExtraArgsBefore;
+        AdjustedArgs.insert(AdjustedArgs.begin(), Args.begin(), Args.end());
+        if (Opts.ExtraArgs)
+          AdjustedArgs.insert(AdjustedArgs.end(), Opts.ExtraArgs->begin(),
+                              Opts.ExtraArgs->end());
+        return AdjustedArgs;
+      };
+
+  // Remove plugins arguments.
+  ArgumentsAdjuster PluginArgumentsRemover =
+      [&Context](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);
+  *Errors = Context.getErrors();
+  return Context.getStats();
+}
+
+void handleErrors(const std::vector<ClangTidyError> &Errors, bool Fix,
+                  unsigned &WarningsAsErrorsCount) {
+  ErrorReporter Reporter(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 : Errors) {
+    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 std::vector<ClangTidyError> &Errors,
+                        raw_ostream &OS) {
+  tooling::TranslationUnitReplacements TUR;
+  for (const ClangTidyError &Error : Errors)
+    TUR.Replacements.insert(TUR.Replacements.end(), Error.Fix.begin(),
+                            Error.Fix.end());
+
+  yaml::Output YAML(OS);
+  YAML << TUR;
+}
+
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/ClangTidy.h b/clang-tidy/ClangTidy.h
new file mode 100644 (file)
index 0000000..5c2721c
--- /dev/null
@@ -0,0 +1,236 @@
+//===--- 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 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:
+  typedef std::vector<std::pair<std::string, bool>> CheckersList;
+  CheckersList getCheckersControlList(GlobList &Filter);
+
+  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.
+ClangTidyStats
+runClangTidy(std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider,
+             const tooling::CompilationDatabase &Compilations,
+             ArrayRef<std::string> InputFiles,
+             std::vector<ClangTidyError> *Errors,
+             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.
+void handleErrors(const std::vector<ClangTidyError> &Errors, bool Fix,
+                  unsigned &WarningsAsErrorsCount);
+
+/// \brief Serializes replacements into YAML and writes them to the specified
+/// output stream.
+void exportReplacements(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..d0a7e5f
--- /dev/null
@@ -0,0 +1,573 @@
+//===--- 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, ClangTidyMessage,
+///  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(SourceLocation Loc, PresumedLoc PLoc,
+                             DiagnosticsEngine::Level Level, StringRef Message,
+                             ArrayRef<CharSourceRange> Ranges,
+                             const SourceManager *SM,
+                             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.CheckName + "]";
+    if (Message.endswith(CheckNameInMessage))
+      Message = Message.substr(0, Message.size() - CheckNameInMessage.size());
+
+    ClangTidyMessage TidyMessage = Loc.isValid()
+                                       ? ClangTidyMessage(Message, *SM, Loc)
+                                       : ClangTidyMessage(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(SourceLocation Loc, PresumedLoc PLoc,
+                         DiagnosticsEngine::Level Level,
+                         ArrayRef<CharSourceRange> Ranges,
+                         const SourceManager &SM) override {}
+
+  void emitCodeContext(SourceLocation Loc, DiagnosticsEngine::Level Level,
+                       SmallVectorImpl<CharSourceRange> &Ranges,
+                       ArrayRef<FixItHint> Hints,
+                       const SourceManager &SM) 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.");
+
+      Error.Fix.insert(tooling::Replacement(SM, Range, FixIt.CodeToInsert));
+    }
+  }
+
+  void emitIncludeLocation(SourceLocation Loc, PresumedLoc PLoc,
+                           const SourceManager &SM) override {}
+
+  void emitImportLocation(SourceLocation Loc, PresumedLoc PLoc,
+                          StringRef ModuleName,
+                          const SourceManager &SM) override {}
+
+  void emitBuildingModuleLocation(SourceLocation Loc, PresumedLoc PLoc,
+                                  StringRef ModuleName,
+                                  const SourceManager &SM) 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
+
+ClangTidyMessage::ClangTidyMessage(StringRef Message)
+    : Message(Message), FileOffset(0) {}
+
+ClangTidyMessage::ClangTidyMessage(StringRef Message,
+                                   const SourceManager &Sources,
+                                   SourceLocation Loc)
+    : Message(Message) {
+  assert(Loc.isValid() && Loc.isFileID());
+  FilePath = Sources.getFilename(Loc);
+  FileOffset = Sources.getFileOffset(Loc);
+}
+
+ClangTidyError::ClangTidyError(StringRef CheckName,
+                               ClangTidyError::Level DiagLevel,
+                               bool IsWarningAsError,
+                               StringRef BuildDirectory)
+    : CheckName(CheckName), BuildDirectory(BuildDirectory), DiagLevel(DiagLevel),
+      IsWarningAsError(IsWarningAsError) {}
+
+// Returns true if GlobList starts with the negative indicator ('-'), removes it
+// from the GlobList.
+static bool ConsumeNegativeIndicator(StringRef &GlobList) {
+  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 Glob = GlobList.substr(0, GlobList.find(',')).trim();
+  GlobList = GlobList.substr(Glob.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;
+}
+
+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("");
+}
+
+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());
+  if (CheckNamesByDiagnosticID.count(ID) == 0)
+    CheckNamesByDiagnosticID.insert(std::make_pair(ID, CheckName.str()));
+  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.reset(new GlobList(*getOptions().Checks));
+  WarningAsErrorFilter.reset(new GlobList(*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; }
+
+GlobList &ClangTidyContext::getChecksFilter() {
+  assert(CheckFilter != nullptr);
+  return *CheckFilter;
+}
+
+GlobList &ClangTidyContext::getWarningAsErrorFilter() {
+  assert(WarningAsErrorFilter != nullptr);
+  return *WarningAsErrorFilter;
+}
+
+/// \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)
+    : Context(Ctx), LastErrorRelatesToUserCode(false),
+      LastErrorPassesLineFilter(false) {
+  IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
+  Diags.reset(new 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.getChecksFilter().contains(Error.CheckName) &&
+        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) {
+    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;
+    }
+  }
+  return false;
+}
+
+void ClangTidyDiagnosticConsumer::HandleDiagnostic(
+    DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) {
+  if (Info.getLocation().isValid() &&
+      DiagLevel != DiagnosticsEngine::Error &&
+      DiagLevel != DiagnosticsEngine::Fatal &&
+      LineIsMarkedWithNOLINT(Diags->getSourceManager(), Info.getLocation())) {
+    ++Context.Stats.ErrorsIgnoredNOLINT;
+    return;
+  }
+  // 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.getWarningAsErrorFilter().contains(CheckName);
+    Errors.push_back(ClangTidyError(CheckName, Level, IsWarningAsError,
+                                    Context.getCurrentBuildDirectory()));
+  }
+
+  ClangTidyDiagnosticRenderer Converter(
+      Context.getLangOpts(), &Context.DiagEngine->getDiagnosticOptions(),
+      Errors.back());
+  SmallString<100> Message;
+  Info.FormatDiagnostic(Message);
+  SourceManager *Sources = nullptr;
+  if (Info.hasSourceManager())
+    Sources = &Info.getSourceManager();
+  Converter.emitDiagnostic(Info.getLocation(), DiagLevel, Message,
+                           Info.getRanges(), Info.getFixItHints(), Sources);
+
+  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.reset(
+        new 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
+  // proccess, 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 proccess 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 proccess 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 proccessed before, disallowing the second one, and the
+      //   end point of the first one will also be proccessed 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 &Replace : Error.Fix)
+      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 &Replace : Errors[I].Fix) {
+      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;
+      FileEvents[FilePath].push_back(
+          Event(Begin, End, Event::ET_Begin, I, Sizes[I]));
+      FileEvents[FilePath].push_back(
+          Event(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.push_back(
+          ClangTidyMessage("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 ClangTidyMessage &M1 = LHS.Message;
+    const ClangTidyMessage &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());
+  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..d7e4d2b
--- /dev/null
@@ -0,0 +1,293 @@
+//===--- 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/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 message from a clang-tidy check.
+///
+/// Note that this is independent of a \c SourceManager.
+struct ClangTidyMessage {
+  ClangTidyMessage(StringRef Message = "");
+  ClangTidyMessage(StringRef Message, const SourceManager &Sources,
+                   SourceLocation Loc);
+  std::string Message;
+  std::string FilePath;
+  unsigned FileOffset;
+};
+
+/// \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 {
+  enum Level {
+    Warning = DiagnosticsEngine::Warning,
+    Error = DiagnosticsEngine::Error
+  };
+
+  ClangTidyError(StringRef CheckName, Level DiagLevel, bool IsWarningAsError,
+                 StringRef BuildDirectory);
+
+  std::string CheckName;
+  ClangTidyMessage Message;
+  tooling::Replacements Fix;
+  SmallVector<ClangTidyMessage, 1> Notes;
+
+  // A build directory of the diagnostic source file.
+  //
+  // It's an absolute path which is `directory` field of the source file in
+  // compilation database. If users don't specify the compilation database
+  // directory, it is the current directory where clang-tidy runs.
+  //
+  // Note: it is empty in unittest.
+  std::string BuildDirectory;
+
+  Level DiagLevel;
+  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);
+
+  /// \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 check filter for the \c CurrentFile.
+  ///
+  /// The \c CurrentFile can be changed using \c setCurrentFile.
+  GlobList &getChecksFilter();
+
+  /// \brief Returns check filter for the \c CurrentFile which
+  /// selects checks for upgrade to error.
+  GlobList &getWarningAsErrorFilter();
+
+  /// \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.
+  const std::vector<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;
+  std::unique_ptr<GlobList> CheckFilter;
+  std::unique_ptr<GlobList> 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);
+
+  // 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;
+  std::unique_ptr<DiagnosticsEngine> Diags;
+  SmallVector<ClangTidyError, 8> Errors;
+  std::unique_ptr<llvm::Regex> HeaderFilter;
+  bool LastErrorRelatesToUserCode;
+  bool LastErrorPassesLineFilter;
+};
+
+} // 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..3a3ae82
--- /dev/null
@@ -0,0 +1,39 @@
+//===--- 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) {
+  GlobList &Filter = Context->getChecksFilter();
+  for (const auto &Factory : Factories) {
+    if (Filter.contains(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..58e833c
--- /dev/null
@@ -0,0 +1,98 @@
+//===--- 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..de2a3d7
--- /dev/null
@@ -0,0 +1,26 @@
+//===--- 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"
+
+extern template class llvm::Registry<clang::tidy::ClangTidyModule>;
+
+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..4dd2196
--- /dev/null
@@ -0,0 +1,334 @@
+//===--- 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)
+LLVM_YAML_IS_SEQUENCE_VECTOR(ClangTidyOptions::StringPair)
+LLVM_YAML_IS_SEQUENCE_VECTOR(std::string)
+
+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("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.User = llvm::None;
+  for (ClangTidyModuleRegistry::iterator I = ClangTidyModuleRegistry::begin(),
+                                         E = ClangTidyModuleRegistry::end();
+       I != E; ++I)
+    Options = Options.mergeWith(I->instantiate()->getModuleOptions());
+  return Options;
+}
+
+ClangTidyOptions
+ClangTidyOptions::mergeWith(const ClangTidyOptions &Other) const {
+  ClangTidyOptions Result = *this;
+
+  // Merge comma-separated glob lists by appending the new value after a comma.
+  if (Other.Checks)
+    Result.Checks =
+        (Result.Checks && !Result.Checks->empty() ? *Result.Checks + "," : "") +
+        *Other.Checks;
+  if (Other.WarningsAsErrors)
+    Result.WarningsAsErrors =
+        (Result.WarningsAsErrors && !Result.WarningsAsErrors->empty()
+             ? *Result.WarningsAsErrors + ","
+             : "") +
+        *Other.WarningsAsErrors;
+
+  if (Other.HeaderFilterRegex)
+    Result.HeaderFilterRegex = Other.HeaderFilterRegex;
+  if (Other.SystemHeaders)
+    Result.SystemHeaders = Other.SystemHeaders;
+  if (Other.AnalyzeTemporaryDtors)
+    Result.AnalyzeTemporaryDtors = Other.AnalyzeTemporaryDtors;
+  if (Other.User)
+    Result.User = Other.User;
+  if (Other.ExtraArgs)
+    Result.ExtraArgs = Other.ExtraArgs;
+  if (Other.ExtraArgsBefore)
+    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..74a483f
--- /dev/null
@@ -0,0 +1,259 @@
+//===--- 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 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..868ccd1
--- /dev/null
@@ -0,0 +1,305 @@
+#!/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 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:
+      match = re.search('.*:http-equiv=refresh: \d+;URL=(.*).html.*',
+                        doc.read())
+      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)
+  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/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/cert/CERTTidyModule.cpp b/clang-tidy/cert/CERTTidyModule.cpp
new file mode 100644 (file)
index 0000000..1a0d9c5
--- /dev/null
@@ -0,0 +1,91 @@
+//===--- 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 "FloatLoopCounter.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<VariadicFunctionDefCheck>(
+        "cert-dcl50-cpp");
+    CheckFactories.registerCheck<misc::NewDeleteOverloadsCheck>(
+        "cert-dcl54-cpp");
+    CheckFactories.registerCheck<google::build::UnnamedNamespaceInHeaderCheck>(
+        "cert-dcl59-cpp");
+    // OOP
+    CheckFactories.registerCheck<misc::MoveConstructorInitCheck>(
+        "cert-oop11-cpp");
+    // ERR
+    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");
+
+    // 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");
+  }
+  ClangTidyOptions getModuleOptions() override {
+    ClangTidyOptions Options;
+    Options.CheckOptions["cert-oop11-cpp.UseCERTSemantics"] = "1";
+    return Options;
+  }
+};
+
+} // 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..a53ec2f
--- /dev/null
@@ -0,0 +1,23 @@
+set(LLVM_LINK_COMPONENTS support)
+
+add_clang_library(clangTidyCERTModule
+  CERTTidyModule.cpp
+  CommandProcessorCheck.cpp
+  FloatLoopCounter.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/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..b332152
--- /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/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..53597fa
--- /dev/null
@@ -0,0 +1,50 @@
+//===--- 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)
+    return;
+
+  // Match any static or thread_local variable declaration that is initialized
+  // with a constructor that can throw.
+  Finder->addMatcher(
+      varDecl(anyOf(hasThreadStorageDuration(), hasStaticStorageDuration()),
+              hasInitializer(cxxConstructExpr(hasDeclaration(
+                  cxxConstructorDecl(unless(isNoThrow()))
+                      .bind("ctor")))))
+          .bind("var"),
+      this);
+}
+
+void StaticObjectExceptionCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *VD = Result.Nodes.getNodeAs<VarDecl>("var");
+  const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructorDecl>("ctor");
+
+  diag(VD->getLocation(),
+       "construction 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);
+  diag(Ctor->getLocation(), "possibly throwing constructor declared here",
+       DiagnosticIDs::Note);
+}
+
+} // 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..5266daf
--- /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/Analysis/Analyses/FormatString.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.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, Result.Context->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..e05539e
--- /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..65c40ed
--- /dev/null
@@ -0,0 +1,26 @@
+set(LLVM_LINK_COMPONENTS support)
+
+add_clang_library(clangTidyCppCoreGuidelinesModule
+  CppCoreGuidelinesTidyModule.cpp
+  InterfacesGlobalInitCheck.cpp
+  ProBoundsArrayToPointerDecayCheck.cpp
+  ProBoundsConstantArrayIndexCheck.cpp
+  ProBoundsPointerArithmeticCheck.cpp
+  ProTypeConstCastCheck.cpp
+  ProTypeCstyleCastCheck.cpp
+  ProTypeMemberInitCheck.cpp
+  ProTypeReinterpretCastCheck.cpp
+  ProTypeStaticCastDowncastCheck.cpp
+  ProTypeUnionAccessCheck.cpp
+  ProTypeVarargCheck.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..97c352b
--- /dev/null
@@ -0,0 +1,72 @@
+//===--- 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 "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"
+
+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<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<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/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..b376e64
--- /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..a57ec49
--- /dev/null
@@ -0,0 +1,138 @@
+//===--- 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;
+
+  Finder->addMatcher(arraySubscriptExpr(hasBase(ignoringImpCasts(hasType(
+                                            constantArrayType().bind("type")))),
+                                        hasIndex(expr().bind("index")))
+                         .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..da85093
--- /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..6f330cf
--- /dev/null
@@ -0,0 +1,36 @@
+//===--- 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..5331cda
--- /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..fda082e
--- /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..4055423
--- /dev/null
@@ -0,0 +1,109 @@
+//===--- 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, Result.Context->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,
+                                       Result.Context->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..4cf7b16
--- /dev/null
@@ -0,0 +1,428 @@
+//===--- 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 {
+
+// 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::getPreviousNonCommentToken(
+                     Context, Constructor.getBody()->getLocStart())
+                     .getLocation();
+      break;
+    case InitializerPlacement::Before:
+      Location = utils::lexer::getPreviousNonCommentToken(
+                     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 T *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 T *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);
+  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);
+    checkMissingBaseClassInitializer(*Result.Context, Ctor);
+  } 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);
+}
+
+void ProTypeMemberInitCheck::checkMissingMemberInitializer(
+    ASTContext &Context, const CXXConstructorDecl *Ctor) {
+  const CXXRecordDecl *ClassDecl = Ctor->getParent();
+  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))
+      FieldsToInit.insert(F);
+  });
+  if (FieldsToInit.empty())
+    return;
+
+  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->getLocStart(),
+           IsUnion
+               ? "union constructor should initialize one of these fields: %0"
+               : "constructor does not initialize these fields: %0")
+      << toCommaSeparatedString(OrderedFields, AllFieldsToInit);
+
+  // Do not propose fixes in macros since we cannot place them correctly.
+  if (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.
+    if (!F->getType()->isEnumeralType())
+      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 {
+    // Otherwise, rewrite the constructor's initializer list.
+    fixInitializerList(Context, Diag, Ctor, FieldsToFix);
+  }
+}
+
+void ProTypeMemberInitCheck::checkMissingBaseClassInitializer(
+    const ASTContext &Context, const CXXConstructorDecl *Ctor) {
+  const CXXRecordDecl *ClassDecl = Ctor->getParent();
+
+  // 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.
+  for (const CXXCtorInitializer *Init : Ctor->inits()) {
+    if (Init->isBaseInitializer() && Init->isWritten())
+      BasesToInit.erase(Init->getBaseClass()->getAsCXXRecordDecl());
+  }
+
+  if (BasesToInit.empty())
+    return;
+
+  DiagnosticBuilder Diag =
+      diag(Ctor->getLocStart(),
+           "constructor does not initialize these bases: %0")
+      << toCommaSeparatedString(AllBases, BasesToInit);
+
+  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..401fe03
--- /dev/null
@@ -0,0 +1,72 @@
+//===--- 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 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 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..002f8f1
--- /dev/null
@@ -0,0 +1,54 @@
+//===--- 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..d718fec
--- /dev/null
@@ -0,0 +1,35 @@
+//===--- 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..5233ad8
--- /dev/null
@@ -0,0 +1,35 @@
+//===--- 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..28097fa
--- /dev/null
@@ -0,0 +1,37 @@
+//===--- 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..680037f
--- /dev/null
@@ -0,0 +1,78 @@
+//===--- 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/google/AvoidCStyleCastsCheck.cpp b/clang-tidy/google/AvoidCStyleCastsCheck.cpp
new file mode 100644 (file)
index 0000000..34353b3
--- /dev/null
@@ -0,0 +1,175 @@
+//===--- 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) {
+  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;
+}
+
+static bool pointedTypesAreEqual(QualType SourceType, QualType DestType) {
+  SourceType = SourceType.getNonReferenceType();
+  DestType = DestType.getNonReferenceType();
+  while (SourceType->isPointerType() && DestType->isPointerType()) {
+    SourceType = SourceType->getPointeeType();
+    DestType = DestType->getPointeeType();
+  }
+  return SourceType.getUnqualifiedType() == DestType.getUnqualifiedType();
+}
+
+void AvoidCStyleCastsCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *CastExpr = Result.Nodes.getNodeAs<CStyleCastExpr>("cast");
+
+  auto ParenRange = CharSourceRange::getTokenRange(CastExpr->getLParenLoc(),
+                                                   CastExpr->getRParenLoc());
+  // Ignore casts in macros.
+  if (ParenRange.getBegin().isMacroID() || ParenRange.getEnd().isMacroID())
+    return;
+
+  // Casting to void is an idiomatic way to mute "unused variable" and similar
+  // warnings.
+  if (CastExpr->getTypeAsWritten()->isVoidType())
+    return;
+
+  QualType SourceType = CastExpr->getSubExprAsWritten()->getType();
+  QualType DestType = CastExpr->getTypeAsWritten();
+
+  if (SourceType == DestType) {
+    diag(CastExpr->getLocStart(), "redundant cast to the same type")
+        << FixItHint::CreateRemoval(ParenRange);
+    return;
+  }
+  SourceType = SourceType.getCanonicalType();
+  DestType = DestType.getCanonicalType();
+  if (SourceType == DestType) {
+    diag(CastExpr->getLocStart(),
+         "possibly redundant cast between typedefs of the same type");
+    return;
+  }
+
+
+  // The rest of this check is only relevant to C++.
+  if (!Result.Context->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;
+  // Ignore code in .c files #included in other files (which shouldn't be done,
+  // but people still do this for test and other purposes).
+  SourceManager &SM = *Result.SourceManager;
+  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, Result.Context->getLangOpts());
+
+  auto diag_builder =
+      diag(CastExpr->getLocStart(), "C-style casts are discouraged; use %0");
+
+  auto ReplaceWithCast = [&](StringRef CastType) {
+    diag_builder << CastType;
+
+    const Expr *SubExpr = CastExpr->getSubExprAsWritten()->IgnoreImpCasts();
+    std::string CastText = (CastType + "<" + DestTypeString + ">").str();
+    if (!isa<ParenExpr>(SubExpr)) {
+      CastText.push_back('(');
+      diag_builder << FixItHint::CreateInsertion(
+          Lexer::getLocForEndOfToken(SubExpr->getLocEnd(), 0, SM,
+                                     Result.Context->getLangOpts()),
+          ")");
+    }
+    diag_builder << FixItHint::CreateReplacement(ParenRange, CastText);
+  };
+
+  // Suggest appropriate C++ cast. See [expr.cast] for cast notation semantics.
+  switch (CastExpr->getCastKind()) {
+  case CK_NoOp:
+    if (needsConstCast(SourceType, DestType) &&
+        pointedTypesAreEqual(SourceType, DestType)) {
+      ReplaceWithCast("const_cast");
+      return;
+    }
+    if (DestType->isReferenceType() &&
+        (SourceType.getNonReferenceType() ==
+             DestType.getNonReferenceType().withConst() ||
+         SourceType.getNonReferenceType() == DestType.getNonReferenceType())) {
+      ReplaceWithCast("const_cast");
+      return;
+    }
+    // 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())) {
+      ReplaceWithCast("static_cast");
+      return;
+    }
+    break;
+  case CK_BitCast:
+    // FIXME: Suggest const_cast<...>(reinterpret_cast<...>(...)) replacement.
+    if (!needsConstCast(SourceType, DestType)) {
+      ReplaceWithCast("reinterpret_cast");
+      return;
+    }
+    break;
+  default:
+    break;
+  }
+
+  diag_builder << "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..efe3b3c
--- /dev/null
@@ -0,0 +1,27 @@
+set(LLVM_LINK_COMPONENTS support)
+
+add_clang_library(clangTidyGoogleModule
+  AvoidCStyleCastsCheck.cpp
+  DefaultArgumentsCheck.cpp
+  ExplicitConstructorCheck.cpp
+  ExplicitMakePairCheck.cpp
+  GlobalNamesInHeadersCheck.cpp
+  GoogleTidyModule.cpp
+  IntegerTypesCheck.cpp
+  MemsetZeroLengthCheck.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..0714516
--- /dev/null
@@ -0,0 +1,133 @@
+//===--- 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)
+    Finder->addMatcher(
+        cxxConstructorDecl(unless(isInstantiated())).bind("ctor"), 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) {
+  const CXXConstructorDecl *Ctor =
+      Result.Nodes.getNodeAs<CXXConstructorDecl>("ctor");
+  // Do not be confused: isExplicit means 'explicit' keyword is present,
+  // isImplicit means that it's a compiler-generated constructor.
+  if (Ctor->isOutOfLine() || Ctor->isImplicit() || Ctor->isDeleted() ||
+      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, Result.Context->getLangOpts(),
+                  Ctor->getOuterLocStart(), Ctor->getLocEnd(), isKWExplicit);
+    StringRef ConstructorDescription;
+    if (Ctor->isMoveConstructor())
+      ConstructorDescription = "move";
+    else if (Ctor->isCopyConstructor())
+      ConstructorDescription = "copy";
+    else
+      ConstructorDescription = "initializer-list";
+
+    DiagnosticBuilder 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,
+       "%0 must be marked explicit to avoid unintentional implicit conversions")
+      << (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..a07e0ff
--- /dev/null
@@ -0,0 +1,77 @@
+//===--- 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..4e70754
--- /dev/null
@@ -0,0 +1,81 @@
+//===--- 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..49f879c
--- /dev/null
@@ -0,0 +1,102 @@
+//===--- 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 "MemsetZeroLengthCheck.h"
+#include "OverloadedUnaryAndCheck.h"
+#include "NonConstReferences.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<runtime::MemsetZeroLengthCheck>(
+        "google-runtime-memset");
+    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..0045812
--- /dev/null
@@ -0,0 +1,145 @@
+//===--- 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/MemsetZeroLengthCheck.cpp b/clang-tidy/google/MemsetZeroLengthCheck.cpp
new file mode 100644 (file)
index 0000000..bcf4b5b
--- /dev/null
@@ -0,0 +1,95 @@
+//===--- MemsetZeroLengthCheck.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 "MemsetZeroLengthCheck.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 runtime {
+
+void
+MemsetZeroLengthCheck::registerMatchers(ast_matchers::MatchFinder *Finder) {
+  // Look for memset(x, y, 0) as those is most likely an argument swap.
+  // TODO: Also handle other standard functions that suffer from the same
+  //       problem, e.g. memchr.
+  Finder->addMatcher(
+      callExpr(callee(functionDecl(hasName("::memset"))), argumentCountIs(3),
+               unless(isInTemplateInstantiation())).bind("decl"),
+      this);
+}
+
+/// \brief Get a StringRef representing a SourceRange.
+static StringRef getAsString(const MatchFinder::MatchResult &Result,
+                             SourceRange R) {
+  const SourceManager &SM = *Result.SourceManager;
+  // Don't even try to resolve macro or include contraptions. Not worth emitting
+  // a fixit for.
+  if (R.getBegin().isMacroID() ||
+      !SM.isWrittenInSameFile(R.getBegin(), R.getEnd()))
+    return StringRef();
+
+  const char *Begin = SM.getCharacterData(R.getBegin());
+  const char *End = SM.getCharacterData(Lexer::getLocForEndOfToken(
+      R.getEnd(), 0, SM, Result.Context->getLangOpts()));
+
+  return StringRef(Begin, End - Begin);
+}
+
+void MemsetZeroLengthCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *Call = Result.Nodes.getNodeAs<CallExpr>("decl");
+
+  // Note, this is:
+  // void *memset(void *buffer, int fill_char, size_t byte_count);
+  // Arg1 is fill_char, Arg2 is byte_count.
+  const Expr *Arg1 = Call->getArg(1);
+  const Expr *Arg2 = Call->getArg(2);
+
+  // Return if `byte_count` is not zero at compile time.
+  llvm::APSInt Value1, Value2;
+  if (Arg2->isValueDependent() ||
+      !Arg2->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 (!Arg1->isValueDependent() &&
+      Arg1->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");
+  SourceRange LHSRange = Arg1->getSourceRange();
+  SourceRange RHSRange = Arg2->getSourceRange();
+  StringRef RHSString = getAsString(Result, RHSRange);
+  StringRef LHSString = getAsString(Result, LHSRange);
+  if (LHSString.empty() || RHSString.empty())
+    return;
+
+  D << FixItHint::CreateReplacement(CharSourceRange::getTokenRange(LHSRange),
+                                    RHSString)
+    << FixItHint::CreateReplacement(CharSourceRange::getTokenRange(RHSRange),
+                                    LHSString);
+}
+
+} // namespace runtime
+} // namespace google
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/google/MemsetZeroLengthCheck.h b/clang-tidy/google/MemsetZeroLengthCheck.h
new file mode 100644 (file)
index 0000000..57c7aec
--- /dev/null
@@ -0,0 +1,39 @@
+//===--- MemsetZeroLengthCheck.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_MEMSETZEROLENGTHCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_MEMSETZEROLENGTHCHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace google {
+namespace runtime {
+
+/// Finds calls to memset with a literal zero in the length argument.
+///
+/// This is most likely unintended and the length and value arguments are
+/// swapped.
+///
+/// Corresponding cpplint.py check name: 'runtime/memset'.
+class MemsetZeroLengthCheck : public ClangTidyCheck {
+public:
+  MemsetZeroLengthCheck(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_MEMSETZEROLENGTHCHECK_H
diff --git a/clang-tidy/google/NonConstReferences.cpp b/clang-tidy/google/NonConstReferences.cpp
new file mode 100644 (file)
index 0000000..5667bd1
--- /dev/null
@@ -0,0 +1,126 @@
+//===--- 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 "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 {
+
+void NonConstReferences::registerMatchers(MatchFinder *Finder) {
+  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 CXXMethodDecl *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");
+  // 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..3adb1f9
--- /dev/null
@@ -0,0 +1,36 @@
+//===--- 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)
+      : 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_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..6fb1ca9
--- /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..c632457
--- /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..cf539ca
--- /dev/null
@@ -0,0 +1,64 @@
+//===--- 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..c4ac5a4
--- /dev/null
@@ -0,0 +1,46 @@
+//===--- 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;
+
+  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.
+}
+
+} // 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..a36f380
--- /dev/null
@@ -0,0 +1,48 @@
+//===--- 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;
+};
+
+} // namespace build
+} // namespace google
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_USINGNAMESPACEDIRECTIVECHECK_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..c44abbd
--- /dev/null
@@ -0,0 +1,55 @@
+//===--- 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 {
+
+bool LLVMHeaderGuardCheck::shouldFixHeaderGuard(StringRef Filename) {
+  return Filename.endswith(".h");
+}
+
+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..f13dd5b
--- /dev/null
@@ -0,0 +1,33 @@
+//===--- 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.
+class LLVMHeaderGuardCheck : public utils::HeaderGuardCheck {
+public:
+  LLVMHeaderGuardCheck(StringRef Name, ClangTidyContext *Context)
+      : HeaderGuardCheck(Name, Context) {}
+  bool shouldSuggestEndifComment(StringRef Filename) override { return false; }
+  bool shouldFixHeaderGuard(StringRef Filename) override;
+  std::string getHeaderGuard(StringRef Filename, StringRef OldGuard) override;
+};
+
+} // 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..e6d5bc3
--- /dev/null
@@ -0,0 +1,169 @@
+//===--- 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"
+
+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
+  };
+  std::vector<IncludeDirective> 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;
+  }
+  IncludeDirectives.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.
+  std::vector<unsigned> Blocks(1, 0);
+  for (unsigned I = 1, E = IncludeDirectives.size(); I != E; ++I)
+    if (SM.getExpansionLineNumber(IncludeDirectives[I].Loc) !=
+        SM.getExpansionLineNumber(IncludeDirectives[I - 1].Loc) + 1)
+      Blocks.push_back(I);
+  Blocks.push_back(IncludeDirectives.size()); // Sentinel value.
+
+  // Get a vector of indices.
+  std::vector<unsigned> IncludeIndices;
+  for (unsigned I = 0, E = IncludeDirectives.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],
+              [this](unsigned LHSI, unsigned RHSI) {
+      IncludeDirective &LHS = IncludeDirectives[LHSI];
+      IncludeDirective &RHS = IncludeDirectives[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(IncludeDirectives[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 = IncludeDirectives[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 = IncludeDirectives[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..2f39f46
--- /dev/null
@@ -0,0 +1,64 @@
+//===--- 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 VarDecl *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))
+      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,
+          Result.Context->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..a0bdd8a
--- /dev/null
@@ -0,0 +1,184 @@
+//===--- 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"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+ArgumentCommentCheck::ArgumentCommentCheck(StringRef Name,
+                                           ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      IdentRE("^(/\\* *)([_A-Za-z][_A-Za-z0-9]*)( *= *\\*/)$") {}
+
+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(anyOf(
+                   hasName("NewCallback"), hasName("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.getKind() == tok::eof)
+      break;
+
+    if (Tok.getKind() == 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()));
+    }
+  }
+
+  return Comments;
+}
+
+bool
+ArgumentCommentCheck::isLikelyTypo(llvm::ArrayRef<ParmVarDecl *> Params,
+                                   StringRef ArgName, unsigned ArgIndex) {
+  std::string ArgNameLower = ArgName.lower();
+  unsigned UpperBound = (ArgName.size() + 2) / 3 + 1;
+  unsigned ThisED = StringRef(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 = StringRef(ArgNameLower).edit_distance(
+        II->getName().lower(),
+        /*AllowReplacements=*/true, ThisED + Threshold);
+    if (OtherED < ThisED + Threshold)
+      return false;
+  }
+
+  return true;
+}
+
+void ArgumentCommentCheck::checkCallArgs(ASTContext *Ctx,
+                                         const FunctionDecl *Callee,
+                                         SourceLocation ArgBeginLoc,
+                                         llvm::ArrayRef<const Expr *> Args) {
+  Callee = Callee->getFirstDecl();
+  for (unsigned i = 0,
+                e = std::min<unsigned>(Args.size(), Callee->getNumParams());
+       i != e; ++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 = CharSourceRange::getCharRange(
+        i == 0 ? ArgBeginLoc : Args[i - 1]->getLocEnd(),
+        Args[i]->getLocStart());
+    BeforeArgument = Lexer::makeFileCharRange(
+        BeforeArgument, Ctx->getSourceManager(), Ctx->getLangOpts());
+
+    for (auto Comment : getCommentsInRange(Ctx, BeforeArgument)) {
+      llvm::SmallVector<StringRef, 2> Matches;
+      if (IdentRE.match(Comment.second, &Matches)) {
+        if (Matches[2] != II->getName()) {
+          {
+            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;
+        }
+      }
+    }
+  }
+}
+
+void ArgumentCommentCheck::check(const MatchFinder::MatchResult &Result) {
+  const Expr *E = Result.Nodes.getNodeAs<Expr>("expr");
+  if (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 {
+    auto Construct = cast<CXXConstructExpr>(E);
+    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..51ad734
--- /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;
+
+private:
+  llvm::Regex IdentRE;
+
+  bool isLikelyTypo(llvm::ArrayRef<ParmVarDecl *> Params, StringRef ArgName,
+                    unsigned ArgIndex);
+  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..7bfbd3b
--- /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 = Result.Context->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..f293be4
--- /dev/null
@@ -0,0 +1,72 @@
+//===--- 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.getStmtAs<IfStmt>("if");
+  auto *Var = Result.Nodes.getStmtAs<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..80dcdc7
--- /dev/null
@@ -0,0 +1,43 @@
+//===--- 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..4d6fb2c
--- /dev/null
@@ -0,0 +1,55 @@
+set(LLVM_LINK_COMPONENTS support)
+
+add_clang_library(clangTidyMiscModule
+  ArgumentCommentCheck.cpp
+  AssertSideEffectCheck.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
+  MultipleStatementMacroCheck.cpp
+  NewDeleteOverloadsCheck.cpp
+  NoexceptMoveConstructorCheck.cpp
+  NonCopyableObjects.cpp
+  PointerAndIntegralOperationCheck.cpp
+  RedundantExpressionCheck.cpp
+  SizeofContainerCheck.cpp
+  SizeofExpressionCheck.cpp
+  StaticAssertCheck.cpp
+  StringConstructorCheck.cpp
+  StringIntegerAssignmentCheck.cpp
+  StringLiteralWithEmbeddedNulCheck.cpp
+  SuspiciousMissingCommaCheck.cpp
+  SuspiciousSemicolonCheck.cpp
+  SuspiciousStringCompareCheck.cpp
+  SwappedArgumentsCheck.cpp
+  ThrowByValueCatchByReferenceCheck.cpp
+  UndelegatedConstructor.cpp
+  UniqueptrResetReleaseCheck.cpp
+  UnusedAliasDeclsCheck.cpp
+  UnusedParametersCheck.cpp
+  UnusedRAIICheck.cpp
+  UnusedUsingDeclsCheck.cpp
+  VirtualNearMissCheck.cpp
+
+  LINK_LIBS
+  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..77ca26f
--- /dev/null
@@ -0,0 +1,150 @@
+//===--- 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().hasErrorOccurred())
+    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;
+    // Function template full specialization is prohibited in header file.
+    if (FD->getTemplateSpecializationKind() == TSK_ImplicitInstantiation)
+      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();
+      }
+    }
+
+    diag(FD->getLocation(),
+         "function %0 defined in a header file; "
+         "function definitions in header files can lead to ODR violations")
+        << 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;
+    if (VD->getTemplateSpecializationKind() == TSK_ImplicitInstantiation)
+      return;
+    // Ignore variable definition within function scope.
+    if (VD->hasLocalStorage() || VD->isStaticLocal())
+      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..98053ff
--- /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 <stack>
+#include <string>
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+
+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..cc701da
--- /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 <set>
+#include <vector>
+#include "llvm/ADT/SmallPtrSet.h"
+#include "../ClangTidy.h"
+
+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/InaccurateEraseCheck.cpp b/clang-tidy/misc/InaccurateEraseCheck.cpp
new file mode 100644 (file)
index 0000000..7c8a132
--- /dev/null
@@ -0,0 +1,74 @@
+//===--- 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 {
+
+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 CheckForEndCall = hasArgument(
+      1, anyOf(cxxConstructExpr(has(ignoringParenImpCasts(
+                   cxxMemberCallExpr(callee(cxxMethodDecl(hasName("end"))))
+                       .bind("InaccEndCall")))),
+               anything()));
+
+  Finder->addMatcher(
+      cxxMemberCallExpr(
+          on(hasType(namedDecl(matchesName("^::std::")))),
+          callee(cxxMethodDecl(hasName("erase"))), argumentCountIs(1),
+          hasArgument(0, has(ignoringParenImpCasts(
+                             callExpr(callee(functionDecl(matchesName(
+                                          "^::std::(remove(_if)?|unique)$"))),
+                                      CheckForEndCall)
+                                 .bind("InaccAlgCall")))),
+          unless(isInTemplateInstantiation()))
+          .bind("InaccErase"),
+      this);
+}
+
+void InaccurateEraseCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *MemberCall =
+      Result.Nodes.getNodeAs<CXXMemberCallExpr>("InaccErase");
+  const auto *EndExpr =
+      Result.Nodes.getNodeAs<CXXMemberCallExpr>("InaccEndCall");
+  const SourceLocation Loc = MemberCall->getLocStart();
+
+  FixItHint Hint;
+
+  if (!Loc.isMacroID() && EndExpr) {
+    const auto *AlgCall = Result.Nodes.getNodeAs<CallExpr>("InaccAlgCall");
+    std::string ReplacementText = Lexer::getSourceText(
+        CharSourceRange::getTokenRange(EndExpr->getSourceRange()),
+        *Result.SourceManager, Result.Context->getLangOpts());
+    const SourceLocation EndLoc = Lexer::getLocForEndOfToken(
+        AlgCall->getLocEnd(), 0, *Result.SourceManager,
+        Result.Context->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..ee6f666
--- /dev/null
@@ -0,0 +1,72 @@
+//===--- 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.getStmtAs<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..7c571bd
--- /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..8101186
--- /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 = Result.Context->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/MacroParenthesesCheck.cpp b/clang-tidy/misc/MacroParenthesesCheck.cpp
new file mode 100644 (file)
index 0000000..ebfb8c0
--- /dev/null
@@ -0,0 +1,259 @@
+//===--- 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->getArgumentNum(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..699966a
--- /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/PPCallbacks.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Lex/MacroArgs.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->getNumArgs(); ++ArgNo) {
+    const IdentifierInfo *Arg = *(MI->arg_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..45fa838
--- /dev/null
@@ -0,0 +1,155 @@
+//===--- 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 "MisplacedConstCheck.h"
+#include "UnconventionalAssignOperatorCheck.h"
+#include "BoolPointerImplicitConversionCheck.h"
+#include "DanglingHandleCheck.h"
+#include "DefinitionsInHeadersCheck.h"
+#include "FoldInitTypeCheck.h"
+#include "ForwardDeclarationNamespaceCheck.h"
+#include "InaccurateEraseCheck.h"
+#include "IncorrectRoundings.h"
+#include "InefficientAlgorithmCheck.h"
+#include "MacroParenthesesCheck.h"
+#include "MacroRepeatedSideEffectsCheck.h"
+#include "MisplacedWideningCastCheck.h"
+#include "MoveConstantArgumentCheck.h"
+#include "MoveConstructorInitCheck.h"
+#include "MultipleStatementMacroCheck.h"
+#include "NewDeleteOverloadsCheck.h"
+#include "NoexceptMoveConstructorCheck.h"
+#include "NonCopyableObjects.h"
+#include "PointerAndIntegralOperationCheck.h"
+#include "RedundantExpressionCheck.h"
+#include "SizeofContainerCheck.h"
+#include "SizeofExpressionCheck.h"
+#include "StaticAssertCheck.h"
+#include "StringConstructorCheck.h"
+#include "StringIntegerAssignmentCheck.h"
+#include "StringLiteralWithEmbeddedNulCheck.h"
+#include "SuspiciousMissingCommaCheck.h"
+#include "SuspiciousSemicolonCheck.h"
+#include "SuspiciousStringCompareCheck.h"
+#include "SwappedArgumentsCheck.h"
+#include "ThrowByValueCatchByReferenceCheck.h"
+#include "UndelegatedConstructor.h"
+#include "UniqueptrResetReleaseCheck.h"
+#include "UnusedAliasDeclsCheck.h"
+#include "UnusedParametersCheck.h"
+#include "UnusedRAIICheck.h"
+#include "UnusedUsingDeclsCheck.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<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<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<PointerAndIntegralOperationCheck>(
+        "misc-pointer-and-integral-operation");
+    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<StringConstructorCheck>(
+        "misc-string-constructor");
+    CheckFactories.registerCheck<StringIntegerAssignmentCheck>(
+        "misc-string-integer-assignment");
+    CheckFactories.registerCheck<StringLiteralWithEmbeddedNulCheck>(
+        "misc-string-literal-with-embedded-nul");
+    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<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..7919258
--- /dev/null
@@ -0,0 +1,230 @@
+//===--- 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 "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "../utils/Matchers.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", true)) {}
+
+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..cf62732
--- /dev/null
@@ -0,0 +1,96 @@
+//===--- 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) {
+    bool IsVariable = isa<DeclRefExpr>(Arg);
+    auto Diag = diag(FileMoveRange.getBegin(),
+                     "std::move of the %select{|const }0"
+                     "%select{expression|variable}1 "
+                     "%select{|of a trivially-copyable type }2"
+                     "has no effect; remove std::move()")
+                << IsConstArg << IsVariable << IsTriviallyCopyable;
+
+    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..acb6de4
--- /dev/null
@@ -0,0 +1,185 @@
+//===--- 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 {
+
+namespace {
+
+unsigned int
+parmVarDeclRefExprOccurences(const ParmVarDecl &MovableParam,
+                             const CXXConstructorDecl &ConstructorDecl,
+                             ASTContext &Context) {
+  unsigned int Occurrences = 0;
+  auto AllDeclRefs =
+      findAll(declRefExpr(to(parmVarDecl(equalsNode(&MovableParam)))));
+  Occurrences += match(AllDeclRefs, *ConstructorDecl.getBody(), Context).size();
+  for (const auto *Initializer : ConstructorDecl.inits()) {
+    Occurrences += match(AllDeclRefs, *Initializer->getInit(), Context).size();
+  }
+  return Occurrences;
+}
+
+} // namespace
+
+MoveConstructorInitCheck::MoveConstructorInitCheck(StringRef Name,
+                                                   ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      IncludeStyle(utils::IncludeSorter::parseIncludeStyle(
+          Options.get("IncludeStyle", "llvm"))),
+      UseCERTSemantics(Options.get("UseCERTSemantics", 0) != 0) {}
+
+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);
+
+  auto NonConstValueMovableAndExpensiveToCopy =
+      qualType(allOf(unless(pointerType()), unless(isConstQualified()),
+                     hasDeclaration(cxxRecordDecl(hasMethod(cxxConstructorDecl(
+                         isMoveConstructor(), unless(isDeleted()))))),
+                     matchers::isExpensiveToCopy()));
+
+  // This checker is also used to implement cert-oop11-cpp, but when using that
+  // form of the checker, we do not want to diagnose movable parameters.
+  if (!UseCERTSemantics) {
+    Finder->addMatcher(
+        cxxConstructorDecl(
+            allOf(
+                unless(isMoveConstructor()),
+                hasAnyConstructorInitializer(withInitializer(cxxConstructExpr(
+                    hasDeclaration(cxxConstructorDecl(isCopyConstructor())),
+                    hasArgument(
+                        0,
+                        declRefExpr(
+                            to(parmVarDecl(
+                                   hasType(
+                                       NonConstValueMovableAndExpensiveToCopy))
+                                   .bind("movable-param")))
+                            .bind("init-arg")))))))
+            .bind("ctor-decl"),
+        this);
+  }
+}
+
+void MoveConstructorInitCheck::check(const MatchFinder::MatchResult &Result) {
+  if (Result.Nodes.getNodeAs<CXXCtorInitializer>("move-init") != nullptr)
+    handleMoveConstructor(Result);
+  if (Result.Nodes.getNodeAs<ParmVarDecl>("movable-param") != nullptr)
+    handleParamNotMoved(Result);
+}
+
+void MoveConstructorInitCheck::handleParamNotMoved(
+    const MatchFinder::MatchResult &Result) {
+  const auto *MovableParam =
+      Result.Nodes.getNodeAs<ParmVarDecl>("movable-param");
+  const auto *ConstructorDecl =
+      Result.Nodes.getNodeAs<CXXConstructorDecl>("ctor-decl");
+  const auto *InitArg = Result.Nodes.getNodeAs<DeclRefExpr>("init-arg");
+  // If the parameter is referenced more than once it is not safe to move it.
+  if (parmVarDeclRefExprOccurences(*MovableParam, *ConstructorDecl,
+                                   *Result.Context) > 1)
+    return;
+  auto DiagOut = diag(InitArg->getLocStart(),
+                      "value argument %0 can be moved to avoid copy")
+                 << MovableParam;
+  DiagOut << FixItHint::CreateReplacement(
+      InitArg->getSourceRange(),
+      (Twine("std::move(") + MovableParam->getName() + ")").str());
+  if (auto IncludeFixit = Inserter->CreateIncludeInsertion(
+          Result.SourceManager->getFileID(InitArg->getLocStart()), "utility",
+          /*IsAngled=*/true)) {
+    DiagOut << *IncludeFixit;
+  }
+}
+
+void MoveConstructorInitCheck::handleMoveConstructor(
+    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;
+  
+  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));
+  Options.store(Opts, "UseCERTSemantics", UseCERTSemantics ? 1 : 0);
+}
+
+} // 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..6f9dacb
--- /dev/null
@@ -0,0 +1,50 @@
+//===--- 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:
+  void
+  handleMoveConstructor(const ast_matchers::MatchFinder::MatchResult &Result);
+  void
+  handleParamNotMoved(const ast_matchers::MatchFinder::MatchResult &Result);
+
+  std::unique_ptr<utils::IncludeInserter> Inserter;
+  const utils::IncludeSorter::IncludeStyle IncludeStyle;
+  const bool UseCERTSemantics;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MOVECONSTRUCTORINITCHECK_H
diff --git a/clang-tidy/misc/MultipleStatementMacroCheck.cpp b/clang-tidy/misc/MultipleStatementMacroCheck.cpp
new file mode 100644 (file)
index 0000000..b7001ae
--- /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 Stmt *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..4da5006
--- /dev/null
@@ -0,0 +1,212 @@
+//===--- 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..582e659
--- /dev/null
@@ -0,0 +1,37 @@
+//===--- 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..769efe4
--- /dev/null
@@ -0,0 +1,74 @@
+//===--- 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>();
+    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..0c976af
--- /dev/null
@@ -0,0 +1,39 @@
+//===--- 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..8bc558f
--- /dev/null
@@ -0,0 +1,99 @@
+//===--- 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 {
+
+namespace {
+// 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.
+bool isPOSIXTypeName(StringRef ClassName) {
+  static const char *const TypeNames[] = {
+    "::pthread_cond_t",
+    "::pthread_mutex_t",
+    "pthread_cond_t",
+    "pthread_mutex_t"
+  };
+  return std::binary_search(std::begin(TypeNames), std::end(TypeNames),
+                            ClassName);
+}
+
+bool isFILETypeName(StringRef ClassName) {
+  static const char *const TypeNames[] = {
+    "::FILE",
+    "FILE",
+    "std::FILE"
+  };
+  return std::binary_search(std::begin(TypeNames), std::end(TypeNames),
+                            ClassName);
+}
+
+AST_MATCHER(NamedDecl, isFILEType) {
+  return isFILETypeName(Node.getQualifiedNameAsString());
+}
+
+AST_MATCHER(NamedDecl, isPOSIXType) {
+  return isPOSIXTypeName(Node.getQualifiedNameAsString());
+}
+} // namespace
+
+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.
+  auto BadFILEType = hasType(namedDecl(isFILEType()).bind("type_decl"));
+  auto BadPOSIXType = hasType(namedDecl(isPOSIXType()).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..9805f74
--- /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/PointerAndIntegralOperationCheck.cpp b/clang-tidy/misc/PointerAndIntegralOperationCheck.cpp
new file mode 100644 (file)
index 0000000..f60da78
--- /dev/null
@@ -0,0 +1,104 @@
+//===--- PointerAndIntegralOperationCheck.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 "PointerAndIntegralOperationCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "../utils/Matchers.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+void PointerAndIntegralOperationCheck::registerMatchers(MatchFinder *Finder) {
+  const auto PointerExpr = expr(hasType(pointerType()));
+  const auto BoolExpr = ignoringParenImpCasts(hasType(booleanType()));
+  const auto CharExpr = ignoringParenImpCasts(hasType(isAnyCharacter()));
+
+  const auto BinOpWithPointerExpr =
+      binaryOperator(unless(anyOf(hasOperatorName(","), hasOperatorName("="))),
+                     hasEitherOperand(PointerExpr));
+
+  const auto AssignToPointerExpr =
+      binaryOperator(hasOperatorName("="), hasLHS(PointerExpr));
+
+  const auto CompareToPointerExpr =
+      binaryOperator(matchers::isRelationalOperator(),
+                     hasEitherOperand(PointerExpr));
+
+  // Detect expression like: ptr = (x != y);
+  Finder->addMatcher(binaryOperator(AssignToPointerExpr, hasRHS(BoolExpr))
+                         .bind("assign-bool-to-pointer"),
+                     this);
+
+  // Detect expression like: ptr = A[i]; where A is char*.
+  Finder->addMatcher(binaryOperator(AssignToPointerExpr, hasRHS(CharExpr))
+                         .bind("assign-char-to-pointer"),
+                     this);
+
+  // Detect expression like: ptr < false;
+  Finder->addMatcher(
+      binaryOperator(BinOpWithPointerExpr,
+                     hasEitherOperand(ignoringParenImpCasts(cxxBoolLiteral())))
+          .bind("pointer-and-bool-literal"),
+      this);
+
+  // Detect expression like: ptr < 'a';
+  Finder->addMatcher(binaryOperator(BinOpWithPointerExpr,
+                                    hasEitherOperand(ignoringParenImpCasts(
+                                        characterLiteral())))
+                         .bind("pointer-and-char-literal"),
+                     this);
+
+  // Detect expression like: ptr < 0;
+  Finder->addMatcher(binaryOperator(CompareToPointerExpr,
+                                    hasEitherOperand(ignoringParenImpCasts(
+                                        integerLiteral(equals(0)))))
+                         .bind("compare-pointer-to-zero"),
+                     this);
+
+  // Detect expression like: ptr < nullptr;
+  Finder->addMatcher(binaryOperator(CompareToPointerExpr,
+                                    hasEitherOperand(ignoringParenImpCasts(
+                                        cxxNullPtrLiteralExpr())))
+                         .bind("compare-pointer-to-null"),
+                     this);
+}
+
+void PointerAndIntegralOperationCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  if (const auto *E =
+          Result.Nodes.getNodeAs<BinaryOperator>("assign-bool-to-pointer")) {
+    diag(E->getOperatorLoc(), "suspicious assignment from bool to pointer");
+  } else if (const auto *E = Result.Nodes.getNodeAs<BinaryOperator>(
+                 "assign-char-to-pointer")) {
+    diag(E->getOperatorLoc(), "suspicious assignment from char to pointer");
+  } else if (const auto *E = Result.Nodes.getNodeAs<BinaryOperator>(
+                 "pointer-and-bool-literal")) {
+    diag(E->getOperatorLoc(),
+         "suspicious operation between pointer and bool literal");
+  } else if (const auto *E = Result.Nodes.getNodeAs<BinaryOperator>(
+                 "pointer-and-char-literal")) {
+    diag(E->getOperatorLoc(),
+         "suspicious operation between pointer and character literal");
+  } else if (const auto *E = Result.Nodes.getNodeAs<BinaryOperator>(
+                 "compare-pointer-to-zero")) {
+    diag(E->getOperatorLoc(), "suspicious comparison of pointer with zero");
+  } else if (const auto *E = Result.Nodes.getNodeAs<BinaryOperator>(
+                 "compare-pointer-to-null")) {
+    diag(E->getOperatorLoc(),
+         "suspicious comparison of pointer with null expression");
+  }
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/misc/PointerAndIntegralOperationCheck.h b/clang-tidy/misc/PointerAndIntegralOperationCheck.h
new file mode 100644 (file)
index 0000000..996253e
--- /dev/null
@@ -0,0 +1,35 @@
+//===--- PointerAndIntegralOperationCheck.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_POINTER_AND_INTEGRAL_OPERATION_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_POINTER_AND_INTEGRAL_OPERATION_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Find suspicious expressions involving pointer and integral types.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/misc-pointer-and-integral-operation.html
+class PointerAndIntegralOperationCheck : public ClangTidyCheck {
+public:
+  PointerAndIntegralOperationCheck(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_POINTER_AND_INTEGRAL_OPERATION_H
diff --git a/clang-tidy/misc/RedundantExpressionCheck.cpp b/clang-tidy/misc/RedundantExpressionCheck.cpp
new file mode 100644 (file)
index 0000000..535918f
--- /dev/null
@@ -0,0 +1,727 @@
+//===--- 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/Lex/Lexer.h"
+
+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_LE || 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;
+
+  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, 0);
+  } 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..188d28a
--- /dev/null
@@ -0,0 +1,50 @@
+//===--- 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..c17bbbd
--- /dev/null
@@ -0,0 +1,37 @@
+//===--- 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..ea40b2c
--- /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 "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "../utils/Matchers.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..6a7dc6c
--- /dev/null
@@ -0,0 +1,175 @@
+//===--- 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 IsAlwaysFalse =
+      expr(anyOf(cxxBoolLiteral(equals(false)), integerLiteral(equals(0)),
+                 cxxNullPtrLiteralExpr(), gnuNullExpr()))
+          .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..284fdeb
--- /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/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..65af4a1
--- /dev/null
@@ -0,0 +1,87 @@
+//===--- 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,
+      Result.Context->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..e977b57
--- /dev/null
@@ -0,0 +1,36 @@
+//===--- 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/SuspiciousMissingCommaCheck.cpp b/clang-tidy/misc/SuspiciousMissingCommaCheck.cpp
new file mode 100644 (file)
index 0000000..3881a1f
--- /dev/null
@@ -0,0 +1,126 @@
+//===--- 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..21cb680
--- /dev/null
@@ -0,0 +1,43 @@
+//===--- 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..1d26de9
--- /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().hasErrorOccurred())
+    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::getPreviousNonCommentToken(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..9315ff5
--- /dev/null
@@ -0,0 +1,219 @@
+//===--- 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(),
+        Result.Context->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(),
+        Result.Context->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..b6ffbe9
--- /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.getStmtAs<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..5e89bd1
--- /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/ASTMatchers/ASTMatchFinder.h"
+#include "clang/AST/OperationKinds.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 DeclRefExpr *variableReference =
+        dyn_cast<DeclRefExpr>(currentSubExpr);
+    const CXXConstructExpr *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) {
+  const char *diagMsgCatchReference = "catch handler catches a pointer value; "
+                                      "should throw a non-pointer value and "
+                                      "catch by reference instead";
+  if (!catchStmt)
+    return;
+  auto caughtType = catchStmt->getCaughtType();
+  if (caughtType.isNull())
+    return;
+  auto *varDecl = catchStmt->getExceptionDecl();
+  if (const auto *PT = caughtType.getCanonicalType()->getAs<PointerType>()) {
+    // 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()) {
+    // If it's not a pointer and not a reference then it must be thrown "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..51c2396
--- /dev/null
@@ -0,0 +1,89 @@
+//===--- 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(
+      unaryOperator(hasOperatorName("*"), hasUnaryOperand(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..3b6f198
--- /dev/null
@@ -0,0 +1,83 @@
+//===--- 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.getStmtAs<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..9d50069
--- /dev/null
@@ -0,0 +1,137 @@
+//===--- 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, Result.Context->getLangOpts());
+  std::string RightText = clang::Lexer::getSourceText(
+      CharSourceRange::getTokenRange(Right->getSourceRange()),
+      *Result.SourceManager, Result.Context->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..fb18059
--- /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,
+            Result.Context->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..54f66f7
--- /dev/null
@@ -0,0 +1,38 @@
+//===--- 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..6131132
--- /dev/null
@@ -0,0 +1,127 @@
+//===--- 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/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+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().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));
+}
+
+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;
+
+  auto DeclRefExpr =
+      declRefExpr(to(equalsNode(Function)),
+                  unless(hasAncestor(callExpr(callee(equalsNode(Function))))));
+
+  // Comment out parameter name for non-local functions.
+  if (Function->isExternallyVisible() ||
+      !Result.SourceManager->isInMainFile(Function->getLocation()) ||
+      !ast_matchers::match(DeclRefExpr, *Result.Context).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.
+  auto CallMatches = ast_matchers::match(
+      decl(forEachDescendant(
+          callExpr(callee(functionDecl(equalsNode(Function)))).bind("x"))),
+      *Result.Context->getTranslationUnitDecl(), *Result.Context);
+  for (const auto &Match : CallMatches)
+    MyDiag << removeArgument(Result, Match.getNodeAs<CallExpr>("x"),
+                             ParamIndex);
+}
+
+void UnusedParametersCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *Function = Result.Nodes.getNodeAs<FunctionDecl>("function");
+  if (!Function->doesThisDeclarationHaveABody() ||
+      !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..45b7f93
--- /dev/null
@@ -0,0 +1,39 @@
+//===--- 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)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+  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..1786627
--- /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.getStmtAs<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.getStmtAs<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.getStmtAs<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,
+                                 Result.Context->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..8c9859c
--- /dev/null
@@ -0,0 +1,136 @@
+//===--- 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);
+}
+
+void UnusedUsingDeclsCheck::check(const MatchFinder::MatchResult &Result) {
+  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,
+            Result.Context->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 *Specialization =
+            dyn_cast<ClassTemplateSpecializationDecl>(Used))
+      Used = Specialization->getSpecializedTemplate();
+    removeFromFoundDecls(Used);
+    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) {
+  // 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/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..c7c51a2
--- /dev/null
@@ -0,0 +1,163 @@
+//===--- 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/Lex/Lexer.h"
+#include <cassert>
+#include <unordered_map>
+
+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("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; });
+
+  Stream << "[" << (HasCapturedArgument ? "=" : "") << "]";
+  addPlaceholderArgs(Args, Stream);
+  Stream << " { return " << F->getName() << "(";
+  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..57a2b4e
--- /dev/null
@@ -0,0 +1,33 @@
+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
+  ShrinkToFitCheck.cpp
+  UseAutoCheck.cpp
+  UseBoolLiteralsCheck.cpp
+  UseDefaultCheck.cpp
+  UseEmplaceCheck.cpp
+  UseNullptrCheck.cpp
+  UseOverrideCheck.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..005edcb
--- /dev/null
@@ -0,0 +1,105 @@
+//===--- 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 <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;
+};
+} // 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", "ccomplex"},
+            {"ctype.h", "cctype"},   {"errno.h", "cerrno"},
+            {"float.h", "cfloat"},   {"inttypes.h", "cinttypes"},
+            {"iso646.h", "ciso646"}, {"limits.h", "climits"},
+            {"locale.h", "clocale"}, {"math.h", "cmath"},
+            {"setjmp.h", "csetjmp"}, {"signal.h", "csignal"},
+            {"stdarg.h", "cstdarg"}, {"stddef.h", "cstddef"},
+            {"stdint.h", "cstdint"}, {"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"},
+              {"stdalign.h", "cstdalign"},
+              {"stdbool.h", "cstdbool"},
+              {"tgmath.h", "ctgmath"},
+              {"uchar.h", "cuchar"}})) {
+      CStyledHeaderToCxx.insert(KeyValue);
+    }
+  }
+}
+
+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 occurance.
+  // 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);
+  }
+}
+
+} // 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..a1c5009
--- /dev/null
@@ -0,0 +1,898 @@
+//===--- 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 "../utils/Matchers.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang;
+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();
+    AliasVarIsRef = AliasVar->getType()->isReferenceType();
+
+    // 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.getDeclAs<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.getDeclAs<VarDecl>(IncrementVarName);
+  const auto *CondVar = Nodes.getDeclAs<VarDecl>(ConditionVarName);
+  const auto *InitVar = Nodes.getDeclAs<VarDecl>(InitVarName);
+  if (!areSameVariable(LoopVar, CondVar) || !areSameVariable(LoopVar, InitVar))
+    return false;
+  const auto *EndVar = Nodes.getDeclAs<VarDecl>(EndVarName);
+  const auto *ConditionEndVar = Nodes.getDeclAs<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.getStmtAs<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.getStmtAs<ForStmt>(LoopNameArray))) {
+    FixerKind = LFK_Array;
+  } else if ((Loop = Nodes.getStmtAs<ForStmt>(LoopNameIterator))) {
+    FixerKind = LFK_Iterator;
+  } else {
+    Loop = Nodes.getStmtAs<ForStmt>(LoopNamePseudoArray);
+    assert(Loop && "Bad Callback. No for statement");
+    FixerKind = LFK_PseudoArray;
+  }
+
+  if (!isConvertible(Context, Nodes, Loop, FixerKind))
+    return;
+
+  const auto *LoopVar = Nodes.getDeclAs<VarDecl>(IncrementVarName);
+  const auto *EndVar = Nodes.getDeclAs<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.getStmtAs<CXXMemberCallExpr>(EndCallName);
+  const auto *BoundExpr = Nodes.getStmtAs<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..3fee2c4
--- /dev/null
@@ -0,0 +1,883 @@
+//===--- 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"
+
+using namespace clang::ast_matchers;
+using namespace clang::tooling;
+using namespace clang;
+using namespace llvm;
+
+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) {
+  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) {
+  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);
+}
+
+/// \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) {
+  // 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..f0267d4
--- /dev/null
@@ -0,0 +1,459 @@
+//===--- 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/Lex/Lexer.h"
+#include "clang/Tooling/Refactoring.h"
+
+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() {}
+
+  /// 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);
+  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..8d3020c
--- /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(
+      matchesName("::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..9b1ea3c
--- /dev/null
@@ -0,0 +1,150 @@
+//===--- 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/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+const char MakeSmartPtrCheck::PointerType[] = "pointerType";
+const char MakeSmartPtrCheck::ConstructorCall[] = "constructorCall";
+const char MakeSmartPtrCheck::NewExpression[] = "newExpression";
+
+MakeSmartPtrCheck::MakeSmartPtrCheck(StringRef Name, ClangTidyContext *Context,
+                                     std::string makeSmartPtrFunctionName)
+    : ClangTidyCheck(Name, Context),
+      makeSmartPtrFunctionName(std::move(makeSmartPtrFunctionName)) {}
+
+void MakeSmartPtrCheck::registerMatchers(ast_matchers::MatchFinder *Finder) {
+  if (!getLangOpts().CPlusPlus11)
+    return;
+
+  Finder->addMatcher(
+      cxxBindTemporaryExpr(has(ignoringParenImpCasts(
+          cxxConstructExpr(
+              hasType(getSmartPointerTypeMatcher()), argumentCountIs(1),
+              hasArgument(0,
+                          cxxNewExpr(hasType(pointsTo(qualType(hasCanonicalType(
+                                         equalsBoundNode(PointerType))))))
+                              .bind(NewExpression)))
+              .bind(ConstructorCall)))),
+      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 *Type = Result.Nodes.getNodeAs<QualType>(PointerType);
+  const auto *New = Result.Nodes.getNodeAs<CXXNewExpr>(NewExpression);
+
+  if (New->getNumPlacementArgs() != 0)
+    return;
+
+  SourceLocation ConstructCallStart = Construct->getExprLoc();
+
+  bool Invalid = false;
+  StringRef ExprStr = Lexer::getSourceText(
+      CharSourceRange::getCharRange(
+          ConstructCallStart, Construct->getParenOrBraceRange().getBegin()),
+      SM, LangOptions(), &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, "<" + Type->getAsString(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)),
+        ")");
+  }
+
+  SourceLocation NewStart = New->getSourceRange().getBegin();
+  SourceLocation NewEnd = New->getSourceRange().getEnd();
+  switch (New->getInitializationStyle()) {
+  case CXXNewExpr::NoInit: {
+    Diag << FixItHint::CreateRemoval(SourceRange(NewStart, NewEnd));
+    break;
+  }
+  case CXXNewExpr::CallInit: {
+    SourceRange InitRange = New->getDirectInitRange();
+    Diag << FixItHint::CreateRemoval(
+        SourceRange(NewStart, InitRange.getBegin()));
+    Diag << FixItHint::CreateRemoval(SourceRange(InitRange.getEnd(), NewEnd));
+    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;
+  }
+  }
+}
+
+} // 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..9de72e0
--- /dev/null
@@ -0,0 +1,52 @@
+//===--- 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 "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,
+                    std::string makeSmartPtrFunctionName);
+  void registerMatchers(ast_matchers::MatchFinder *Finder) final;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) final;
+
+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 NewExpression[];
+
+private:
+  std::string makeSmartPtrFunctionName;
+};
+
+} // 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..cef8e9d
--- /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(
+      matchesName("::std::unique_ptr"), templateArgumentCountIs(2),
+      hasTemplateArgument(
+          0, templateArgument(refersToType(qualType().bind(PointerType)))),
+      hasTemplateArgument(
+          1, templateArgument(refersToType(
+                 qualType(hasDeclaration(classTemplateSpecializationDecl(
+                     matchesName("::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..b939633
--- /dev/null
@@ -0,0 +1,41 @@
+//===--- 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..15c9a57
--- /dev/null
@@ -0,0 +1,94 @@
+//===--- 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 "ShrinkToFitCheck.h"
+#include "UseAutoCheck.h"
+#include "UseBoolLiteralsCheck.h"
+#include "UseDefaultCheck.h"
+#include "UseEmplaceCheck.h"
+#include "UseNullptrCheck.h"
+#include "UseOverrideCheck.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<ShrinkToFitCheck>("modernize-shrink-to-fit");
+    CheckFactories.registerCheck<UseAutoCheck>("modernize-use-auto");
+    CheckFactories.registerCheck<UseBoolLiteralsCheck>(
+        "modernize-use-bool-literals");
+    CheckFactories.registerCheck<UseDefaultCheck>("modernize-use-default");
+    CheckFactories.registerCheck<UseEmplaceCheck>("modernize-use-emplace");
+    CheckFactories.registerCheck<UseNullptrCheck>("modernize-use-nullptr");
+    CheckFactories.registerCheck<UseOverrideCheck>("modernize-use-override");
+    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..6bc5c00
--- /dev/null
@@ -0,0 +1,227 @@
+//===--- 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"))) {}
+
+void PassByValueCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "IncludeStyle",
+                utils::IncludeSorter::toString(IncludeStyle));
+}
+
+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(
+                  // Clang builds a CXXConstructExpr only whin 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.
+                                  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().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,
+            Result.Context->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..6443b44
--- /dev/null
@@ -0,0 +1,39 @@
+//===--- 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;
+};
+
+} // 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..1f51caa
--- /dev/null
@@ -0,0 +1,141 @@
+//===--- 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")) {}
+
+void RawStringLiteralCheck::storeOptions(ClangTidyOptions::OptionMap &Options) {
+  ClangTidyCheck::storeOptions(Options);
+}
+
+void RawStringLiteralCheck::registerMatchers(MatchFinder *Finder) {
+  Finder->addMatcher(
+      stringLiteral(unless(hasParent(predefinedExpr()))).bind("lit"), this);
+}
+
+void RawStringLiteralCheck::check(const MatchFinder::MatchResult &Result) {
+  // Raw string literals require C++11 or later.
+  if (!Result.Context->getLangOpts().CPlusPlus11)
+    return;
+
+  const auto *Literal = Result.Nodes.getNodeAs<StringLiteral>("lit");
+  if (Literal->getLocStart().isMacroID())
+    return;
+
+  if (containsEscapedCharacters(Result, Literal))
+    replaceWithRawStringLiteral(Result, Literal);
+}
+
+void RawStringLiteralCheck::replaceWithRawStringLiteral(
+    const MatchFinder::MatchResult &Result, const StringLiteral *Literal) {
+  CharSourceRange CharRange = Lexer::makeFileCharRange(
+      CharSourceRange::getTokenRange(Literal->getSourceRange()),
+      *Result.SourceManager, Result.Context->getLangOpts());
+  diag(Literal->getLocStart(),
+       "escaped string literal can be written as a raw string literal")
+      << FixItHint::CreateReplacement(
+          CharRange, asRawStringLiteral(Literal, DelimiterStem));
+}
+
+} // 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..1a1e135
--- /dev/null
@@ -0,0 +1,44 @@
+//===--- 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);
+
+  std::string DelimiterStem;
+};
+
+} // 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..d81f156
--- /dev/null
@@ -0,0 +1,250 @@
+//===- 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) {
+  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) {
+  if (!Result.Context->getLangOpts().CPlusPlus) {
+    return;
+  }
+
+  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,
+      Result.Context->getLangOpts());
+
+  std::string DeclText = Lexer::getSourceText(CharRange, *Result.SourceManager,
+                                              Result.Context->getLangOpts())
+                             .str();
+  Lexer PrototypeLexer(CharRange.getBegin(), Result.Context->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..df6dadc
--- /dev/null
@@ -0,0 +1,271 @@
+//===--- 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"));
+}
+
+/// \brief Matcher that finds auto_ptr declarations.
+static DeclarationMatcher AutoPtrDecl =
+    recordDecl(hasName("auto_ptr"), isFromStdNamespace());
+
+/// \brief Matches types declared as auto_ptr.
+static TypeMatcher AutoPtrType = qualType(hasDeclaration(AutoPtrDecl));
+
+/// \brief Matcher that finds expressions that are candidates to be wrapped with
+/// 'std::move'.
+///
+/// Binds the id \c AutoPtrOwnershipTransferId to the expression.
+static StatementMatcher MovableArgumentMatcher =
+    expr(allOf(isLValue(), hasType(AutoPtrType)))
+        .bind(AutoPtrOwnershipTransferId);
+
+/// \brief Creates a matcher that finds the locations of types referring to the
+/// \c std::auto_ptr() type.
+///
+/// \code
+///   std::auto_ptr<int> a;
+///        ^~~~~~~~~~~~~
+///
+///   typedef std::auto_ptr<int> int_ptr_t;
+///                ^~~~~~~~~~~~~
+///
+///   std::auto_ptr<int> fn(std::auto_ptr<int>);
+///        ^~~~~~~~~~~~~         ^~~~~~~~~~~~~
+///
+///   <etc...>
+/// \endcode
+TypeLocMatcher makeAutoPtrTypeLocMatcher() {
+  // Skip elaboratedType() as the named type will match soon thereafter.
+  return typeLoc(loc(qualType(AutoPtrType, unless(elaboratedType()))))
+      .bind(AutoPtrTokenId);
+}
+
+/// \brief Creates a matcher that finds the using declarations referring to
+/// \c std::auto_ptr.
+///
+/// \code
+///   using std::auto_ptr;
+///   ^~~~~~~~~~~~~~~~~~~
+/// \endcode
+DeclarationMatcher makeAutoPtrUsingDeclMatcher() {
+  return usingDecl(hasAnyUsingShadowDecl(hasTargetDecl(
+                       allOf(hasName("auto_ptr"), isFromStdNamespace()))))
+      .bind(AutoPtrTokenId);
+}
+
+/// \brief Creates a matcher that finds the \c std::auto_ptr copy-ctor and
+/// assign-operator expressions.
+///
+/// \c AutoPtrOwnershipTransferId is assigned to the argument of the expression,
+/// this is the part that has to be wrapped by \c std::move().
+///
+/// \code
+///   std::auto_ptr<int> i, j;
+///   i = j;
+///   ~~~~^
+/// \endcode
+StatementMatcher makeTransferOwnershipExprMatcher() {
+  return anyOf(
+      cxxOperatorCallExpr(allOf(hasOverloadedOperatorName("="),
+                                callee(cxxMethodDecl(ofClass(AutoPtrDecl))),
+                                hasArgument(1, MovableArgumentMatcher))),
+      cxxConstructExpr(allOf(hasType(AutoPtrType), argumentCountIs(1),
+                             hasArgument(0, MovableArgumentMatcher))));
+}
+
+/// \brief Locates the \c auto_ptr token when it is referred by a \c TypeLoc.
+///
+/// \code
+///   std::auto_ptr<int> i;
+///        ^~~~~~~~~~~~~
+/// \endcode
+///
+/// The caret represents the location returned and the tildes cover the
+/// parameter \p AutoPtrTypeLoc.
+///
+/// \return An invalid \c SourceLocation if not found, otherwise the location
+/// of the beginning of the \c auto_ptr token.
+static SourceLocation locateFromTypeLoc(const TypeLoc *AutoPtrTypeLoc,
+                                        const SourceManager &SM) {
+  auto TL = AutoPtrTypeLoc->getAs<TemplateSpecializationTypeLoc>();
+  if (TL.isNull())
+    return SourceLocation();
+
+  return TL.getTemplateNameLoc();
+}
+
+/// \brief Locates the \c auto_ptr token in using declarations.
+///
+/// \code
+///   using std::auto_ptr;
+///              ^
+/// \endcode
+///
+/// The caret represents the location returned.
+///
+/// \return An invalid \c SourceLocation if not found, otherwise the location
+/// of the beginning of the \c auto_ptr token.
+static SourceLocation locateFromUsingDecl(const UsingDecl *UsingAutoPtrDecl,
+                                          const SourceManager &SM) {
+  return UsingAutoPtrDecl->getNameInfo().getBeginLoc();
+}
+
+/// \brief Verifies that the token at \p TokenStart is 'auto_ptr'.
+static bool checkTokenIsAutoPtr(SourceLocation TokenStart,
+                                const SourceManager &SM,
+                                const LangOptions &LO) {
+  SmallVector<char, 8> Buffer;
+  bool Invalid = false;
+  StringRef Res = Lexer::getSpelling(TokenStart, Buffer, SM, LO, &Invalid);
+
+  return (!Invalid && Res == "auto_ptr");
+}
+
+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) {
+    Finder->addMatcher(makeAutoPtrTypeLocMatcher(), this);
+    Finder->addMatcher(makeAutoPtrUsingDeclMatcher(), this);
+    Finder->addMatcher(makeTransferOwnershipExprMatcher(), 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) {
+    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(), ")");
+
+    auto Insertion =
+        Inserter->CreateIncludeInsertion(SM.getMainFileID(), "utility",
+                                         /*IsAngled=*/true);
+    if (Insertion.hasValue())
+      Diag << Insertion.getValue();
+
+    return;
+  }
+
+  SourceLocation IdentifierLoc;
+  if (const auto *TL = Result.Nodes.getNodeAs<TypeLoc>(AutoPtrTokenId)) {
+    IdentifierLoc = locateFromTypeLoc(TL, SM);
+  } else if (const auto *D =
+                 Result.Nodes.getNodeAs<UsingDecl>(AutoPtrTokenId)) {
+    IdentifierLoc = locateFromUsingDecl(D, SM);
+  } else {
+    llvm_unreachable("Bad Callback. No node provided.");
+  }
+
+  if (IdentifierLoc.isMacroID())
+    IdentifierLoc = SM.getSpellingLoc(IdentifierLoc);
+
+  // Ensure that only the 'auto_ptr' token is replaced and not the template
+  // aliases.
+  if (!checkTokenIsAutoPtr(IdentifierLoc, SM, LangOptions()))
+    return;
+
+  SourceLocation EndLoc =
+      IdentifierLoc.getLocWithOffset(strlen("auto_ptr") - 1);
+  diag(IdentifierLoc, "auto_ptr is deprecated, use unique_ptr instead")
+      << FixItHint::CreateReplacement(SourceRange(IdentifierLoc, 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/ShrinkToFitCheck.cpp b/clang-tidy/modernize/ShrinkToFitCheck.cpp
new file mode 100644 (file)
index 0000000..7438332
--- /dev/null
@@ -0,0 +1,91 @@
+//===--- 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) {
+  // 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 LangOptions &Opts = Result.Context->getLangOpts();
+
+  if (!Opts.CPlusPlus11)
+    return;
+
+  const auto *MemberCall =
+      Result.Nodes.getNodeAs<CXXMemberCallExpr>("CopyAndSwapTrick");
+  const auto *Container = Result.Nodes.getNodeAs<Expr>("ContainerToShrink");
+  FixItHint Hint;
+
+  if (!MemberCall->getLocStart().isMacroID()) {
+    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/UseAutoCheck.cpp b/clang-tidy/modernize/UseAutoCheck.cpp
new file mode 100644 (file)
index 0000000..8c43e6b
--- /dev/null
@@ -0,0 +1,393 @@
+//===--- 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/ASTMatchers.h"
+#include "clang/ASTMatchers/ASTMatchFinder.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";
+
+/// \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"));
+}
+
+/// \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(
+             // 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(has(varDecl(anyOf(
+                 unless(hasWrittenNonListInitializer()), hasType(autoType()),
+                 unless(hasType(
+                     isSugarFor(anyOf(typedefIterator(), nestedIterator(),
+                                      iteratorFromUsingDeclaration())))))))))
+      .bind(IteratorDeclStmtId);
+}
+
+StatementMatcher makeDeclWithNewMatcher() {
+  return declStmt(
+             has(varDecl()),
+             unless(has(varDecl(anyOf(
+                 unless(hasInitializer(ignoringParenImpCasts(cxxNewExpr()))),
+                 // Skip declarations that are already using auto.
+                 anyOf(hasType(autoType()),
+                       hasType(pointerType(pointee(autoType())))),
+                 // 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);
+}
+
+} // 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(makeIteratorDeclMatcher(), this);
+    Finder->addMatcher(makeDeclWithNewMatcher(), 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::replaceNew(const DeclStmt *D, ASTContext *Context) {
+  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 *NewExpr = cast<CXXNewExpr>(V->getInit()->IgnoreParenImpCasts());
+    // Ensure that every VarDecl has a CXXNewExpr initializer.
+    if (!NewExpr)
+      return;
+
+    // If VarDecl and Initializer have mismatching unqualified types.
+    if (!Context->hasSameUnqualifiedType(V->getType(), NewExpr->getType()))
+      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();
+  }
+  SourceRange Range(Loc.getSourceRange());
+  auto Diag = diag(Range.getBegin(), "use auto when initializing with new"
+                                     " to avoid duplicating the type name");
+
+  // Space after 'auto' to handle cases where the '*' in the pointer type is
+  // next to the identifier. This avoids changing 'int *p' into 'autop'.
+  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)) {
+    replaceNew(Decl, Result.Context);
+  } 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..c63db06
--- /dev/null
@@ -0,0 +1,37 @@
+//===--- 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 replaceNew(const DeclStmt *D, ASTContext *Context);
+
+  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..ccb3e8f
--- /dev/null
@@ -0,0 +1,55 @@
+//===--- 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 {
+
+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);
+}
+
+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;
+
+  auto Diag =
+      diag(Expression->getExprLoc(),
+           "converting integer literal to bool, use bool literal instead");
+
+  if (!Expression->getLocStart().isMacroID())
+    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..46d7879
--- /dev/null
@@ -0,0 +1,35 @@
+//===--- 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)
+      : 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_BOOL_LITERALS_H
diff --git a/clang-tidy/modernize/UseDefaultCheck.cpp b/clang-tidy/modernize/UseDefaultCheck.cpp
new file mode 100644 (file)
index 0000000..c4e0498
--- /dev/null
@@ -0,0 +1,322 @@
+//===--- UseDefaultCheck.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 "UseDefaultCheck.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 the SourceLocation of the colon ':' before the initialization
+/// list in the definition of a constructor.
+static SourceLocation getColonLoc(const ASTContext *Context,
+                                  const CXXConstructorDecl *Ctor) {
+  // FIXME: First init is the first initialization that is going to be
+  // performed, no matter what was the real order in the source code. If the
+  // order of the inits is wrong in the code, it may result in a false negative.
+  SourceLocation FirstInit = (*Ctor->init_begin())->getSourceLocation();
+  SourceLocation LastArg =
+      Ctor->getParamDecl(Ctor->getNumParams() - 1)->getLocEnd();
+  // We need to find the colon between the ')' and the first initializer.
+  bool Invalid = false;
+  StringRef Text = Lexer::getSourceText(
+      CharSourceRange::getCharRange(LastArg, FirstInit),
+      Context->getSourceManager(), Context->getLangOpts(), &Invalid);
+  if (Invalid)
+    return SourceLocation();
+
+  size_t ColonPos = Text.rfind(':');
+  if (ColonPos == StringRef::npos)
+    return SourceLocation();
+
+  Text = Text.drop_front(ColonPos + 1);
+  if (std::strspn(Text.data(), " \t\r\n") != Text.size()) {
+    // If there are comments, preprocessor directives or anything, abort.
+    return SourceLocation();
+  }
+  // FIXME: don't remove comments in the middle of the initializers.
+  return LastArg.getLocWithOffset(ColonPos);
+}
+
+/// \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 UseDefaultCheck::registerMatchers(MatchFinder *Finder) {
+  if (getLangOpts().CPlusPlus) {
+    // Destructor.
+    Finder->addMatcher(cxxDestructorDecl(isDefinition()).bind(SpecialFunction),
+                       this);
+    Finder->addMatcher(
+        cxxConstructorDecl(
+            isDefinition(),
+            anyOf(
+                // Default constructor.
+                allOf(unless(hasAnyConstructorInitializer(anything())),
+                      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 UseDefaultCheck::check(const MatchFinder::MatchResult &Result) {
+  std::string SpecialFunctionName;
+  SourceLocation StartLoc, EndLoc;
+
+  // 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->isUserProvided() || !SpecialFunctionDecl->hasBody())
+    return;
+
+  const auto *Body = dyn_cast<CompoundStmt>(SpecialFunctionDecl->getBody());
+  if (!Body)
+    return;
+
+  // Default locations.
+  StartLoc = Body->getLBracLoc();
+  EndLoc = Body->getRBracLoc();
+
+  // If there are comments inside the body, don't do the change.
+  if (!SpecialFunctionDecl->isCopyAssignmentOperator() &&
+      !bodyEmpty(Result.Context, Body))
+    return;
+
+  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.
+    if (Ctor->getNumCtorInitializers() != 0) {
+      StartLoc = getColonLoc(Result.Context, Ctor);
+      if (!StartLoc.isValid())
+        return;
+    }
+  } else if (isa<CXXDestructorDecl>(SpecialFunctionDecl)) {
+    SpecialFunctionName = "destructor";
+  } else {
+    if (!isCopyAssignmentAndCanBeDefaulted(Result.Context, SpecialFunctionDecl))
+      return;
+    SpecialFunctionName = "copy-assignment operator";
+  }
+
+  diag(SpecialFunctionDecl->getLocStart(),
+       "use '= default' to define a trivial " + SpecialFunctionName)
+      << FixItHint::CreateReplacement(
+          CharSourceRange::getTokenRange(StartLoc, EndLoc), "= default;");
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/modernize/UseDefaultCheck.h b/clang-tidy/modernize/UseDefaultCheck.h
new file mode 100644 (file)
index 0000000..74a9b5e
--- /dev/null
@@ -0,0 +1,51 @@
+//===--- UseDefaultCheck.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_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_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-default.html
+class UseDefaultCheck : public ClangTidyCheck {
+public:
+  UseDefaultCheck(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_DEFAULT_H
+
diff --git a/clang-tidy/modernize/UseEmplaceCheck.cpp b/clang-tidy/modernize/UseEmplaceCheck.cpp
new file mode 100644 (file)
index 0000000..4084454
--- /dev/null
@@ -0,0 +1,131 @@
+//===--- 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 {
+
+static const auto DefaultContainersWithPushBack =
+    "::std::vector; ::std::list; ::std::deque";
+static const auto DefaultSmartPointers =
+    "::std::shared_ptr; ::std::unique_ptr; ::std::auto_ptr; ::std::weak_ptr";
+
+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))) {}
+
+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
+  // + match for make_pair calls.
+  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 = has(ignoringImplicit(initListExpr()));
+  // 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 ctorAsArgument = materializeTemporaryExpr(
+      anyOf(hasConstructExpr, has(cxxFunctionalCastExpr(hasConstructExpr))));
+
+  Finder->addMatcher(cxxMemberCallExpr(callPushBack, has(ctorAsArgument),
+                                       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");
+
+  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;
+
+  Diag << FixItHint::CreateReplacement(FunctionNameSourceRange,
+                                       "emplace_back(");
+
+  auto CallParensRange = InnerCtorCall->getParenOrBraceRange();
+
+  // Finish if there is no explicit constructor call.
+  if (CallParensRange.getBegin().isInvalid())
+    return;
+
+  // Range for constructor name and opening brace.
+  auto CtorCallSourceRange = CharSourceRange::getTokenRange(
+      InnerCtorCall->getExprLoc(), CallParensRange.getBegin());
+
+  Diag << FixItHint::CreateRemoval(CtorCallSourceRange)
+       << 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));
+}
+
+} // 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..72e1cfd
--- /dev/null
@@ -0,0 +1,44 @@
+//===--- 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;
+};
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_EMPLACE_H
diff --git a/clang-tidy/modernize/UseNullptrCheck.cpp b/clang-tidy/modernize/UseNullptrCheck.cpp
new file mode 100644 (file)
index 0000000..059c0c5
--- /dev/null
@@ -0,0 +1,488 @@
+//===--- 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 BuiltinType *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(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) {
+    CastExpr *C = dyn_cast<CastExpr>(S);
+    if (!C) {
+      FirstSubExpr = nullptr;
+      return true;
+    }
+    if (!FirstSubExpr)
+      FirstSubExpr = C->getSubExpr()->IgnoreParens();
+
+    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 skipSubTree();
+    }
+
+    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..81fa73f
--- /dev/null
@@ -0,0 +1,203 @@
+//===--- 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;
+  while (!RawLexer.LexFromRawLexer(Tok)) {
+    if (Tok.is(tok::semi) || Tok.is(tok::l_brace))
+      break;
+    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());
+    }
+    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 FunctionDecl *Method = Result.Nodes.getStmtAs<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,
+      Result.Context->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.
+      Token LastNonCommentToken;
+      for (Token T : Tokens) {
+        if (!T.is(tok::comment)) {
+          LastNonCommentToken = T;
+        }
+      }
+      InsertLoc = LastNonCommentToken.getEndLoc();
+      ReplacementText = " override";
+    }
+
+    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/UseUsingCheck.cpp b/clang-tidy/modernize/UseUsingCheck.cpp
new file mode 100644 (file)
index 0000000..498a95c
--- /dev/null
@@ -0,0 +1,93 @@
+//===--- 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 "../utils/LexerUtils.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+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, const SourceLocation &LocStart,
+                         const SourceLocation &LocEnd, ASTContext &Context,
+                         SourceRange &ResultRange) {
+  FileID FID = SM.getFileID(LocEnd);
+  llvm::MemoryBuffer *Buffer = SM.getBuffer(FID, LocEnd);
+  Lexer DeclLexer(SM.getLocForStartOfFile(FID), Context.getLangOpts(),
+                  Buffer->getBufferStart(), SM.getCharacterData(LocStart),
+                  Buffer->getBufferEnd());
+  Token DeclToken;
+  bool result = false;
+  int parenthesisLevel = 0;
+
+  while (!DeclLexer.LexFromRawLexer(DeclToken)) {
+    if (DeclToken.getKind() == tok::TokenKind::l_paren)
+      parenthesisLevel++;
+    if (DeclToken.getKind() == tok::TokenKind::r_paren)
+      parenthesisLevel--;
+    if (DeclToken.getKind() == tok::TokenKind::semi)
+      break;
+    // if there is comma and we are not between open parenthesis then it is
+    // two or more declatarions in this chain
+    if (parenthesisLevel == 0 && DeclToken.getKind() == tok::TokenKind::comma)
+      return false;
+
+    if (DeclToken.isOneOf(tok::TokenKind::identifier,
+                          tok::TokenKind::raw_identifier)) {
+      auto TokenStr = DeclToken.getRawIdentifier().str();
+
+      if (TokenStr == "typedef") {
+        ResultRange =
+            SourceRange(DeclToken.getLocation(), DeclToken.getEndLoc());
+        result = true;
+      }
+    }
+  }
+  // assert if there was keyword 'typedef' in declaration
+  assert(result && "No typedef found");
+
+  return result;
+}
+
+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;
+
+  auto Diag =
+      diag(MatchedDecl->getLocStart(), "use 'using' instead of 'typedef'");
+  if (MatchedDecl->getLocStart().isMacroID()) {
+    return;
+  }
+  SourceRange RemovalRange;
+  if (CheckRemoval(SM, MatchedDecl->getLocStart(), MatchedDecl->getLocEnd(),
+                   Context, RemovalRange)) {
+    Diag << FixItHint::CreateReplacement(
+        MatchedDecl->getSourceRange(),
+        "using " + MatchedDecl->getNameAsString() + " = " +
+            MatchedDecl->getUnderlyingType().getAsString(getLangOpts()));
+  }
+}
+
+} // 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..f1c70de
--- /dev/null
@@ -0,0 +1,35 @@
+//===--- 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 {
+public:
+  UseUsingCheck(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_USING_H
diff --git a/clang-tidy/performance/CMakeLists.txt b/clang-tidy/performance/CMakeLists.txt
new file mode 100644 (file)
index 0000000..32d8a24
--- /dev/null
@@ -0,0 +1,18 @@
+set(LLVM_LINK_COMPONENTS support)
+
+add_clang_library(clangTidyPerformanceModule
+  FasterStringFindCheck.cpp
+  ForRangeCopyCheck.cpp
+  ImplicitCastInLoopCheck.cpp
+  PerformanceTidyModule.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..ab2dff4
--- /dev/null
@@ -0,0 +1,111 @@
+//===--- 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 =
+      anyOf(hasName("find"), hasName("rfind"), hasName("find_first_of"),
+            hasName("find_first_not_of"), hasName("find_last_of"),
+            hasName("find_last_not_of"));
+
+  llvm::Optional<ast_matchers::internal::Matcher<NamedDecl>> IsStringClass;
+
+  for (const auto &ClassName : StringLikeClasses) {
+    const auto HasName = hasName(ClassName);
+    IsStringClass = IsStringClass ? anyOf(*IsStringClass, HasName) : HasName;
+  }
+
+  if (IsStringClass) {
+    Finder->addMatcher(
+        cxxMemberCallExpr(
+            callee(functionDecl(StringFindFunctions).bind("func")),
+            anyOf(argumentCountIs(1), argumentCountIs(2)),
+            hasArgument(0, SingleChar),
+            on(expr(hasType(recordDecl(*IsStringClass)),
+                    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..3fcfdba
--- /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..913ee96
--- /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/PerformanceTidyModule.cpp b/clang-tidy/performance/PerformanceTidyModule.cpp
new file mode 100644 (file)
index 0000000..984a9fb
--- /dev/null
@@ -0,0 +1,51 @@
+//===--- 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 "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<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/UnnecessaryCopyInitialization.cpp b/clang-tidy/performance/UnnecessaryCopyInitialization.cpp
new file mode 100644 (file)
index 0000000..e486350
--- /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()),
+                                   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..71dc455
--- /dev/null
@@ -0,0 +1,172 @@
+//===--- 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;
+}
+
+} // 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(matchers::isExpensiveToCopy(),
+                                                 unless(referenceType())))),
+                  decl().bind("param"));
+  Finder->addMatcher(
+      functionDecl(isDefinition(), unless(cxxMethodDecl(isOverride())),
+                   unless(isInstantiated()),
+                   has(typeLoc(forEach(ExpensiveValueParamDecl))),
+                   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();
+
+  // Skip declarations delayed by late template parsing without a body.
+  if (!Function->getBody())
+    return;
+
+  // Do not trigger on non-const value parameters when:
+  // 1. they are in a constructor definition since they can likely trigger
+  //    misc-move-constructor-init which will suggest to move the argument.
+  if (!IsConstQualified && (llvm::isa<CXXConstructorDecl>(Function) ||
+                            !Function->doesThisDeclarationHaveABody()))
+    return;
+
+  auto AllDeclRefExprs = utils::decl_ref_expr::allDeclRefExprs(
+      *Param, *Function->getBody(), *Result.Context);
+  auto ConstDeclRefExprs = utils::decl_ref_expr::constReferenceDeclRefExprs(
+      *Param, *Function->getBody(), *Result.Context);
+  // 2. 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) {
+    auto CanonicalType = Param->getType().getCanonicalType();
+    if (AllDeclRefExprs.size() == 1 &&
+        ((utils::type_traits::hasNonTrivialMoveConstructor(CanonicalType) &&
+          utils::decl_ref_expr::isCopyConstructorArgument(
+              **AllDeclRefExprs.begin(), *Function->getBody(),
+              *Result.Context)) ||
+         (utils::type_traits::hasNonTrivialMoveAssignment(CanonicalType) &&
+          utils::decl_ref_expr::isCopyAssignmentArgument(
+              **AllDeclRefExprs.begin(), *Function->getBody(),
+              *Result.Context)))) {
+      handleMoveFix(*Param, **AllDeclRefExprs.begin(), *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 in macros since we cannot place them correctly, or if
+  // function is virtual as it might break overrides.
+  const auto *Method = llvm::dyn_cast<CXXMethodDecl>(Function);
+  if (Param->getLocStart().isMacroID() || (Method && Method->isVirtual()))
+    return;
+  for (const auto *FunctionDecl = Function; FunctionDecl != nullptr;
+       FunctionDecl = FunctionDecl->getPreviousDecl()) {
+    const auto &CurrentParam = *FunctionDecl->getParamDecl(Index);
+    Diag << utils::fixit::changeVarDeclToReference(CurrentParam,
+                                                           *Result.Context);
+    if (!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..212c7aa
--- /dev/null
@@ -0,0 +1,21 @@
+add_clang_library(clangTidyPlugin
+  ClangTidyPlugin.cpp
+
+  LINK_LIBS
+  clangAST
+  clangASTMatchers
+  clangBasic
+  clangFrontend
+  clangSema
+  clangTidy
+  clangTidyBoostModule
+  clangTidyCERTModule
+  clangTidyCppCoreGuidelinesModule
+  clangTidyGoogleModule
+  clangTidyLLVMModule
+  clangTidyMiscModule
+  clangTidyModernizeModule
+  clangTidyPerformanceModule
+  clangTidyReadabilityModule
+  clangTooling
+  )
diff --git a/clang-tidy/plugin/ClangTidyPlugin.cpp b/clang-tidy/plugin/ClangTidyPlugin.cpp
new file mode 100644 (file)
index 0000000..19509a5
--- /dev/null
@@ -0,0 +1,122 @@
+//===- 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 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..11a5883
--- /dev/null
@@ -0,0 +1,117 @@
+//===--- 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 "llvm/ADT/Optional.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 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.
+                   unless(cxxMethodDecl(ofClass(cxxRecordDecl(isLambda())))),
+                   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, Result.Context->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..d65f60b
--- /dev/null
@@ -0,0 +1,277 @@
+//===--- 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(*FullSourceLoc(Loc, SM).getCharacterData()))
+      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 = LastTokenLoc;
+  // 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(*FullSourceLoc(Loc, SM).getCharacterData()))
+      Loc = Loc.getLocWithOffset(1);
+
+    if (isVerticalWhitespace(*FullSourceLoc(Loc, SM).getCharacterData())) {
+      // 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(), SourceLocation());
+    }
+  } 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..12d7b37
--- /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..6254410
--- /dev/null
@@ -0,0 +1,32 @@
+set(LLVM_LINK_COMPONENTS support)
+
+add_clang_library(clangTidyReadabilityModule
+  AvoidConstParamsInDecls.cpp
+  BracesAroundStatementsCheck.cpp
+  ContainerSizeEmptyCheck.cpp
+  DeletedDefaultCheck.cpp
+  ElseAfterReturnCheck.cpp
+  FunctionSizeCheck.cpp
+  IdentifierNamingCheck.cpp
+  ImplicitBoolCastCheck.cpp
+  InconsistentDeclarationParameterNameCheck.cpp
+  NamedParameterCheck.cpp
+  NamespaceCommentCheck.cpp
+  ReadabilityTidyModule.cpp
+  RedundantControlFlowCheck.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..b0c6fb5
--- /dev/null
@@ -0,0 +1,152 @@
+//===--- 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 "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Lex/Lexer.h"
+#include "llvm/ADT/StringRef.h"
+#include "../utils/Matchers.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+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 stlContainer = hasAnyName(
+      "array", "basic_string", "deque", "forward_list", "list", "map",
+      "multimap", "multiset", "priority_queue", "queue", "set", "stack",
+      "unordered_map", "unordered_multimap", "unordered_multiset",
+      "unordered_set", "vector");
+
+  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(namedDecl(stlContainer)),
+                        hasType(pointsTo(namedDecl(stlContainer))),
+                        hasType(references(namedDecl(stlContainer)))))
+                 .bind("STLObject")),
+          callee(cxxMethodDecl(hasName("size"))), WrongUse)
+          .bind("SizeCallExpr"),
+      this);
+}
+
+void ContainerSizeEmptyCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *MemberCall =
+      Result.Nodes.getNodeAs<CXXMemberCallExpr>("SizeCallExpr");
+  const auto *BinaryOp = Result.Nodes.getNodeAs<BinaryOperator>("SizeBinaryOp");
+  const auto *E = Result.Nodes.getNodeAs<Expr>("STLObject");
+  FixItHint Hint;
+  std::string ReplacementText = Lexer::getSourceText(
+      CharSourceRange::getTokenRange(E->getSourceRange()),
+      *Result.SourceManager, Result.Context->getLangOpts());
+  if (E->getType()->isPointerType())
+    ReplacementText += "->empty()";
+  else
+    ReplacementText += ".empty()";
+
+  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);
+  }
+  diag(MemberCall->getLocStart(), "the 'empty' method should be used to check "
+                                  "for emptiness instead of 'size'")
+      << Hint;
+}
+
+} // 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/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..9560f12
--- /dev/null
@@ -0,0 +1,47 @@
+//===--- 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) {
+  // FIXME: Support continue, break and throw.
+  Finder->addMatcher(
+      compoundStmt(
+          forEach(ifStmt(hasThen(stmt(anyOf(returnStmt(),
+                                            compoundStmt(has(returnStmt()))))),
+                         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();
+  DiagnosticBuilder Diag = diag(ElseLoc, "don't use else after return");
+  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..d02972d
--- /dev/null
@@ -0,0 +1,134 @@
+//===--- 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;
+    // fallthrough
+    case Stmt::CompoundStmtClass:
+      TrackedParent.push_back(true);
+      break;
+    default:
+      TrackedParent.push_back(false);
+      break;
+    }
+
+    Base::TraverseStmt(Node);
+
+    TrackedParent.pop_back();
+    return true;
+  }
+
+  bool TraverseDecl(Decl *Node) {
+    TrackedParent.push_back(false);
+    Base::TraverseDecl(Node);
+    TrackedParent.pop_back();
+    return true;
+  }
+
+  struct FunctionInfo {
+    FunctionInfo() : Lines(0), Statements(0), Branches(0) {}
+    unsigned Lines;
+    unsigned Statements;
+    unsigned Branches;
+  };
+  FunctionInfo Info;
+  std::vector<bool> TrackedParent;
+};
+
+FunctionSizeCheck::FunctionSizeCheck(StringRef Name, ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      LineThreshold(Options.get("LineThreshold", -1U)),
+      StatementThreshold(Options.get("StatementThreshold", 800U)),
+      BranchThreshold(Options.get("BranchThreshold", -1U)) {}
+
+void FunctionSizeCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "LineThreshold", LineThreshold);
+  Options.store(Opts, "StatementThreshold", StatementThreshold);
+  Options.store(Opts, "BranchThreshold", BranchThreshold);
+}
+
+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.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());
+    }
+  }
+
+  if (FI.Lines > LineThreshold || FI.Statements > StatementThreshold ||
+      FI.Branches > BranchThreshold) {
+    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;
+  }
+}
+
+} // 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..ed40330
--- /dev/null
@@ -0,0 +1,48 @@
+//===--- 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).
+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;
+};
+
+} // 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..b8df167
--- /dev/null
@@ -0,0 +1,844 @@
+//===--- 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 "llvm/Support/Debug.h"
+#include "llvm/Support/Format.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"
+
+#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(NamingCheckId LHS, 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<CaseType>(Str)
+        .Case("lower_case", CT_LowerCase)
+        .Case("UPPER_CASE", CT_UpperCase)
+        .Case("camelBack", CT_CamelBack)
+        .Case("CamelCase", CT_CamelCase)
+        .Default(CT_AnyCase);
+  };
+
+  for (auto const &Name : StyleNames) {
+    NamingStyles.push_back(
+        NamingStyle(fromString(Options.get((Name + "Case").str(), "")),
+                    Options.get((Name + "Prefix").str(), ""),
+                    Options.get((Name + "Suffix").str(), "")));
+  }
+
+  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";
+    }
+
+    llvm_unreachable("Unknown Case Type");
+  };
+
+  for (size_t i = 0; i < SK_Count; ++i) {
+    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]*$"),
+  };
+
+  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;
+
+  if (!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;
+  }
+
+  return Fixup;
+}
+
+static std::string fixupWithStyle(StringRef Name,
+                                  IdentifierNamingCheck::NamingStyle Style) {
+  return Style.Prefix + fixupWithCase(Name, Style.Case) + Style.Suffix;
+}
+
+static StyleKind findStyleKind(
+    const NamedDecl *D,
+    const std::vector<IdentifierNamingCheck::NamingStyle> &NamingStyles) {
+  if (isa<TypedefDecl>(D) && NamingStyles[SK_Typedef].isSet())
+    return SK_Typedef;
+
+  if (isa<TypeAliasDecl>(D) && NamingStyles[SK_TypeAlias].isSet())
+    return SK_TypeAlias;
+
+  if (const auto *Decl = dyn_cast<NamespaceDecl>(D)) {
+    if (Decl->isAnonymousNamespace())
+      return SK_Invalid;
+
+    if (Decl->isInline() && NamingStyles[SK_InlineNamespace].isSet())
+      return SK_InlineNamespace;
+
+    if (NamingStyles[SK_Namespace].isSet())
+      return SK_Namespace;
+  }
+
+  if (isa<EnumDecl>(D) && NamingStyles[SK_Enum].isSet())
+    return SK_Enum;
+
+  if (isa<EnumConstantDecl>(D)) {
+    if (NamingStyles[SK_EnumConstant].isSet())
+      return SK_EnumConstant;
+
+    if (NamingStyles[SK_Constant].isSet())
+      return SK_Constant;
+
+    return SK_Invalid;
+  }
+
+  if (const auto *Decl = dyn_cast<CXXRecordDecl>(D)) {
+    if (Decl->isAnonymousStructOrUnion())
+      return SK_Invalid;
+
+    if (Decl->hasDefinition() && Decl->isAbstract() &&
+        NamingStyles[SK_AbstractClass].isSet())
+      return SK_AbstractClass;
+
+    if (Decl->isStruct() && NamingStyles[SK_Struct].isSet())
+      return SK_Struct;
+
+    if (Decl->isStruct() && NamingStyles[SK_Class].isSet())
+      return SK_Class;
+
+    if (Decl->isClass() && NamingStyles[SK_Class].isSet())
+      return SK_Class;
+
+    if (Decl->isClass() && NamingStyles[SK_Struct].isSet())
+      return SK_Struct;
+
+    if (Decl->isUnion() && NamingStyles[SK_Union].isSet())
+      return SK_Union;
+
+    if (Decl->isEnum() && NamingStyles[SK_Enum].isSet())
+      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].isSet())
+      return SK_ConstantMember;
+
+    if (!Type.isNull() && Type.isLocalConstQualified() &&
+        NamingStyles[SK_Constant].isSet())
+      return SK_Constant;
+
+    if (Decl->getAccess() == AS_private &&
+        NamingStyles[SK_PrivateMember].isSet())
+      return SK_PrivateMember;
+
+    if (Decl->getAccess() == AS_protected &&
+        NamingStyles[SK_ProtectedMember].isSet())
+      return SK_ProtectedMember;
+
+    if (Decl->getAccess() == AS_public && NamingStyles[SK_PublicMember].isSet())
+      return SK_PublicMember;
+
+    if (NamingStyles[SK_Member].isSet())
+      return SK_Member;
+
+    return SK_Invalid;
+  }
+
+  if (const auto *Decl = dyn_cast<ParmVarDecl>(D)) {
+    QualType Type = Decl->getType();
+
+    if (Decl->isConstexpr() && NamingStyles[SK_ConstexprVariable].isSet())
+      return SK_ConstexprVariable;
+
+    if (!Type.isNull() && Type.isLocalConstQualified() &&
+        NamingStyles[SK_ConstantParameter].isSet())
+      return SK_ConstantParameter;
+
+    if (!Type.isNull() && Type.isLocalConstQualified() &&
+        NamingStyles[SK_Constant].isSet())
+      return SK_Constant;
+
+    if (Decl->isParameterPack() && NamingStyles[SK_ParameterPack].isSet())
+      return SK_ParameterPack;
+
+    if (NamingStyles[SK_Parameter].isSet())
+      return SK_Parameter;
+
+    return SK_Invalid;
+  }
+
+  if (const auto *Decl = dyn_cast<VarDecl>(D)) {
+    QualType Type = Decl->getType();
+
+    if (Decl->isConstexpr() && NamingStyles[SK_ConstexprVariable].isSet())
+      return SK_ConstexprVariable;
+
+    if (!Type.isNull() && Type.isLocalConstQualified() &&
+        Decl->isStaticDataMember() && NamingStyles[SK_ClassConstant].isSet())
+      return SK_ClassConstant;
+
+    if (!Type.isNull() && Type.isLocalConstQualified() &&
+        Decl->isFileVarDecl() && NamingStyles[SK_GlobalConstant].isSet())
+      return SK_GlobalConstant;
+
+    if (!Type.isNull() && Type.isLocalConstQualified() &&
+        Decl->isStaticLocal() && NamingStyles[SK_StaticConstant].isSet())
+      return SK_StaticConstant;
+
+    if (!Type.isNull() && Type.isLocalConstQualified() &&
+        Decl->isLocalVarDecl() && NamingStyles[SK_LocalConstant].isSet())
+      return SK_LocalConstant;
+
+    if (!Type.isNull() && Type.isLocalConstQualified() &&
+        Decl->isFunctionOrMethodVarDecl() &&
+        NamingStyles[SK_LocalConstant].isSet())
+      return SK_LocalConstant;
+
+    if (!Type.isNull() && Type.isLocalConstQualified() &&
+        NamingStyles[SK_Constant].isSet())
+      return SK_Constant;
+
+    if (Decl->isStaticDataMember() && NamingStyles[SK_ClassMember].isSet())
+      return SK_ClassMember;
+
+    if (Decl->isFileVarDecl() && NamingStyles[SK_GlobalVariable].isSet())
+      return SK_GlobalVariable;
+
+    if (Decl->isStaticLocal() && NamingStyles[SK_StaticVariable].isSet())
+      return SK_StaticVariable;
+
+    if (Decl->isLocalVarDecl() && NamingStyles[SK_LocalVariable].isSet())
+      return SK_LocalVariable;
+
+    if (Decl->isFunctionOrMethodVarDecl() &&
+        NamingStyles[SK_LocalVariable].isSet())
+      return SK_LocalVariable;
+
+    if (NamingStyles[SK_Variable].isSet())
+      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].isSet())
+      return SK_ConstexprMethod;
+
+    if (Decl->isConstexpr() && NamingStyles[SK_ConstexprFunction].isSet())
+      return SK_ConstexprFunction;
+
+    if (Decl->isStatic() && NamingStyles[SK_ClassMethod].isSet())
+      return SK_ClassMethod;
+
+    if (Decl->isVirtual() && NamingStyles[SK_VirtualMethod].isSet())
+      return SK_VirtualMethod;
+
+    if (Decl->getAccess() == AS_private &&
+        NamingStyles[SK_PrivateMethod].isSet())
+      return SK_PrivateMethod;
+
+    if (Decl->getAccess() == AS_protected &&
+        NamingStyles[SK_ProtectedMethod].isSet())
+      return SK_ProtectedMethod;
+
+    if (Decl->getAccess() == AS_public && NamingStyles[SK_PublicMethod].isSet())
+      return SK_PublicMethod;
+
+    if (NamingStyles[SK_Method].isSet())
+      return SK_Method;
+
+    if (NamingStyles[SK_Function].isSet())
+      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].isSet())
+      return SK_ConstexprFunction;
+
+    if (Decl->isGlobal() && NamingStyles[SK_GlobalFunction].isSet())
+      return SK_GlobalFunction;
+
+    if (NamingStyles[SK_Function].isSet())
+      return SK_Function;
+  }
+
+  if (isa<TemplateTypeParmDecl>(D)) {
+    if (NamingStyles[SK_TypeTemplateParameter].isSet())
+      return SK_TypeTemplateParameter;
+
+    if (NamingStyles[SK_TemplateParameter].isSet())
+      return SK_TemplateParameter;
+
+    return SK_Invalid;
+  }
+
+  if (isa<NonTypeTemplateParmDecl>(D)) {
+    if (NamingStyles[SK_ValueTemplateParameter].isSet())
+      return SK_ValueTemplateParameter;
+
+    if (NamingStyles[SK_TemplateParameter].isSet())
+      return SK_TemplateParameter;
+
+    return SK_Invalid;
+  }
+
+  if (isa<TemplateTemplateParmDecl>(D)) {
+    if (NamingStyles[SK_TemplateTemplateParameter].isSet())
+      return SK_TemplateTemplateParameter;
+
+    if (NamingStyles[SK_TemplateParameter].isSet())
+      return SK_TemplateParameter;
+
+    return SK_Invalid;
+  }
+
+  return SK_Invalid;
+}
+
+static void addUsage(IdentifierNamingCheck::NamingCheckFailureMap &Failures,
+                     const IdentifierNamingCheck::NamingCheckId &Decl,
+                     SourceRange Range) {
+  // Do nothing if the provided range is invalid.
+  if (Range.getBegin().isInvalid() || Range.getEnd().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(Range.getBegin().getRawEncoding()).second)
+    return;
+
+  Failure.ShouldFix = Failure.ShouldFix && !Range.getBegin().isMacroID() &&
+                      !Range.getEnd().isMacroID();
+}
+
+/// Convenience method when the usage to be added is a NamedDecl
+static void addUsage(IdentifierNamingCheck::NamingCheckFailureMap &Failures,
+                     const NamedDecl *Decl, SourceRange Range) {
+  return addUsage(Failures, IdentifierNamingCheck::NamingCheckId(
+                                Decl->getLocation(), Decl->getNameAsString()),
+                  Range);
+}
+
+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());
+    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);
+    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;
+
+    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));
+      }
+    } 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) {
+  StringRef Name = MacroNameTok.getIdentifierInfo()->getName();
+  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));
+    }
+  } 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..953803c
--- /dev/null
@@ -0,0 +1,111 @@
+//===--- 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,
+  };
+
+  struct NamingStyle {
+    NamingStyle() : Case(CT_AnyCase) {}
+
+    NamingStyle(CaseType Case, const std::string &Prefix,
+                const std::string &Suffix)
+        : Case(Case), Prefix(Prefix), Suffix(Suffix) {}
+
+    CaseType Case;
+    std::string Prefix;
+    std::string Suffix;
+
+    bool isSet() const {
+      return !(Case == CT_AnyCase && Prefix.empty() && Suffix.empty());
+    }
+  };
+
+  /// \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<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..6f80ccf
--- /dev/null
@@ -0,0 +1,417 @@
+//===--- 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"
+
+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) &&
+         clang::Lexer::getImmediateMacroName(Loc, SM, LO) == "NULL";
+}
+
+AST_MATCHER(Stmt, isNULLMacroExpansion) {
+  return isNULLMacroExpansion(&Node, Finder->getASTContext());
+}
+
+ast_matchers::internal::Matcher<Expr> createExceptionCasesMatcher() {
+  return expr(anyOf(hasParent(explicitCastExpr()),
+                    allOf(isMacroExpansion(), unless(isNULLMacroExpansion())),
+                    isInTemplateInstantiation(),
+                    hasAncestor(functionTemplateDecl())));
+}
+
+StatementMatcher createImplicitCastFromBoolMatcher() {
+  return implicitCastExpr(
+      unless(createExceptionCasesMatcher()),
+      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(qualType(booleanType())))));
+}
+
+StringRef
+getZeroLiteralToCompareWithForGivenType(CastKind CastExpressionKind,
+                                        QualType CastSubExpressionType,
+                                        ASTContext &Context) {
+  switch (CastExpressionKind) {
+  case CK_IntegralToBoolean:
+    return CastSubExpressionType->isUnsignedIntegerType() ? "0u" : "0";
+
+  case CK_FloatingToBoolean:
+    return Context.hasSameType(CastSubExpressionType, 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 *UnaryOperatorExpression =
+      llvm::dyn_cast<UnaryOperator>(Statement);
+  return UnaryOperatorExpression != nullptr &&
+         UnaryOperatorExpression->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 CXXOperatorCallExpr *OverloadedOperatorCall =
+          llvm::dyn_cast<CXXOperatorCallExpr>(Statement)) {
+    return areParensNeededForOverloadedOperator(
+        OverloadedOperatorCall->getOperator());
+  }
+
+  return llvm::isa<BinaryOperator>(Statement) ||
+         llvm::isa<UnaryOperator>(Statement);
+}
+
+void addFixItHintsForGenericExpressionCastToBool(
+    DiagnosticBuilder &Diagnostic, const ImplicitCastExpr *CastExpression,
+    const Stmt *ParentStatement, ASTContext &Context) {
+  // In case of expressions like (! integer), we should remove the redundant not
+  // operator and use inverted comparison (integer == 0).
+  bool InvertComparison =
+      ParentStatement != nullptr && isUnaryLogicalNotOperator(ParentStatement);
+  if (InvertComparison) {
+    SourceLocation ParentStartLoc = ParentStatement->getLocStart();
+    SourceLocation ParentEndLoc =
+        llvm::cast<UnaryOperator>(ParentStatement)->getSubExpr()->getLocStart();
+    Diagnostic.AddFixItHint(FixItHint::CreateRemoval(
+        CharSourceRange::getCharRange(ParentStartLoc, ParentEndLoc)));
+
+    auto FurtherParents = Context.getParents(*ParentStatement);
+    ParentStatement = FurtherParents[0].get<Stmt>();
+  }
+
+  const Expr *SubExpression = CastExpression->getSubExpr();
+
+  bool NeedInnerParens = areParensNeededForStatement(SubExpression);
+  bool NeedOuterParens = ParentStatement != nullptr &&
+                         areParensNeededForStatement(ParentStatement);
+
+  std::string StartLocInsertion;
+
+  if (NeedOuterParens) {
+    StartLocInsertion += "(";
+  }
+  if (NeedInnerParens) {
+    StartLocInsertion += "(";
+  }
+
+  if (!StartLocInsertion.empty()) {
+    SourceLocation StartLoc = CastExpression->getLocStart();
+    Diagnostic.AddFixItHint(
+        FixItHint::CreateInsertion(StartLoc, StartLocInsertion));
+  }
+
+  std::string EndLocInsertion;
+
+  if (NeedInnerParens) {
+    EndLocInsertion += ")";
+  }
+
+  if (InvertComparison) {
+    EndLocInsertion += " == ";
+  } else {
+    EndLocInsertion += " != ";
+  }
+
+  EndLocInsertion += getZeroLiteralToCompareWithForGivenType(
+      CastExpression->getCastKind(), SubExpression->getType(), Context);
+
+  if (NeedOuterParens) {
+    EndLocInsertion += ")";
+  }
+
+  SourceLocation EndLoc = Lexer::getLocForEndOfToken(
+      CastExpression->getLocEnd(), 0, Context.getSourceManager(),
+      Context.getLangOpts());
+  Diagnostic.AddFixItHint(FixItHint::CreateInsertion(EndLoc, EndLocInsertion));
+}
+
+StringRef getEquivalentBoolLiteralForExpression(const Expr *Expression,
+                                                ASTContext &Context) {
+  if (isNULLMacroExpansion(Expression, Context)) {
+    return "false";
+  }
+
+  if (const auto *IntLit = llvm::dyn_cast<IntegerLiteral>(Expression)) {
+    return (IntLit->getValue() == 0) ? "false" : "true";
+  }
+
+  if (const auto *FloatLit = llvm::dyn_cast<FloatingLiteral>(Expression)) {
+    llvm::APFloat FloatLitAbsValue = FloatLit->getValue();
+    FloatLitAbsValue.clearSign();
+    return (FloatLitAbsValue.bitcastToAPInt() == 0) ? "false" : "true";
+  }
+
+  if (const auto *CharLit = llvm::dyn_cast<CharacterLiteral>(Expression)) {
+    return (CharLit->getValue() == 0) ? "false" : "true";
+  }
+
+  if (llvm::isa<StringLiteral>(Expression->IgnoreCasts())) {
+    return "true";
+  }
+
+  return StringRef();
+}
+
+void addFixItHintsForLiteralCastToBool(DiagnosticBuilder &Diagnostic,
+                                       const ImplicitCastExpr *CastExpression,
+                                       StringRef EquivalentLiteralExpression) {
+  SourceLocation StartLoc = CastExpression->getLocStart();
+  SourceLocation EndLoc = CastExpression->getLocEnd();
+
+  Diagnostic.AddFixItHint(FixItHint::CreateReplacement(
+      CharSourceRange::getTokenRange(StartLoc, EndLoc),
+      EquivalentLiteralExpression));
+}
+
+void addFixItHintsForGenericExpressionCastFromBool(
+    DiagnosticBuilder &Diagnostic, const ImplicitCastExpr *CastExpression,
+    ASTContext &Context, StringRef OtherType) {
+  const Expr *SubExpression = CastExpression->getSubExpr();
+  bool NeedParens = !llvm::isa<ParenExpr>(SubExpression);
+
+  std::string StartLocInsertion = "static_cast<";
+  StartLocInsertion += OtherType.str();
+  StartLocInsertion += ">";
+  if (NeedParens) {
+    StartLocInsertion += "(";
+  }
+
+  SourceLocation StartLoc = CastExpression->getLocStart();
+  Diagnostic.AddFixItHint(
+      FixItHint::CreateInsertion(StartLoc, StartLocInsertion));
+
+  if (NeedParens) {
+    SourceLocation EndLoc = Lexer::getLocForEndOfToken(
+        CastExpression->getLocEnd(), 0, Context.getSourceManager(),
+        Context.getLangOpts());
+
+    Diagnostic.AddFixItHint(FixItHint::CreateInsertion(EndLoc, ")"));
+  }
+}
+
+StringRef getEquivalentLiteralForBoolLiteral(
+    const CXXBoolLiteralExpr *BoolLiteralExpression, QualType DestinationType,
+    ASTContext &Context) {
+  // Prior to C++11, false literal could be implicitly converted to pointer.
+  if (!Context.getLangOpts().CPlusPlus11 &&
+      (DestinationType->isPointerType() ||
+       DestinationType->isMemberPointerType()) &&
+      BoolLiteralExpression->getValue() == false) {
+    return "0";
+  }
+
+  if (DestinationType->isFloatingType()) {
+    if (BoolLiteralExpression->getValue() == true) {
+      return Context.hasSameType(DestinationType, Context.FloatTy) ? "1.0f"
+                                                                   : "1.0";
+    }
+    return Context.hasSameType(DestinationType, Context.FloatTy) ? "0.0f"
+                                                                 : "0.0";
+  }
+
+  if (BoolLiteralExpression->getValue() == true) {
+    return DestinationType->isUnsignedIntegerType() ? "1u" : "1";
+  }
+  return DestinationType->isUnsignedIntegerType() ? "0u" : "0";
+}
+
+void addFixItHintsForLiteralCastFromBool(DiagnosticBuilder &Diagnostic,
+                                         const ImplicitCastExpr *CastExpression,
+                                         ASTContext &Context,
+                                         QualType DestinationType) {
+  SourceLocation StartLoc = CastExpression->getLocStart();
+  SourceLocation EndLoc = CastExpression->getLocEnd();
+  const auto *BoolLiteralExpression =
+      llvm::dyn_cast<CXXBoolLiteralExpr>(CastExpression->getSubExpr());
+
+  Diagnostic.AddFixItHint(FixItHint::CreateReplacement(
+      CharSourceRange::getTokenRange(StartLoc, EndLoc),
+      getEquivalentLiteralForBoolLiteral(BoolLiteralExpression, DestinationType,
+                                         Context)));
+}
+
+StatementMatcher createConditionalExpressionMatcher() {
+  return stmt(anyOf(ifStmt(), conditionalOperator(),
+                    parenExpr(hasParent(conditionalOperator()))));
+}
+
+bool isAllowedConditionalCast(const ImplicitCastExpr *CastExpression,
+                              ASTContext &Context) {
+  auto AllowedConditionalMatcher = stmt(hasParent(stmt(
+      anyOf(createConditionalExpressionMatcher(),
+            unaryOperator(hasOperatorName("!"),
+                          hasParent(createConditionalExpressionMatcher()))))));
+
+  auto MatchResult = match(AllowedConditionalMatcher, *CastExpression, Context);
+  return !MatchResult.empty();
+}
+
+} // anonymous namespace
+
+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;
+  }
+
+  Finder->addMatcher(
+      implicitCastExpr(
+          // Exclude cases common to implicit cast to and from bool.
+          unless(createExceptionCasesMatcher()),
+          // 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())))),
+          anyOf(hasCastKind(CK_IntegralToBoolean),
+                hasCastKind(CK_FloatingToBoolean),
+                hasCastKind(CK_PointerToBoolean),
+                hasCastKind(CK_MemberPointerToBoolean)),
+          // Retrive also parent statement, to check if we need additional
+          // parens in replacement.
+          anyOf(hasParent(stmt().bind("parentStmt")), anything()))
+          .bind("implicitCastToBool"),
+      this);
+
+  Finder->addMatcher(
+      implicitCastExpr(
+          createImplicitCastFromBoolMatcher(),
+          // 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(hasOperatorName("=="), hasOperatorName("!=")),
+              hasLHS(createImplicitCastFromBoolMatcher()),
+              hasRHS(createImplicitCastFromBoolMatcher())))),
+          // Check also for nested casts, for example: bool -> int -> float.
+          anyOf(hasParent(implicitCastExpr().bind("furtherImplicitCast")),
+                anything()))
+          .bind("implicitCastFromBool"),
+      this);
+}
+
+void ImplicitBoolCastCheck::check(const MatchFinder::MatchResult &Result) {
+  if (const auto *CastToBool =
+          Result.Nodes.getNodeAs<ImplicitCastExpr>("implicitCastToBool")) {
+    const auto *ParentStatement = Result.Nodes.getNodeAs<Stmt>("parentStmt");
+    return handleCastToBool(CastToBool, ParentStatement, *Result.Context);
+  }
+
+  if (const auto *CastFromBool =
+          Result.Nodes.getNodeAs<ImplicitCastExpr>("implicitCastFromBool")) {
+    const auto *FurtherImplicitCastExpression =
+        Result.Nodes.getNodeAs<ImplicitCastExpr>("furtherImplicitCast");
+    return handleCastFromBool(CastFromBool, FurtherImplicitCastExpression,
+                              *Result.Context);
+  }
+}
+
+void ImplicitBoolCastCheck::handleCastToBool(
+    const ImplicitCastExpr *CastExpression, const Stmt *ParentStatement,
+    ASTContext &Context) {
+  if (AllowConditionalPointerCasts &&
+      (CastExpression->getCastKind() == CK_PointerToBoolean ||
+       CastExpression->getCastKind() == CK_MemberPointerToBoolean) &&
+      isAllowedConditionalCast(CastExpression, Context)) {
+    return;
+  }
+
+  if (AllowConditionalIntegerCasts &&
+      CastExpression->getCastKind() == CK_IntegralToBoolean &&
+      isAllowedConditionalCast(CastExpression, Context)) {
+    return;
+  }
+
+  std::string OtherType = CastExpression->getSubExpr()->getType().getAsString();
+  DiagnosticBuilder Diagnostic =
+      diag(CastExpression->getLocStart(), "implicit cast '%0' -> bool")
+      << OtherType;
+
+  StringRef EquivalentLiteralExpression = getEquivalentBoolLiteralForExpression(
+      CastExpression->getSubExpr(), Context);
+  if (!EquivalentLiteralExpression.empty()) {
+    addFixItHintsForLiteralCastToBool(Diagnostic, CastExpression,
+                                      EquivalentLiteralExpression);
+  } else {
+    addFixItHintsForGenericExpressionCastToBool(Diagnostic, CastExpression,
+                                                ParentStatement, Context);
+  }
+}
+
+void ImplicitBoolCastCheck::handleCastFromBool(
+    const ImplicitCastExpr *CastExpression,
+    const ImplicitCastExpr *FurtherImplicitCastExpression,
+    ASTContext &Context) {
+  QualType DestinationType = (FurtherImplicitCastExpression != nullptr)
+                                 ? FurtherImplicitCastExpression->getType()
+                                 : CastExpression->getType();
+  std::string DestinationTypeString = DestinationType.getAsString();
+  DiagnosticBuilder Diagnostic =
+      diag(CastExpression->getLocStart(), "implicit cast bool -> '%0'")
+      << DestinationTypeString;
+
+  if (llvm::isa<CXXBoolLiteralExpr>(CastExpression->getSubExpr())) {
+    addFixItHintsForLiteralCastFromBool(Diagnostic, CastExpression, Context,
+                                        DestinationType);
+  } else {
+    addFixItHintsForGenericExpressionCastFromBool(
+        Diagnostic, CastExpression, Context, DestinationTypeString);
+  }
+}
+
+} // 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..e9fa480
--- /dev/null
@@ -0,0 +1,49 @@
+//===--- 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)
+      : ClangTidyCheck(Name, Context),
+        AllowConditionalIntegerCasts(
+            Options.get("AllowConditionalIntegerCasts", 0) != 0),
+        AllowConditionalPointerCasts(
+            Options.get("AllowConditionalPointerCasts", 0) != 0) {}
+  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..e3e82d7
--- /dev/null
@@ -0,0 +1,336 @@
+//===--- 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/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..e57f736
--- /dev/null
@@ -0,0 +1,146 @@
+//===--- 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 NamespaceDecl *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, Result.Context->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/ReadabilityTidyModule.cpp b/clang-tidy/readability/ReadabilityTidyModule.cpp
new file mode 100644 (file)
index 0000000..51bda6f
--- /dev/null
@@ -0,0 +1,86 @@
+//===--- 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 "DeletedDefaultCheck.h"
+#include "ElseAfterReturnCheck.h"
+#include "FunctionSizeCheck.h"
+#include "IdentifierNamingCheck.h"
+#include "ImplicitBoolCastCheck.h"
+#include "InconsistentDeclarationParameterNameCheck.h"
+#include "NamedParameterCheck.h"
+#include "RedundantControlFlowCheck.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<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<StaticDefinitionInAnonymousNamespaceCheck>(
+        "readability-static-definition-in-anonymous-namespace");
+    CheckFactories.registerCheck<readability::NamedParameterCheck>(
+        "readability-named-parameter");
+    CheckFactories.registerCheck<RedundantControlFlowCheck>(
+        "readability-redundant-control-flow");
+    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..54a26e2
--- /dev/null
@@ -0,0 +1,99 @@
+//===--- 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,
+        Result.Context->getLangOpts(),
+        /*SkipTrailingWhitespaceAndNewLine=*/true);
+  else
+    Start = StmtRange.getBegin();
+  auto RemovedRange = CharSourceRange::getCharRange(
+      Start,
+      Lexer::findLocationAfterToken(StmtRange.getEnd(), tok::semi, SM,
+                                    Result.Context->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/RedundantSmartptrGetCheck.cpp b/clang-tidy/readability/RedundantSmartptrGetCheck.cpp
new file mode 100644 (file)
index 0000000..f36ed82
--- /dev/null
@@ -0,0 +1,131 @@
+//===--- 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(
+      anyOf(hasName("::std::unique_ptr"), hasName("::std::shared_ptr")));
+
+  // Matches against nullptr.
+  Finder->addMatcher(
+      binaryOperator(
+          anyOf(hasOperatorName("=="), hasOperatorName("!=")),
+          hasEitherOperand(ignoringImpCasts(cxxNullPtrLiteralExpr())),
+          hasEitherOperand(callToGet(IsAKnownSmartptr))),
+      Callback);
+  // TODO: Catch ptr.get() == other_ptr.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 Expr *GetCall = Result.Nodes.getNodeAs<Expr>("redundant_get");
+  const Expr *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, Result.Context->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..ecd173a
--- /dev/null
@@ -0,0 +1,209 @@
+//===- 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(hasName("c_str"))))
+          .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.getStmtAs<CallExpr>("call");
+  const auto *Arg = Result.Nodes.getStmtAs<Expr>("arg");
+  bool Arrow = Result.Nodes.getStmtAs<MemberExpr>("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 `c_str()`")
+      << 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..7ca8323
--- /dev/null
@@ -0,0 +1,71 @@
+//===- 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..151a6ad
--- /dev/null
@@ -0,0 +1,683 @@
+//===--- 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/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 RightExpressionId[] = "bool-op-expr-yields-expr";
+const char LeftExpressionId[] = "expr-op-bool-yields-expr";
+const char NegatedRightExpressionId[] = "bool-op-expr-yields-not-expr";
+const char NegatedLeftExpressionId[] = "expr-op-bool-yields-not-expr";
+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 LHSId[] = "lhs-expr";
+const char RHSId[] = "rhs-expr";
+
+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
+
+SimplifyBooleanExprCheck::SimplifyBooleanExprCheck(StringRef Name,
+                                                   ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      ChainedConditionalReturn(Options.get("ChainedConditionalReturn", 0U)),
+      ChainedConditionalAssignment(
+          Options.get("ChainedConditionalAssignment", 0U)) {}
+
+void SimplifyBooleanExprCheck::matchBoolBinOpExpr(MatchFinder *Finder,
+                                                  bool Value,
+                                                  StringRef OperatorName,
+                                                  StringRef BooleanId) {
+  Finder->addMatcher(
+      binaryOperator(
+          isExpansionInMainFile(), hasOperatorName(OperatorName),
+          hasLHS(allOf(expr().bind(LHSId),
+                       cxxBoolLiteral(equals(Value)).bind(BooleanId))),
+          hasRHS(expr().bind(RHSId)),
+          unless(hasRHS(hasDescendant(cxxBoolLiteral())))),
+      this);
+}
+
+void SimplifyBooleanExprCheck::matchExprBinOpBool(MatchFinder *Finder,
+                                                  bool Value,
+                                                  StringRef OperatorName,
+                                                  StringRef BooleanId) {
+  Finder->addMatcher(
+      binaryOperator(
+          isExpansionInMainFile(), hasOperatorName(OperatorName),
+          hasLHS(expr().bind(LHSId)),
+          unless(
+              hasLHS(anyOf(cxxBoolLiteral(), hasDescendant(cxxBoolLiteral())))),
+          hasRHS(allOf(expr().bind(RHSId),
+                       cxxBoolLiteral(equals(Value)).bind(BooleanId)))),
+      this);
+}
+
+void SimplifyBooleanExprCheck::matchBoolCompOpExpr(MatchFinder *Finder,
+                                                   bool Value,
+                                                   StringRef OperatorName,
+                                                   StringRef BooleanId) {
+  Finder->addMatcher(
+      binaryOperator(
+          isExpansionInMainFile(), hasOperatorName(OperatorName),
+          hasLHS(allOf(
+              expr().bind(LHSId),
+              ignoringImpCasts(cxxBoolLiteral(equals(Value)).bind(BooleanId)))),
+          hasRHS(expr().bind(RHSId)),
+          unless(hasRHS(hasDescendant(cxxBoolLiteral())))),
+      this);
+}
+
+void SimplifyBooleanExprCheck::matchExprCompOpBool(MatchFinder *Finder,
+                                                   bool Value,
+                                                   StringRef OperatorName,
+                                                   StringRef BooleanId) {
+  Finder->addMatcher(
+      binaryOperator(
+          isExpansionInMainFile(), hasOperatorName(OperatorName),
+          unless(hasLHS(hasDescendant(cxxBoolLiteral()))),
+          hasLHS(expr().bind(LHSId)),
+          hasRHS(allOf(expr().bind(RHSId),
+                       ignoringImpCasts(
+                           cxxBoolLiteral(equals(Value)).bind(BooleanId))))),
+      this);
+}
+
+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) {
+  matchBoolBinOpExpr(Finder, true, "&&", RightExpressionId);
+  matchBoolBinOpExpr(Finder, false, "||", RightExpressionId);
+  matchExprBinOpBool(Finder, false, "&&", RightExpressionId);
+  matchExprBinOpBool(Finder, true, "||", RightExpressionId);
+  matchBoolCompOpExpr(Finder, true, "==", RightExpressionId);
+  matchBoolCompOpExpr(Finder, false, "!=", RightExpressionId);
+
+  matchExprBinOpBool(Finder, true, "&&", LeftExpressionId);
+  matchExprBinOpBool(Finder, false, "||", LeftExpressionId);
+  matchBoolBinOpExpr(Finder, false, "&&", LeftExpressionId);
+  matchBoolBinOpExpr(Finder, true, "||", LeftExpressionId);
+  matchExprCompOpBool(Finder, true, "==", LeftExpressionId);
+  matchExprCompOpBool(Finder, false, "!=", LeftExpressionId);
+
+  matchBoolCompOpExpr(Finder, false, "==", NegatedRightExpressionId);
+  matchBoolCompOpExpr(Finder, true, "!=", NegatedRightExpressionId);
+
+  matchExprCompOpBool(Finder, false, "==", NegatedLeftExpressionId);
+  matchExprCompOpBool(Finder, true, "!=", NegatedLeftExpressionId);
+
+  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 *LeftRemoved =
+          getBoolLiteral(Result, RightExpressionId))
+    replaceWithExpression(Result, LeftRemoved, false);
+  else if (const CXXBoolLiteralExpr *RightRemoved =
+               getBoolLiteral(Result, LeftExpressionId))
+    replaceWithExpression(Result, RightRemoved, true);
+  else if (const CXXBoolLiteralExpr *NegatedLeftRemoved =
+               getBoolLiteral(Result, NegatedRightExpressionId))
+    replaceWithExpression(Result, NegatedLeftRemoved, false, true);
+  else if (const CXXBoolLiteralExpr *NegatedRightRemoved =
+               getBoolLiteral(Result, NegatedLeftExpressionId))
+    replaceWithExpression(Result, NegatedRightRemoved, true, true);
+  else 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);
+}
+
+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,
+      Result.Context->getLangOpts());
+
+  DiagnosticBuilder Diag = diag(Loc, Description);
+  if (!containsDiscardedTokens(Result, CharRange))
+    Diag << FixItHint::CreateReplacement(CharRange, Replacement);
+}
+
+void SimplifyBooleanExprCheck::replaceWithExpression(
+    const ast_matchers::MatchFinder::MatchResult &Result,
+    const CXXBoolLiteralExpr *BoolLiteral, bool UseLHS, bool Negated) {
+  const auto *LHS = Result.Nodes.getNodeAs<Expr>(LHSId);
+  const auto *RHS = Result.Nodes.getNodeAs<Expr>(RHSId);
+  std::string Replacement =
+      replacementExpression(Result, Negated, UseLHS ? LHS : RHS);
+  SourceRange Range(LHS->getLocStart(), RHS->getLocEnd());
+  issueDiag(Result, BoolLiteral->getLocStart(), SimplifyOperatorDiagnostic,
+            Range, 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..4a80765
--- /dev/null
@@ -0,0 +1,102 @@
+//===--- 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:
+  void matchBoolBinOpExpr(ast_matchers::MatchFinder *Finder, bool Value,
+                          StringRef OperatorName, StringRef BooleanId);
+
+  void matchExprBinOpBool(ast_matchers::MatchFinder *Finder, bool Value,
+                          StringRef OperatorName, StringRef BooleanId);
+
+  void matchBoolCompOpExpr(ast_matchers::MatchFinder *Finder, bool Value,
+                           StringRef OperatorName, StringRef BooleanId);
+
+  void matchExprCompOpBool(ast_matchers::MatchFinder *Finder, bool Value,
+                           StringRef OperatorName, StringRef BooleanId);
+
+  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
+  replaceWithExpression(const ast_matchers::MatchFinder::MatchResult &Result,
+                        const CXXBoolLiteralExpr *BoolLiteral, bool UseLHS,
+                        bool Negated = false);
+
+  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..9de2d40
--- /dev/null
@@ -0,0 +1,72 @@
+//===--- 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 {
+
+namespace {
+AST_POLYMORPHIC_MATCHER(isStatic, AST_POLYMORPHIC_SUPPORTED_TYPES(FunctionDecl,
+                                                                  VarDecl)) {
+  return Node.getStorageClass() == SC_Static;
+}
+} // namespace
+
+void StaticDefinitionInAnonymousNamespaceCheck::registerMatchers(
+    MatchFinder *Finder) {
+  Finder->addMatcher(namedDecl(anyOf(functionDecl(isDefinition(), isStatic()),
+                                     varDecl(isDefinition(), isStatic())),
+                               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,
+                             Result.Context->getLangOpts(), true)) {
+    SourceRange TokenRange(Tok.getLocation(), Tok.getEndLoc());
+    StringRef SourceText = Lexer::getSourceText(
+        CharSourceRange::getTokenRange(TokenRange),
+        *Result.SourceManager, Result.Context->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..eeaec6a
--- /dev/null
@@ -0,0 +1,35 @@
+//===--- 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..3a7e5dc
--- /dev/null
@@ -0,0 +1,71 @@
+//===--- 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,
+                                 Result.Context->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..43ca22d
--- /dev/null
@@ -0,0 +1,37 @@
+//===--- 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..4f11df8
--- /dev/null
@@ -0,0 +1,93 @@
+#!/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 re
+import sys
+import glob
+
+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():
+  if len(sys.argv) != 4:
+    print('Usage: rename_check.py <module> <old-check-name> <new-check-name>\n')
+    print('       example: rename_check.py misc awesome-functions new-awesome-function')
+    return
+
+  module = sys.argv[1].lower()
+  check_name = sys.argv[2]
+  check_name_camel = ''.join(map(lambda elem: elem.capitalize(),
+                                 check_name.split('-'))) + 'Check'
+  check_name_new = sys.argv[3]
+  check_name_new_camel = ''.join(map(lambda elem: elem.capitalize(),
+                                 check_name_new.split('-'))) + 'Check'
+
+  clang_tidy_path = os.path.dirname(sys.argv[0])
+
+  header_guard_old = module.upper() + '_' + check_name.upper().replace('-', '_')
+  header_guard_new = module.upper() + '_' + check_name_new.upper().replace('-', '_')
+
+  for filename in getListOfFiles(clang_tidy_path):
+    originalName = filename
+    filename = fileRename(filename, check_name, check_name_new)
+    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, check_name, check_name_new)
+    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..45ec798
--- /dev/null
@@ -0,0 +1,32 @@
+set(LLVM_LINK_COMPONENTS
+  support
+  )
+
+add_clang_executable(clang-tidy
+  ClangTidyMain.cpp
+  )
+add_dependencies(clang-tidy
+  clang-headers
+  )
+target_link_libraries(clang-tidy
+  clangAST
+  clangASTMatchers
+  clangBasic
+  clangTidy
+  clangTidyBoostModule
+  clangTidyCERTModule
+  clangTidyCppCoreGuidelinesModule
+  clangTidyGoogleModule
+  clangTidyLLVMModule
+  clangTidyMiscModule
+  clangTidyModernizeModule
+  clangTidyPerformanceModule
+  clangTidyReadabilityModule
+  clangTooling
+  )
+
+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..eed7d31
--- /dev/null
@@ -0,0 +1,472 @@
+//===--- 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
+    User:            user
+    CheckOptions:
+      - key:             some-check.SomeOption
+        value:           'some value'
+    ...
+
+)");
+
+const char DefaultChecks[] =  // Enable these checks:
+    "clang-diagnostic-*,"     //   * compiler diagnostics
+    "clang-analyzer-*,"       //   * Static Analyzer checks
+    "-clang-analyzer-alpha*"; //   * but not alpha checks: many false positives
+
+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<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 sorce
+code with clang-apply-replacements.
+)"),
+                                        cl::value_desc("filename"),
+                                        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.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 (!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 OptionsProvider = createOptionsProvider();
+  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;
+
+  std::vector<ClangTidyError> Errors;
+  ClangTidyStats Stats =
+      runClangTidy(std::move(OptionsProvider), OptionsParser.getCompilations(),
+                   PathList, &Errors,
+                   EnableCheckProfile ? &Profile : nullptr);
+  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(Errors, (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(Errors, OS);
+  }
+
+  printStats(Stats);
+  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) {
+    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 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 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
+
+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..e3dcbe7
--- /dev/null
@@ -0,0 +1,121 @@
+#!/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='')
+  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)
+  command.extend(lines_by_file.keys())
+  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..e4dd3da
--- /dev/null
@@ -0,0 +1,199 @@
+#!/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
+"""
+
+import argparse
+import json
+import multiprocessing
+import os
+import Queue
+import re
+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 get_tidy_invocation(f, clang_tidy_binary, checks, tmpdir, build_path,
+                        header_filter):
+  """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)
+  start.append('-p=' + build_path)
+  start.append(f)
+  return start
+
+
+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')
+  invocation.append(tmpdir)
+  subprocess.call(invocation)
+  shutil.rmtree(tmpdir)
+
+
+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)
+    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('-p', dest='build_path',
+                      help='Path used to read a compile command database.')
+  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 >>sys.stderr, "Unable to run clang-tidy."
+    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:
+    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 ...'
+    apply_fixes(args, tmpdir)
+
+if __name__ == '__main__':
+  main()
diff --git a/clang-tidy/utils/CMakeLists.txt b/clang-tidy/utils/CMakeLists.txt
new file mode 100644 (file)
index 0000000..557c7d4
--- /dev/null
@@ -0,0 +1,20 @@
+set(LLVM_LINK_COMPONENTS support)
+
+add_clang_library(clangTidyUtils
+  DeclRefExprUtils.cpp
+  FixItHintUtils.cpp
+  HeaderFileExtensionsUtils.cpp
+  HeaderGuard.cpp
+  IncludeInserter.cpp
+  IncludeSorter.cpp
+  LexerUtils.cpp
+  OptionsUtils.cpp
+  TypeTraits.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..11d0c31
--- /dev/null
@@ -0,0 +1,126 @@
+//===--- 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;
+}
+
+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;
+}
+
+bool isCopyConstructorArgument(const DeclRefExpr &DeclRef, const Stmt &Stmt,
+                               ASTContext &Context) {
+  auto UsedAsConstRefArg = forEachArgumentWithParam(
+      declRefExpr(equalsNode(&DeclRef)),
+      parmVarDecl(hasType(matchers::isReferenceToConst())));
+  auto Matches = match(
+      stmt(hasDescendant(
+          cxxConstructExpr(UsedAsConstRefArg, hasDeclaration(cxxConstructorDecl(
+                                                  isCopyConstructor())))
+              .bind("constructExpr"))),
+      Stmt, Context);
+  return !Matches.empty();
+}
+
+bool isCopyAssignmentArgument(const DeclRefExpr &DeclRef, const Stmt &Stmt,
+                              ASTContext &Context) {
+  auto UsedAsConstRefArg = forEachArgumentWithParam(
+      declRefExpr(equalsNode(&DeclRef)),
+      parmVarDecl(hasType(matchers::isReferenceToConst())));
+  auto Matches = match(
+      stmt(hasDescendant(
+          cxxOperatorCallExpr(UsedAsConstRefArg, hasOverloadedOperatorName("="))
+              .bind("operatorCallExpr"))),
+      Stmt, 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..5d56f7f
--- /dev/null
@@ -0,0 +1,55 @@
+//===--- 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 in Stmt.
+llvm::SmallPtrSet<const DeclRefExpr *, 16>
+allDeclRefExprs(const VarDecl &VarDecl, const Stmt &Stmt, ASTContext &Context);
+
+// Returns set of all DeclRefExprs 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 true if DeclRefExpr is the argument of a copy-constructor call expr.
+bool isCopyConstructorArgument(const DeclRefExpr &DeclRef, const Stmt &Stmt,
+                               ASTContext &Context);
+
+// Returns true if DeclRefExpr is the argument of a copy-assignment operator
+// call expr.
+bool isCopyAssignmentArgument(const DeclRefExpr &DeclRef, const Stmt &Stmt,
+                              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/FixItHintUtils.cpp b/clang-tidy/utils/FixItHintUtils.cpp
new file mode 100644 (file)
index 0000000..d385cef
--- /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::getPreviousNonCommentToken(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..557f119
--- /dev/null
@@ -0,0 +1,66 @@
+//===--- 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);
+  StringRef FileExtension =
+      llvm::sys::path::extension(SM.getFilename(ExpansionLoc));
+  return HeaderFileExtensions.count(FileExtension.substr(1)) > 0;
+}
+
+bool isPresumedLocInHeaderFile(
+    SourceLocation Loc, SourceManager &SM,
+    const HeaderFileExtensionsSet &HeaderFileExtensions) {
+  PresumedLoc PresumedLocation = SM.getPresumedLoc(Loc);
+  StringRef FileExtension =
+      llvm::sys::path::extension(PresumedLocation.getFilename());
+  return HeaderFileExtensions.count(FileExtension.substr(1)) > 0;
+}
+
+bool isSpellingLocInHeaderFile(
+    SourceLocation Loc, SourceManager &SM,
+    const HeaderFileExtensionsSet &HeaderFileExtensions) {
+  SourceLocation SpellingLoc = SM.getSpellingLoc(Loc);
+  StringRef FileExtension =
+      llvm::sys::path::extension(SM.getFilename(SpellingLoc));
+
+  return HeaderFileExtensions.count(FileExtension.substr(1)) > 0;
+}
+
+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;
+}
+
+} // 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..70bdc81
--- /dev/null
@@ -0,0 +1,48 @@
+//===--- 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/StringRef.h"
+#include "llvm/ADT/SmallSet.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);
+
+} // 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..6eb241c
--- /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
+      // preceeded 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 is a header guard macro but it's not in the topmost position
+      // emit a plain warning without fix-its. This often happens when the guard
+      // macro is preceeded by includes.
+      // 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,
+              "Header guard after code/includes. Consider moving it up.");
+          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 FileName.endswith(".h");
+}
+
+bool HeaderGuardCheck::shouldFixHeaderGuard(StringRef FileName) { return true; }
+
+bool HeaderGuardCheck::shouldSuggestToAddHeaderGuard(StringRef FileName) {
+  return FileName.endswith(".h");
+}
+
+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..cb88f08
--- /dev/null
@@ -0,0 +1,48 @@
+//===--- 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"
+
+namespace clang {
+namespace tidy {
+namespace utils {
+
+/// Finds and fixes header guards.
+class HeaderGuardCheck : public ClangTidyCheck {
+public:
+  HeaderGuardCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  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;
+};
+
+} // 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..3a31889
--- /dev/null
@@ -0,0 +1,86 @@
+//===-------- 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..2a8a978
--- /dev/null
@@ -0,0 +1,289 @@
+//===---------- 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.equals(CanonicalInclude)) {
+    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..f80661d
--- /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 getPreviousNonCommentToken(const ASTContext &Context,
+                                 SourceLocation Location) {
+  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()) &&
+        !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..f218592
--- /dev/null
@@ -0,0 +1,31 @@
+//===--- 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 non-comment token skipping over any comment text or
+/// ``tok::unknown`` if not found.
+Token getPreviousNonCommentToken(const ASTContext &Context,
+                                 SourceLocation Location);
+
+} // 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..d294ed7
--- /dev/null
@@ -0,0 +1,53 @@
+//===--- 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 "clang/ASTMatchers/ASTMatchers.h"
+#include "TypeTraits.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/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..ea379f6
--- /dev/null
@@ -0,0 +1,142 @@
+//===--- 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())
+    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 class is trivially constructible if it has a trivial default constructor.
+  if (ClassDecl->hasTrivialDefaultConstructor())
+    return true;
+
+  // If all its fields are trivially constructible.
+  for (const FieldDecl *Field : ClassDecl->fields()) {
+    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;
+  }
+
+  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/docs/CMakeLists.txt b/docs/CMakeLists.txt
new file mode 100644 (file)
index 0000000..d6c2456
--- /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)
+  if (SPHINX_FOUND)
+    include(AddSphinxTarget)
+    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..b7c4b65
--- /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..c5279ab
--- /dev/null
@@ -0,0 +1,385 @@
+===================================
+Extra Clang Tools 3.9 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 3.9.  Here we describe the status of the Extra Clang Tools in some
+detail, including major improvements from the previous release and new feature
+work. For the general Clang release notes, see `the Clang documentation
+<http://llvm.org/releases/3.8.0/tools/clang/docs/ReleaseNotes.html>`_.  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 3.9?
+====================================
+
+Some of the major new features and improvements to Extra Clang Tools are listed
+here. Generic improvements to Extra Clang Tools as a whole or to its underlying
+infrastructure are described first, followed by tool-specific sections.
+
+Major New Features
+------------------
+
+- :program:`clang-include-fixer`, a tool that provides an automated way of
+  adding ``#include`` directives for missing symbols in one translation unit.
+
+  It aims to provide automated insertion of missing ``#includes`` with a single
+  button press in an editor. Integration with Vim and a tool to generate the
+  symbol index used by the tool are also part of this release. See the
+  `include-fixer documentation`_ for more information.
+
+.. _include-fixer documentation: http://clang.llvm.org/extra/include-fixer.html
+
+Improvements to clang-tidy
+--------------------------
+
+:program:`clang-tidy`'s checks are constantly being improved to catch more issues,
+explain them more clearly, and provide more accurate fix-its for the issues
+identified.  The improvements since the 3.8 release include:
+
+- New Boost module containing checks for issues with Boost library.
+
+- New `boost-use-to-string 
+  <http://clang.llvm.org/extra/clang-tidy/checks/boost-use-to-string.html>`_ check
+
+  Finds usages of ``boost::lexical_cast<std::string>`` and changes it to
+  ``std::to_string``.
+
+- New `cert-env33-c
+  <http://clang.llvm.org/extra/clang-tidy/checks/cert-env33-c.html>`_ check
+
+  Flags calls to ``system()``, ``popen()``, and ``_popen()``, which execute a
+  command processor.
+
+- New `cert-err34-c
+  <http://clang.llvm.org/extra/clang-tidy/checks/cert-err34-c.html>`_ check
+
+  Flags calls to string-to-number conversion functions that do not verify the
+  validity of the conversion.
+
+- New `cert-flp30-c
+  <http://clang.llvm.org/extra/clang-tidy/checks/cert-flp30-c.html>`_ check
+
+  Flags ``for`` loops where the induction expression has a floating-point type.
+
+- New `cppcoreguidelines-interfaces-global-init
+  <http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-interfaces-global-init.html>`_ check
+
+  Flags initializers of globals that access extern objects, and therefore can
+  lead to order-of-initialization problems.
+
+- New `cppcoreguidelines-pro-type-member-init
+  <http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-pro-type-member-init.html>`_ check
+
+  Flags user-defined constructor definitions that do not initialize all builtin
+  and pointer fields which leaves their memory in an undefined state.
+
+- New `google-default-arguments
+  <http://clang.llvm.org/extra/clang-tidy/checks/google-default-arguments.html>`_ check
+
+  Flags default arguments in virtual methods.
+
+- New `misc-dangling-handle
+  <http://clang.llvm.org/extra/clang-tidy/checks/misc-dangling-handle.html>`_ check
+
+  Detects dangling references in value handlers like
+  ``std::experimental::string_view``.
+
+- New `misc-fold-init-type
+  <http://clang.llvm.org/extra/clang-tidy/checks/misc-fold-init-type.html>`_ check
+
+  The check flags type mismatches in `folds` like ``std::accumulate`` that might
+  result in loss of precision.
+
+- New `misc-forward-declaration-namespace
+  <http://clang.llvm.org/extra/clang-tidy/checks/misc-forward-declaration-namespace.html>`_ check
+
+  Checks if an unused forward declaration is in a wrong namespace.
+
+- New `misc-misplaced-const
+  <http://clang.llvm.org/extra/clang-tidy/checks/misc-misplaced-const.html>`_ check
+  
+  Checks if a ``const`` qualifier is applied to a ``typedef`` to pointer type
+  instead of the underlying pointee type.
+
+- New `misc-misplaced-widening-cast
+  <http://clang.llvm.org/extra/clang-tidy/checks/misc-misplaced-widening-cast.html>`_ check
+
+  Warns when there is a explicit redundant cast of a calculation result to a
+  bigger type.
+
+- New `misc-multiple-statement-macro
+  <http://clang.llvm.org/extra/clang-tidy/checks/misc-multiple-statement-macro.html>`_ check
+
+  Detect multiple statement macros that are used in unbraced conditionals.
+
+- New `misc-pointer-and-integral-operation
+  <http://clang.llvm.org/extra/clang-tidy/checks/misc-pointer-and-integral-operation.html>`_ check
+
+  Warns about suspicious operations involving pointers and integral types.
+
+- New `misc-redundant-expression
+  <http://clang.llvm.org/extra/clang-tidy/checks/misc-redundant-expression.html>`_ check
+
+  Warns about redundant and equivalent expressions.
+
+- New `misc-sizeof-expression
+  <http://clang.llvm.org/extra/clang-tidy/checks/misc-sizeof-expression.html>`_ check
+
+  Warns about incorrect uses of ``sizeof`` operator.
+
+- New `misc-string-constructor
+  <http://clang.llvm.org/extra/clang-tidy/checks/misc-string-constructor.html>`_ check
+
+  Finds string constructors that are suspicious and probably errors.
+
+- New `misc-string-literal-with-embedded-nul
+  <http://clang.llvm.org/extra/clang-tidy/checks/misc-string-literal-with-embedded-nul.html>`_ check
+
+  Warns about suspicious NUL character in string literals which may lead to
+  truncation or invalid character escaping.
+
+- New `misc-suspicious-missing-comma
+  <http://clang.llvm.org/extra/clang-tidy/checks/misc-suspicious-missing-comma.html>`_ check
+
+  Warns about 'probably' missing comma in string literals initializer list.
+
+- New `misc-suspicious-semicolon
+  <http://clang.llvm.org/extra/clang-tidy/checks/misc-suspicious-semicolon.html>`_ check
+
+  Finds most instances of stray semicolons that unexpectedly alter the meaning
+  of the code.
+
+- New `misc-suspicious-string-compare
+  <http://clang.llvm.org/extra/clang-tidy/checks/misc-suspicious-string-compare.html>`_ check
+
+  Find suspicious usage of runtime string comparison functions.
+
+- New `misc-unconventional-assign-operator
+  <http://clang.llvm.org/extra/clang-tidy/checks/misc-unconventional-assign-operator.html>`_
+  check replacing the *misc-assign-operator-signature* check.
+
+  Does not only checks for correct signature but also for correct ``return``
+  statements (returning ``*this``)
+
+- New `misc-unused-using-decls
+  <http://clang.llvm.org/extra/clang-tidy/checks/misc-unused-using-decls.html>`_ check
+
+  Finds unused ``using`` declarations.
+
+- New `modernize-avoid-bind
+  <http://clang.llvm.org/extra/clang-tidy/checks/modernize-avoid-bind.html>`_ check
+
+  Finds uses of ``std::bind`` and replaces simple uses with lambdas.
+
+- New `modernize-deprecated-headers
+  <http://clang.llvm.org/extra/clang-tidy/checks/modernize-deprecated-headers.html>`_ check
+
+  Replaces C standard library headers with their C++ alternatives.
+
+- New `modernize-make-shared
+  <http://clang.llvm.org/extra/clang-tidy/checks/modernize-make-shared.html>`_ check
+
+  Replaces creation of ``std::shared_ptr`` from new expression with call to ``std::make_shared``.
+
+- New `modernize-raw-string-literal
+  <http://clang.llvm.org/extra/clang-tidy/checks/modernize-raw-string-literal.html>`_ check
+
+  Selectively replaces string literals containing escaped characters with raw
+  string literals.
+
+- New `modernize-use-bool-literals
+  <http://clang.llvm.org/extra/clang-tidy/checks/modernize-use-bool-literals.html>`_ check
+
+  Finds integer literals which are cast to ``bool``.
+
+- New `modernize-use-emplace
+  <http://clang.llvm.org/extra/clang-tidy/checks/modernize-use-emplace.html>`_ check
+
+  Finds calls that could be changed to emplace.
+
+- New `modernize-use-using
+  <http://clang.llvm.org/extra/clang-tidy/checks/modernize-use-using.html>`_ check
+
+  Finds typedefs and replaces it with usings.
+
+- New `performance-faster-string-find
+  <http://clang.llvm.org/extra/clang-tidy/checks/performance-faster-string-find.html>`_ check
+
+  Optimize calls to ``std::string::find()`` and friends when the needle passed
+  is a single character string literal.
+
+- New `performance-implicit-cast-in-loop
+  <http://clang.llvm.org/extra/clang-tidy/checks/performance-implicit-cast-in-loop.html>`_ check
+
+  Warns about 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.
+
+- New `performance-unnecessary-value-param
+  <http://clang.llvm.org/extra/clang-tidy/checks/performance-unnecessary-value-param.html>`_ check
+
+  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.
+
+- New `readability-avoid-const-params-in-decls
+  <http://clang.llvm.org/extra/clang-tidy/checks/readability-avoid-const-params-in-decls.html>`_ check
+
+  Warns about top-level const parameters in function declarations.
+
+- New `readability-deleted-default
+  <http://clang.llvm.org/extra/clang-tidy/checks/readability-deleted-default.html>`_ check
+
+  Warns about defaulted constructors and assignment operators that are actually
+  deleted.
+
+- Updated `readability-identifier-naming-check
+  <http://clang.llvm.org/extra/clang-tidy/checks/readability-identifier-naming.html>`_
+
+  Added support for enforcing the case of macro statements.
+
+- New `readability-redundant-control-flow
+  <http://clang.llvm.org/extra/clang-tidy/checks/readability-redundant-control-flow.html>`_ check
+
+  Looks for procedures (functions returning no value) with ``return`` statements
+  at the end of the function.  Such `return` statements are redundant.
+
+- New `readability-redundant-string-init
+  <http://clang.llvm.org/extra/clang-tidy/checks/readability-redundant-string-init.html>`_ check
+
+  Finds unnecessary string initializations.
+
+- New `readability-static-definition-in-anonymous-namespace
+  <http://clang.llvm.org/extra/clang-tidy/checks/readability-static-definition-in-anonymous-namespace.html>`_ check
+
+  Finds static function and variable definitions in anonymous namespace.
+
+Fixed bugs:
+
+- Crash when running on compile database with relative source files paths.
+
+- Crash when running with the `-fdelayed-template-parsing` flag.
+
+- The `modernize-use-override` check: incorrect fix-its placement around
+  ``__declspec`` and other attributes.
+
+Clang-tidy changes from 3.7 to 3.8
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The 3.8 release didn't include release notes for :program:`clang-tidy`. In the
+3.8 release many new checks have been added to :program:`clang-tidy`:
+
+- Checks enforcing certain rules of the `CERT Secure Coding Standards
+  <https://www.securecoding.cert.org/confluence/display/seccode/SEI+CERT+Coding+Standards>`_:
+
+  * `cert-dcl03-c
+    <http://llvm.org/releases/3.8.0/tools/clang/tools/extra/docs/clang-tidy/checks/cert-dcl03-c.html>`_
+    (an alias to the pre-existing check `misc-static-assert
+    <http://llvm.org/releases/3.8.0/tools/clang/tools/extra/docs/clang-tidy/checks/misc-static-assert.html>`_)
+  * `cert-dcl50-cpp
+    <http://llvm.org/releases/3.8.0/tools/clang/tools/extra/docs/clang-tidy/checks/cert-dcl50-cpp.html>`_
+  * `cert-err52-cpp
+    <http://llvm.org/releases/3.8.0/tools/clang/tools/extra/docs/clang-tidy/checks/cert-err52-cpp.html>`_
+  * `cert-err58-cpp
+    <http://llvm.org/releases/3.8.0/tools/clang/tools/extra/docs/clang-tidy/checks/cert-err58-cpp.html>`_
+  * `cert-err60-cpp
+    <http://llvm.org/releases/3.8.0/tools/clang/tools/extra/docs/clang-tidy/checks/cert-err60-cpp.html>`_
+  * `cert-err61-cpp
+    <http://llvm.org/releases/3.8.0/tools/clang/tools/extra/docs/clang-tidy/checks/cert-err61-cpp.html>`_
+  * `cert-fio38-c
+    <http://llvm.org/releases/3.8.0/tools/clang/tools/extra/docs/clang-tidy/checks/cert-fio38-c.html>`_
+    (an alias to the pre-existing check `misc-non-copyable-objects
+    <http://llvm.org/releases/3.8.0/tools/clang/tools/extra/docs/clang-tidy/checks/misc-non-copyable-objects.html>`_)
+  * `cert-oop11-cpp
+    <http://llvm.org/releases/3.8.0/tools/clang/tools/extra/docs/clang-tidy/checks/cert-oop11-cpp.html>`_
+    (an alias to the pre-existing check `misc-move-constructor-init
+    <http://llvm.org/releases/3.8.0/tools/clang/tools/extra/docs/clang-tidy/checks/misc-move-constructor-init.html>`_)
+
+- Checks supporting the `C++ Core Guidelines
+  <https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md>`_:
+
+  * `cppcoreguidelines-pro-bounds-array-to-pointer-decay
+    <http://llvm.org/releases/3.8.0/tools/clang/tools/extra/docs/clang-tidy/checks/cppcoreguidelines-pro-bounds-array-to-pointer-decay.html>`_
+  * `cppcoreguidelines-pro-bounds-constant-array-index
+    <http://llvm.org/releases/3.8.0/tools/clang/tools/extra/docs/clang-tidy/checks/cppcoreguidelines-pro-bounds-constant-array-index.html>`_
+  * `cppcoreguidelines-pro-bounds-pointer-arithmetic
+    <http://llvm.org/releases/3.8.0/tools/clang/tools/extra/docs/clang-tidy/checks/cppcoreguidelines-pro-bounds-pointer-arithmetic.html>`_
+  * `cppcoreguidelines-pro-type-const-cast
+    <http://llvm.org/releases/3.8.0/tools/clang/tools/extra/docs/clang-tidy/checks/cppcoreguidelines-pro-type-const-cast.html>`_
+  * `cppcoreguidelines-pro-type-cstyle-cast
+    <http://llvm.org/releases/3.8.0/tools/clang/tools/extra/docs/clang-tidy/checks/cppcoreguidelines-pro-type-cstyle-cast.html>`_
+  * `cppcoreguidelines-pro-type-reinterpret-cast
+    <http://llvm.org/releases/3.8.0/tools/clang/tools/extra/docs/clang-tidy/checks/cppcoreguidelines-pro-type-reinterpret-cast.html>`_
+  * `cppcoreguidelines-pro-type-static-cast-downcast
+    <http://llvm.org/releases/3.8.0/tools/clang/tools/extra/docs/clang-tidy/checks/cppcoreguidelines-pro-type-static-cast-downcast.html>`_
+  * `cppcoreguidelines-pro-type-union-access
+    <http://llvm.org/releases/3.8.0/tools/clang/tools/extra/docs/clang-tidy/checks/cppcoreguidelines-pro-type-union-access.html>`_
+  * `cppcoreguidelines-pro-type-vararg
+    <http://llvm.org/releases/3.8.0/tools/clang/tools/extra/docs/clang-tidy/checks/cppcoreguidelines-pro-type-vararg.html>`_
+
+- The functionality of the :program:`clang-modernize` tool has been moved to the
+  new ``modernize`` module in :program:`clang-tidy` along with a few new checks:
+
+  * `modernize-loop-convert
+    <http://llvm.org/releases/3.8.0/tools/clang/tools/extra/docs/clang-tidy/checks/modernize-loop-convert.html>`_
+  * `modernize-make-unique
+    <http://llvm.org/releases/3.8.0/tools/clang/tools/extra/docs/clang-tidy/checks/modernize-make-unique.html>`_
+  * `modernize-pass-by-value
+    <http://llvm.org/releases/3.8.0/tools/clang/tools/extra/docs/clang-tidy/checks/modernize-pass-by-value.html>`_
+  * `modernize-redundant-void-arg
+    <http://llvm.org/releases/3.8.0/tools/clang/tools/extra/docs/clang-tidy/checks/modernize-redundant-void-arg.html>`_
+  * `modernize-replace-auto-ptr
+    <http://llvm.org/releases/3.8.0/tools/clang/tools/extra/docs/clang-tidy/checks/modernize-replace-auto-ptr.html>`_
+  * `modernize-shrink-to-fit
+    <http://llvm.org/releases/3.8.0/tools/clang/tools/extra/docs/clang-tidy/checks/modernize-shrink-to-fit.html>`_
+    (renamed from ``readability-shrink-to-fit``)
+  * `modernize-use-auto
+    <http://llvm.org/releases/3.8.0/tools/clang/tools/extra/docs/clang-tidy/checks/modernize-use-auto.html>`_
+  * `modernize-use-default
+    <http://llvm.org/releases/3.8.0/tools/clang/tools/extra/docs/clang-tidy/checks/modernize-use-default.html>`_
+  * `modernize-use-nullptr
+    <http://llvm.org/releases/3.8.0/tools/clang/tools/extra/docs/clang-tidy/checks/modernize-use-nullptr.html>`_
+  * `modernize-use-override
+    <http://llvm.org/releases/3.8.0/tools/clang/tools/extra/docs/clang-tidy/checks/modernize-use-override.html>`_
+    (renamed from ``misc-use-override``)
+
+- New checks flagging various readability-related issues:
+
+  * `readability-identifier-naming
+    <http://llvm.org/releases/3.8.0/tools/clang/tools/extra/docs/clang-tidy/checks/readability-identifier-naming.html>`_
+  * `readability-implicit-bool-cast
+    <http://llvm.org/releases/3.8.0/tools/clang/tools/extra/docs/clang-tidy/checks/readability-implicit-bool-cast.html>`_
+  * `readability-inconsistent-declaration-parameter-name
+    <http://llvm.org/releases/3.8.0/tools/clang/tools/extra/docs/clang-tidy/checks/readability-inconsistent-declaration-parameter-name.html>`_
+  * `readability-uniqueptr-delete-release
+    <http://llvm.org/releases/3.8.0/tools/clang/tools/extra/docs/clang-tidy/checks/readability-uniqueptr-delete-release.html>`_
+
+- Updated ``cppcoreguidelines-pro-member-type-member-init`` check
+
+  This check now conforms to C++ Core Guidelines rule Type.6: Always Initialize
+  a Member Variable. The check examines every record type where construction
+  might result in an undefined memory state. These record types needing
+  initialization have at least one default-initialized built-in, pointer,
+  array or record type matching these criteria or a default-initialized
+  direct base class of this kind.
+
+  The check has two complementary aspects:
+
+  1. Ensure every constructor for a record type needing initialization
+     value-initializes all members and direct bases via a combination of
+     in-class initializers and the member initializer list.
+  2. Value-initialize every non-member instance of a record type needing
+     initialization that lacks a user-provided default constructor, e.g.
+     a POD.
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..0b37669
--- /dev/null
@@ -0,0 +1,99 @@
+============
+Clang-Rename
+============
+
+.. contents::
+
+See also:
+
+.. toctree::
+   :maxdepth: 1
+
+
+:program:`clang-rename` is a clang-based C++ "linter" 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 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.
+
+.. code-block:: console
+
+  $ clang-rename -help
+  OVERVIEW: A tool to rename symbols in C/C++ code.
+  clang-rename renames every occurrence of a symbol found at <offset> in
+  <source0>. If -i is specified, the edited files are overwritten to disk.
+  Otherwise, the results are written to stdout.
+
+  USAGE: clang-rename [subcommand] [options] <source0> [... <sourceN>]
+
+  OPTIONS:
+
+  Clang-rename 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
+    -i                         - Overwrite edited <file>s.
+    -new-name=<string>         - The new name to change the symbol to.
+    -offset=<uint>             - Locates the symbol by offset as opposed to <line>:<column>.
+    -old-name=<string>         - The fully qualified name of the symbol, if -offset is not used.
+    -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.
+
+  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 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>`_.
+
+Once installed, you can point your cursor to the symbols you want to rename,
+press `,cr` and print 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/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/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-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-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-err34-c.rst b/docs/clang-tidy/checks/cert-err34-c.rst
new file mode 100644 (file)
index 0000000..11a8cec
--- /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:: 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..139d227
--- /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 constructor for the object may throw an exception.
+
+This check corresponds to the CERT C++ Coding Standard rule
+`ERR58-CPP. Constructors of objects with static or thread storage duration must not throw exceptions
+<https://www.securecoding.cert.org/confluence/display/cplusplus/ERR58-CPP.+Constructors+of+objects+with+static+or+thread+storage+duration+must+not+throw+exceptions>`_.
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-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-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..6e2f55d
--- /dev/null
@@ -0,0 +1,15 @@
+.. 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 clang-diagnostic-array-bounds check.
+
+The check can generate fixes after the option :option:`GslHeader` has been set
+to the name of the include file that contains ``gsl::at()``, e.g. `"gsl/gsl.h"`.
+
+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.
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..31ba786
--- /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..96fc6a0
--- /dev/null
@@ -0,0 +1,37 @@
+.. 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.
+
+IgnoreArrays option
+-------------------
+
+For performance critical code, it may be important to not zero
+fixed-size array members. If on, IgnoreArrays will not warn about
+array members that are not zero-initialized during construction.
+IgnoreArrays is false by default.
+
+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..1d29af5
--- /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 fixit 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/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..4249c28
--- /dev/null
@@ -0,0 +1,12 @@
+.. 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`.
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..baa8160
--- /dev/null
@@ -0,0 +1,20 @@
+.. title:: clang-tidy - google-build-using-namespace
+
+google-build-using-namespace
+============================
+
+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:: 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..8bd706d
--- /dev/null
@@ -0,0 +1,9 @@
+.. title:: clang-tidy - google-explicit-constructor
+
+google-explicit-constructor
+===========================
+
+
+Checks that all single-argument constructors are explicit.
+
+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..02aa52c
--- /dev/null
@@ -0,0 +1,17 @@
+.. 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 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.
+
+The relevant style guide section is
+https://google.github.io/styleguide/cppguide.html#Namespaces.
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..1b84ca9
--- /dev/null
@@ -0,0 +1,11 @@
+.. title:: clang-tidy - google-readability-namespace-comments
+
+google-readability-namespace-comments
+=====================================
+
+
+Checks that long namespaces have a closing comment.
+
+http://llvm.org/docs/CodingStandards.html#namespace-indentation
+
+https://google.github.io/styleguide/cppguide.html#Namespaces
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..ec28626
--- /dev/null
@@ -0,0 +1,16 @@
+.. title:: clang-tidy - google-readability-redundant-smartptr-get
+
+google-readability-redundant-smartptr-get
+=========================================
+
+
+Find and remove redundant calls to smart pointer's ``.get()`` method.
+
+Examples:
+
+.. code:: c++
+
+  ptr.get()->Foo()  ==>  ptr->Foo()
+  *ptr.get()  ==>  *ptr
+  *ptr->get()  ==>  **ptr
+
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..89072f1
--- /dev/null
@@ -0,0 +1,12 @@
+.. 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`.
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..8e468e3
--- /dev/null
@@ -0,0 +1,26 @@
+.. 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:: 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-memset.rst b/docs/clang-tidy/checks/google-runtime-memset.rst
new file mode 100644 (file)
index 0000000..3832b6e
--- /dev/null
@@ -0,0 +1,10 @@
+.. title:: clang-tidy - google-runtime-memset
+
+google-runtime-memset
+=====================
+
+Finds calls to ``memset`` with a literal zero in the length argument.
+
+This is most likely unintended and the length and value arguments are swapped.
+
+Corresponding cpplint.py check name: `runtime/memset`.
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..e6e5700
--- /dev/null
@@ -0,0 +1,9 @@
+.. 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
diff --git a/docs/clang-tidy/checks/list.rst b/docs/clang-tidy/checks/list.rst
new file mode 100644 (file)
index 0000000..f2cf6a2
--- /dev/null
@@ -0,0 +1,132 @@
+.. title:: clang-tidy - Clang-Tidy Checks
+
+Clang-Tidy Checks
+=========================
+
+.. toctree::
+   boost-use-to-string
+   cert-dcl03-c (redirects to misc-static-assert) <cert-dcl03-c>
+   cert-dcl50-cpp
+   cert-dcl54-cpp (redirects to misc-new-delete-overloads) <cert-dcl54-cpp>
+   cert-dcl59-cpp (redirects to google-build-namespaces) <cert-dcl59-cpp>
+   cert-env33-c
+   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-oop11-cpp (redirects to misc-move-constructor-init) <cert-oop11-cpp>
+   cppcoreguidelines-interfaces-global-init
+   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
+   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
+   google-readability-redundant-smartptr-get
+   google-readability-todo
+   google-runtime-int
+   google-runtime-member-string-references
+   google-runtime-memset
+   google-runtime-operator
+   google-runtime-references
+   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-inaccurate-erase
+   misc-incorrect-roundings
+   misc-inefficient-algorithm
+   misc-macro-parentheses
+   misc-macro-repeated-side-effects
+   misc-misplaced-const
+   misc-misplaced-widening-cast
+   misc-move-const-arg
+   misc-move-constructor-init
+   misc-multiple-statement-macro
+   misc-new-delete-overloads
+   misc-noexcept-move-constructor
+   misc-non-copyable-objects
+   misc-pointer-and-integral-operation
+   misc-redundant-expression
+   misc-sizeof-container
+   misc-sizeof-expression
+   misc-static-assert
+   misc-string-constructor
+   misc-string-integer-assignment
+   misc-string-literal-with-embedded-nul
+   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-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-shrink-to-fit
+   modernize-use-auto
+   modernize-use-bool-literals
+   modernize-use-default
+   modernize-use-emplace
+   modernize-use-nullptr
+   modernize-use-override
+   modernize-use-using
+   performance-faster-string-find
+   performance-for-range-copy
+   performance-implicit-cast-in-loop
+   performance-unnecessary-copy-initialization
+   performance-unnecessary-value-param
+   readability-avoid-const-params-in-decls
+   readability-braces-around-statements
+   readability-container-size-empty
+   readability-deleted-default
+   readability-else-after-return
+   readability-function-size
+   readability-identifier-naming
+   readability-implicit-bool-cast
+   readability-inconsistent-declaration-parameter-name
+   readability-named-parameter
+   readability-redundant-control-flow
+   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..1643dfa
--- /dev/null
@@ -0,0 +1,6 @@
+.. title:: clang-tidy - llvm-header-guard
+
+llvm-header-guard
+=================
+
+TODO: add docs
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..72b3d2e
--- /dev/null
@@ -0,0 +1,11 @@
+.. title:: clang-tidy - llvm-namespace-comment
+
+llvm-namespace-comment
+======================
+
+
+Checks that long namespaces have a closing comment.
+
+http://llvm.org/docs/CodingStandards.html#namespace-indentation
+
+https://google.github.io/styleguide/cppguide.html#Namespaces
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..5ce0302
--- /dev/null
@@ -0,0 +1,8 @@
+.. 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.
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..872d478
--- /dev/null
@@ -0,0 +1,20 @@
+.. 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:: 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.
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..a4d84bb
--- /dev/null
@@ -0,0 +1,18 @@
+.. 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.
+
+There are two 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..aa1bc20
--- /dev/null
@@ -0,0 +1,17 @@
+.. 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:: 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..03c04e3
--- /dev/null
@@ -0,0 +1,34 @@
+.. title:: clang-tidy - misc-dangling-handle
+
+misc-dangling-handle
+====================
+
+Detect dangling references in value handlers like
+``std::experimental::string_view``.
+These dangling references can come from constructing handles from temporary
+values, where the temporary is destroyed soon after the handle is created.
+
+By default only ``std::experimental::basic_string_view`` is considered.
+This list can be modified by passing a `;` separated list of class names using
+the HandleClasses option.
+
+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;
+  }
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..cb36ab1
--- /dev/null
@@ -0,0 +1,74 @@
+.. 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:: 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() {}
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..6b79789
--- /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:: 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:: 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..7db873e
--- /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:: 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-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..c61db68
--- /dev/null
@@ -0,0 +1,11 @@
+.. 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.
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..a0471e2
--- /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:: 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..824f939
--- /dev/null
@@ -0,0 +1,50 @@
+.. 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::
+
+    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::
+
+    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 (default)
+the checker also detects these cases, for instance::
+
+    long f(int x) {
+        return x * 1000;
+    }
+
+Floating point
+--------------
+
+Currently warnings are only written for integer conversion. No warning is
+written for this code::
+
+    double f(float x) {
+        return (double)(x * 10.0f);
+    }
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..9f46ad4
--- /dev/null
@@ -0,0 +1,28 @@
+.. 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,
+    or
+  - 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:: 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..cc2e6e6
--- /dev/null
@@ -0,0 +1,13 @@
+.. 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.
+
+It also flags constructor arguments that are passed by value, have a non-deleted
+move-constructor and are assigned to a class field by copy construction.
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..b2b4760
--- /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:: 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-pointer-and-integral-operation.rst b/docs/clang-tidy/checks/misc-pointer-and-integral-operation.rst
new file mode 100644 (file)
index 0000000..d32d824
--- /dev/null
@@ -0,0 +1,24 @@
+.. title:: clang-tidy - misc-pointer-and-integral-operation
+
+misc-pointer-and-integral-operation
+===================================
+
+Looks for operation involving pointers and integral types. A common mistake is
+to forget to dereference a pointer. These errors may be detected when a pointer
+object is compare to an object with integral type.
+
+Examples:
+
+.. code:: c++
+
+  char* ptr;
+  if ((ptr = malloc(...)) < nullptr)   // Pointer comparison with operator '<'
+    ...                                // Should probably be '!='
+
+  if (ptr == '\0')   // Should probably be *ptr
+    ... 
+
+  void Process(std::string path, bool* error) {
+    [...]
+    if (error != false)  // Should probably be *error
+      ...
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..c3b1eb8
--- /dev/null
@@ -0,0 +1,21 @@
+.. 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:: 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..1d62abf
--- /dev/null
@@ -0,0 +1,27 @@
+.. 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:: 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..4b92ae7
--- /dev/null
@@ -0,0 +1,137 @@
+.. 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:: 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:: 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:: 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:: 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:: 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:: 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:: 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:: 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:: c++
+
+  #define INT_SZ sizeof(int)
+  int buf[] = { 42 };
+  void getInt(int* dst) {
+    memcpy(dst, buf, sizeof(INT_SZ));  // sizeof(sizeof(int)) is suspicious.
+  }
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-constructor.rst b/docs/clang-tidy/checks/misc-string-constructor.rst
new file mode 100644 (file)
index 0000000..694b742
--- /dev/null
@@ -0,0 +1,35 @@
+.. 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:: 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:: 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:: c++
+
+  std::string("test", 0);   // Creation of an empty string.
+
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..285702e
--- /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:: c++
+
+  basic_string& operator=( CharT ch );
+
+Numeric types can be implicitly casted to character types.
+
+.. code:: c++
+
+  std::string s;
+  int x = 5965;
+  s = 6;
+  s = x;
+
+Use the appropriate conversion functions or character literals.
+
+.. code:: 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:: 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..3661218
--- /dev/null
@@ -0,0 +1,38 @@
+.. title:: clang-tidy - misc-string-literal-with-embedded-nul
+
+misc-string-literal-with-embedded-nul
+=====================================
+
+Finds occurences 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:: 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:: 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-missing-comma.rst b/docs/clang-tidy/checks/misc-suspicious-missing-comma.rst
new file mode 100644 (file)
index 0000000..5f67f08
--- /dev/null
@@ -0,0 +1,44 @@
+.. 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:: 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:: 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 checker may warn incorrectly on cases like:
+
+.. code:: c++
+
+  const char* SupportedFormat[] = {
+    "Error %s",
+    "Code " PRIu64,   // May warn here.
+    "Warning %s",
+  };
+
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..8a5711f
--- /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, becasue 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..c634be7
--- /dev/null
@@ -0,0 +1,40 @@
+.. 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:: 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:: 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:: c++
+
+    if (strcmp(...) < 0.)  // Incorrect usage of the returned value.
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..b0fe40d
--- /dev/null
@@ -0,0 +1,15 @@
+.. title:: clang-tidy - misc-throw-by-value-catch-by-reference
+
+misc-throw-by-value-catch-by-reference
+======================================
+
+"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. This check also has the option to find violations of the rule "Throw anonymous temporaries" (https://www.securecoding.cert.org/confluence/display/cplusplus/ERR09-CPP.+Throw+anonymous+temporaries). The option is named :option:`CheckThrowTemporaries` and it's on by default.
+
+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.
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..2d4553e
--- /dev/null
@@ -0,0 +1,17 @@
+.. title:: clang-tidy - misc-uniqueptr-reset-release
+
+misc-uniqueptr-reset-release
+============================
+
+
+Find and replace ``unique_ptr::reset(release())`` with ``std::move()``.
+
+Example:
+
+.. code:: 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..4e03735
--- /dev/null
@@ -0,0 +1,7 @@
+.. title:: clang-tidy - misc-unused-parameters
+
+misc-unused-parameters
+======================
+
+Finds unused parameters and fixes them, so that `-Wunused-parameter` can be
+turned on.
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..112073d
--- /dev/null
@@ -0,0 +1,28 @@
+.. 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:: 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..8296dc3
--- /dev/null
@@ -0,0 +1,14 @@
+.. title:: clang-tidy - misc-unused-using-decls
+
+misc-unused-using-decls
+=======================
+
+Finds unused ``using`` declarations.
+
+Example:
+
+.. code:: c++
+
+  namespace n { class C; }
+  using n::C;  // Never actually used.
+
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..86b91ae
--- /dev/null
@@ -0,0 +1,38 @@
+.. 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:: c++
+
+  int add(int x, int y) { return x + y; }
+
+Then:
+
+.. code:: c++
+
+  void f() {
+    int x = 2;
+    auto clj = std::bind(add, x, _1);
+  }
+
+is replaced by:
+
+.. code:: 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..4f2b683
--- /dev/null
@@ -0,0 +1,45 @@
+.. 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. For more details refer to the C++ 14 Standard [depr.c.headers]
+section.
+
+This check replaces C standard library headers with their C++ alternatives.
+
+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>`
+* `<iso646.h>`
+* `<limits.h>`
+* `<locale.h>`
+* `<math.h>`
+* `<setjmp.h>`
+* `<signal.h>`
+* `<stdalign.h>` // deprecated since C++11
+* `<stdarg.h>`
+* `<stdbool.h>`  // deprecated since C++11
+* `<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 list.
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..e7c690e
--- /dev/null
@@ -0,0 +1,16 @@
+.. 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);
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..dcb873f
--- /dev/null
@@ -0,0 +1,16 @@
+.. 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);
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..d045c3a
--- /dev/null
@@ -0,0 +1,153 @@
+.. 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: http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/
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..2b694bb
--- /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..975c884
--- /dev/null
@@ -0,0 +1,72 @@
+.. 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.
+
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-use-auto.rst b/docs/clang-tidy/checks/modernize-use-auto.rst
new file mode 100644 (file)
index 0000000..8a31c90
--- /dev/null
@@ -0,0 +1,169 @@
+.. 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 has to be 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;
+
+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.
+
+RemoveStars option
+------------------
+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..d547af5
--- /dev/null
@@ -0,0 +1,18 @@
+.. 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);
+
+  // transforms to
+
+  bool p = true;
+  bool f = true;
+  std::ios_base::sync_with_stdio(false);
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..157a337
--- /dev/null
@@ -0,0 +1,29 @@
+.. title:: clang-tidy - modernize-use-default
+
+modernize-use-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::
+  Copy-constructor, copy-assignment operator, move-constructor and
+  move-assignment operator are not supported yet.
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..1622dd6
--- /dev/null
@@ -0,0 +1,90 @@
+.. 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 by passing a semicolon-separated list of class names
+using the `ContainersWithPushBack` option.
+
+Before:
+
+.. code:: 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:: c++
+
+       std::vector<MyClass> v;
+       v.emplace_back(21, 37);
+
+       std::vector<std::pair<int,int>> w;
+       w.emplace_back(21, 37);
+       // This will be fixed to w.push_back(21, 37); in next version
+       w.emplace_back(std::make_pair(21L, 37L);
+
+The other situation is when we pass arguments that will be converted to a type
+inside a container.
+
+Before:
+
+.. code:: c++
+
+       std::vector<boost::optional<std::string> > v;
+       v.push_back("abc");
+
+After:
+
+.. code:: 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:: 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 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 option
+`SmartPointers` similar to `ContainersWithPushBack`.
+
+
+Check also fires if any argument of constructor call would be:
+- bitfield (bitfields can't bind to rvalue/universal reference)
+- ``new`` expression (to avoid leak)
+or if the argument would be converted via derived-to-base cast.
+
+This check requires C++11 of higher to run.
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..3449eb4
--- /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;
+  }
+
+
+User defined macros
+-------------------
+
+By default this check will only replace the ``NULL`` macro and will skip any
+user-defined macros that behaves like ``NULL``. The user can use the
+:option:`UserNullMacros` option to specify a comma-separated list of macro
+names that will be transformed along with ``NULL``.
+
+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:`UserNullMacros` 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-using.rst b/docs/clang-tidy/checks/modernize-use-using.rst
new file mode 100644 (file)
index 0000000..72d88de
--- /dev/null
@@ -0,0 +1,26 @@
+.. title:: clang-tidy - modernize-use-using
+
+modernize-use-using
+===================
+
+Use C++11's ``using`` instead of ``typedef``.
+
+Before:
+
+.. code:: c++
+
+  typedef int variable;
+
+  class Class{};
+  typedef void (Class::* MyPtrType)() const;
+
+After:
+
+.. code:: 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/performance-faster-string-find.rst b/docs/clang-tidy/checks/performance-faster-string-find.rst
new file mode 100644 (file)
index 0000000..018fa91
--- /dev/null
@@ -0,0 +1,22 @@
+.. 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.
+
+By default only ``std::basic_string`` is considered. This list can be modified by
+passing a `;` separated list of class names using the `StringLikeClasses`
+option. The methods to consired are fixed, though.
+
+Examples:
+
+.. code-block:: c++
+
+  str.find("A");
+
+  // becomes
+
+  str.find('A');
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..d61df3f
--- /dev/null
@@ -0,0 +1,19 @@
+.. 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.
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..0734442
--- /dev/null
@@ -0,0 +1,22 @@
+.. 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:: 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-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..8f27550
--- /dev/null
@@ -0,0 +1,55 @@
+.. 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);
+  }
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..e9ece65
--- /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.  For example:
+
+Examples:
+
+.. code:: 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..a60a531
--- /dev/null
@@ -0,0 +1,33 @@
+.. 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``, ``range-for``,
+``do-while``, and ``while``) are inside braces
+
+Before:
+
+.. code:: c++
+
+  if (condition)
+    statement;
+
+After:
+
+.. code:: c++
+
+  if (condition) {
+    statement;
+  }
+
+Additionally, one can define an option :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).
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..40a878a
--- /dev/null
@@ -0,0 +1,16 @@
+.. 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.
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..2fb87cb
--- /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:: 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..a8e4c46
--- /dev/null
@@ -0,0 +1,9 @@
+.. title:: clang-tidy - readability-else-after-return
+
+readability-else-after-return
+=============================
+
+
+Flags the usages of ``else`` after ``return``.
+
+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..cab1398
--- /dev/null
@@ -0,0 +1,18 @@
+.. 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.
+
+These options are supported:
+
+  * :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).
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..bdd4d7d
--- /dev/null
@@ -0,0 +1,99 @@
+.. 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:: 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, FixIt hints are provided to help solve
+the reported issues. This can be used for improving readability of code, for example:
+
+.. code:: 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 FixIt 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.
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..aa0246c
--- /dev/null
@@ -0,0 +1,45 @@
+.. title:: clang-tidy - readability-inconsistent-declaration-parameter-name
+
+readability-inconsistent-declaration-parameter-name
+===================================================
+
+
+Find function declarations which differ in parameter names.
+
+Example:
+
+.. code:: 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:: c++
+
+   void foo(int a);
+   void foo(int); // no warning
+
+To help with refactoring, in some cases FixIt 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:: c++
+
+   void foo(int a); // warning and FixIt 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-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-redundant-control-flow.rst b/docs/clang-tidy/checks/readability-redundant-control-flow.rst
new file mode 100644 (file)
index 0000000..64f561c
--- /dev/null
@@ -0,0 +1,51 @@
+.. 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:: c++
+
+  extern void g();
+  void f() {
+    g();
+    return;
+  }
+
+becomes
+
+.. code:: c++
+
+  extern void g();
+  void f() {
+    g();
+  }
+
+The following function `k` contains a redundant `continue` statement:
+
+.. code:: c++
+
+  void k() {
+    for (int i = 0; i < 10; ++i) {
+      continue;
+    }
+  }
+
+becomes
+
+.. code:: c++
+
+  void k() {
+    for (int i = 0; i < 10; ++i) {
+    }
+  }
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..7a32d42
--- /dev/null
@@ -0,0 +1,16 @@
+.. title:: clang-tidy - readability-redundant-smartptr-get
+
+readability-redundant-smartptr-get
+==================================
+
+
+Find and remove redundant calls to smart pointer's ``.get()`` method.
+
+Examples:
+
+.. code:: 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..4614c53
--- /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()``.
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..e127109
--- /dev/null
@@ -0,0 +1,15 @@
+.. title:: clang-tidy - readability-redundant-string-init
+
+readability-redundant-string-init
+=================================
+
+
+Finds unnecessary string initializations.
+
+Examples:
+
+.. code:: c++
+
+  // Initializing string with empty string literal is unnecessary.
+  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..7e1f655
--- /dev/null
@@ -0,0 +1,81 @@
+.. 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);``
+
+When a conditional boolean return or assignment appears at the end of a
+chain of ``if``, ``else if`` statements, the conditional statement is left
+unchanged unless the option ``ChainedConditionalReturn`` or
+``ChainedConditionalAssignment``, respectively, is specified as non-zero.
+The default value for both options is zero.
+
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..5e5d7fb
--- /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:: 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..8312235
--- /dev/null
@@ -0,0 +1,7 @@
+.. 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.
diff --git a/docs/clang-tidy/index.rst b/docs/clang-tidy/index.rst
new file mode 100644 (file)
index 0000000..3fc0d35
--- /dev/null
@@ -0,0 +1,614 @@
+==========
+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-alpha*
+
+will disable all default checks (``-*``) and enable all ``clang-analyzer-*``
+checks except for ``clang-analyzer-alpha*`` 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.
+
+There are currently the following groups of checks:
+
+* Checks related to the LLVM coding conventions have names starting with
+  ``llvm-``.
+
+* Checks related to the Google coding conventions have names starting with
+  ``google-``.
+
+* Checks named ``modernize-*`` advocate the usage of modern (currently "modern"
+  means "C++11") language constructs.
+
+* The ``readability-`` checks target readability-related issues that don't
+  relate to any particular coding style.
+
+* Checks with names starting with ``misc-`` the checks that we didn't have a
+  better category for.
+
+* Clang static analyzer checks are named starting with ``clang-analyzer-``.
+
+* Checks related to Boost library starts with ``boost-``. 
+  
+Clang diagnostics are treated in a similar way as check diagnostics. Clang
+diagnostics are displayed by 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-diagostic-<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.
+    -export-fixes=<filename>     - 
+                                   YAML file to store suggested fixes in. The
+                                   stored fixes can be applied to the input sorce
+                                   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.
+    -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
+    -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
+      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 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 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 suggest, renames an existsing
+    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. If the check
+verifies conformance of the code to a certain coding style, it probably deserves
+a separate module and a directory in ``clang-tidy/``. There are already modules
+implementing checks related to:
+
+* `C++ Core Guidelines
+  <http://reviews.llvm.org/diffusion/L/browse/clang-tools-extra/trunk/clang-tidy/cppcoreguidelines/>`_
+* `CERT Secure Coding Standards
+  <http://reviews.llvm.org/diffusion/L/browse/clang-tools-extra/trunk/clang-tidy/cert/>`_
+* `Google Style Guide
+  <http://reviews.llvm.org/diffusion/L/browse/clang-tools-extra/trunk/clang-tidy/google/>`_
+* `LLVM Style
+  <http://reviews.llvm.org/diffusion/L/browse/clang-tools-extra/trunk/clang-tidy/llvm/>`_
+* `modernizing C/C++ code
+  <http://reviews.llvm.org/diffusion/L/browse/clang-tools-extra/trunk/clang-tidy/modernize/>`_
+* potential `performance problems
+  <http://reviews.llvm.org/diffusion/L/browse/clang-tools-extra/trunk/clang-tidy/performance/>`_
+* various `readability issues
+  <http://reviews.llvm.org/diffusion/L/browse/clang-tools-extra/trunk/clang-tidy/readability/>`_
+* and `miscellaneous checks
+  <http://reviews.llvm.org/diffusion/L/browse/clang-tools-extra/trunk/clang-tidy/misc/>`_
+  that we couldn't find a better category for.
+
+After choosing the module, you need to create a class for your check:
+
+.. code-block:: c++
+
+  #include "../ClangTidy.h"
+
+  namespace clang {
+  namespace tidy {
+  namespace some_module {
+
+  class MyCheck : public ClangTidyCheck {
+  public:
+    MyCheck(StringRef Name, ClangTidyContext *Context)
+        : ClangTidyCheck(Name, Context) {}
+  };
+
+  } // namespace some_module
+  } // namespace tidy
+  } // namespace clang
+
+Constructor of the check receives the ``Name`` and ``Context`` parameters, and
+must forward them to the ``ClangTidyCheck`` constructor.
+
+Next, you need to decide whether it should operate on the preprocessor level or
+on the AST level. Let's imagine that we need to work with the AST in our check.
+In this case we need to override two methods:
+
+.. code-block:: c++
+
+  ...
+  class ExplicitConstructorCheck : public ClangTidyCheck {
+  public:
+    ExplicitConstructorCheck(StringRef Name, ClangTidyContext *Context)
+        : ClangTidyCheck(Name, Context) {}
+    void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+    void check(ast_matchers::MatchFinder::MatchResult &Result) override;
+  };
+
+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 ExplicitConstructorCheck::registerMatchers(MatchFinder *Finder) {
+    Finder->addMatcher(constructorDecl().bind("ctor"), this);
+  }
+
+  void ExplicitConstructorCheck::check(const MatchFinder::MatchResult &Result) {
+    const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructorDecl>("ctor");
+    // Do not be confused: isExplicit means 'explicit' keyword is present,
+    // isImplicit means that it's a compiler-generated constructor.
+    if (Ctor->isOutOfLine() || Ctor->isExplicit() || Ctor->isImplicit())
+      return;
+    if (Ctor->getNumParams() == 0 || Ctor->getMinRequiredArguments() > 1)
+      return;
+    SourceLocation Loc = Ctor->getLocation();
+    diag(Loc, "single-argument constructors must be explicit")
+        << FixItHint::CreateInsertion(Loc, "explicit ");
+  }
+
+(The full code for this check resides in
+`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 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 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 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:: bash
+
+  $ clang-tidy -config="{CheckOptions: [{key: a, value: b}, {key: x, value: y}]}" ...
+
+
+Testing Checks
+--------------
+
+: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. To use the 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:: bash
+
+  // 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 fixit 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 multipe times; 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
+  checkers. 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/clang-tidy/tools/dump_check_docs.py b/docs/clang-tidy/tools/dump_check_docs.py
new file mode 100755 (executable)
index 0000000..a45d15a
--- /dev/null
@@ -0,0 +1,79 @@
+#!/usr/bin/env python
+
+r"""
+Create stubs for check documentation files.
+"""
+
+import os
+import re
+import sys
+
+def main():
+  clang_tidy_dir = os.path.normpath(
+      os.path.join(os.path.dirname(sys.argv[0]), '..', '..', '..',
+                   'clang-tidy'))
+
+  checks_doc_dir = os.path.normpath(
+      os.path.join(clang_tidy_dir, '..', 'docs', 'clang-tidy', 'checks'))
+
+  registered_checks = {}
+  defined_checks = {}
+
+  for dir_name, subdir_list, file_list in os.walk(clang_tidy_dir):
+    print('Processing directory ' + dir_name + '...')
+    for file_name in file_list:
+      full_name = os.path.join(dir_name, file_name)
+      if file_name.endswith('Module.cpp'):
+        print('Module ' + file_name)
+        with open(full_name, 'r') as f:
+          text = f.read()
+        for class_name, check_name in re.findall(
+            r'\.\s*registerCheck\s*<\s*([A-Za-z0-9:]+)\s*>\(\s*"([a-z0-9-]+)"',
+            text):
+          registered_checks[check_name] = class_name
+      elif file_name.endswith('.h'):
+        print('    ' + file_name + '...')
+        with open(full_name, 'r') as f:
+          text = f.read()
+        for comment, _, _, class_name in re.findall(
+            r'((([\r\n]//)[^\r\n]*)*)\s+class (\w+)\s*:' +
+            '\s*public\s+ClangTidyCheck\s*\{', text):
+          defined_checks[class_name] = comment
+
+  print('Registered checks [%s]: [%s]' %
+        (len(registered_checks), registered_checks))
+  print('Check implementations: %s' % len(defined_checks))
+
+  checks = registered_checks.keys()
+  checks.sort()
+
+  for check_name in checks:
+    doc_file_name = os.path.join(checks_doc_dir, check_name + '.rst')
+    #if os.path.exists(doc_file_name):
+    #  print('Skipping existing file %s...')
+    #  continue
+    print('Updating %s...' % doc_file_name)
+    with open(doc_file_name, 'w') as f:
+      class_name = re.sub(r'.*:', '', registered_checks[check_name])
+      f.write(check_name + '\n' + ('=' * len(check_name)) + '\n\n')
+      if class_name in defined_checks:
+        text = defined_checks[class_name]
+        text = re.sub(r'\n//+ ?(\\brief )?', r'\n', text)
+        text = re.sub(r'(\n *)\\code\n', r'\1.. code:: c++\n\n', text)
+        text = re.sub(r'(\n *)\\endcode(\n|$)', r'\n', text)
+        text = re.sub(r'`', r'``', text)
+        f.write(text + '\n')
+      else:
+        f.write('TODO: add docs\n')
+
+  with open(os.path.join(checks_doc_dir, 'list.rst'), 'w') as f:
+    f.write(
+r"""List of clang-tidy Checks
+=========================
+
+.. toctree::
+   """ + '\n   '.join(checks))
+
+
+if __name__ == '__main__':
+  main()
diff --git a/docs/conf.py b/docs/conf.py
new file mode 100644 (file)
index 0000000..97ac320
--- /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 X.Y version.
+version = '3.9'
+# The full version, including alpha/beta/rc tags.
+release = '3.9'
+
+# 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..56e96a6
--- /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        = 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.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_ONLY_PREDEF     = NO
+
+# 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             =
+
+# 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 refrences 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..ea24240
--- /dev/null
@@ -0,0 +1,95 @@
+===================
+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.
+
+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
+
+  map ,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 ``,cf`` to
+another binding if you need clang-include-fixer on a different key.
+
+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``
+
+See ``clang-include-fixer.py`` for more details.
+
+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..f9dddb1
--- /dev/null
@@ -0,0 +1,34 @@
+.. 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
+
+
+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..9d6170a
--- /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..db8743a
--- /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..a8c18dc
--- /dev/null
@@ -0,0 +1,26 @@
+set(LLVM_LINK_COMPONENTS
+  support
+  )
+
+add_clang_library(clangIncludeFixer
+  IncludeFixer.cpp
+  IncludeFixerContext.cpp
+  InMemorySymbolIndex.cpp
+  SymbolIndexManager.cpp
+  YamlSymbolIndex.cpp
+
+  LINK_LIBS
+  clangAST
+  clangBasic
+  clangFormat
+  clangFrontend
+  clangLex
+  clangParse
+  clangSema
+  clangTooling
+  clangToolingCore
+  findAllSymbols
+  )
+
+add_subdirectory(tool)
+add_subdirectory(find-all-symbols)
diff --git a/include-fixer/InMemorySymbolIndex.cpp b/include-fixer/InMemorySymbolIndex.cpp
new file mode 100644 (file)
index 0000000..7f85174
--- /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::SymbolInfo;
+
+namespace clang {
+namespace include_fixer {
+
+InMemorySymbolIndex::InMemorySymbolIndex(
+    const std::vector<SymbolInfo> &Symbols) {
+  for (const auto &Symbol : Symbols)
+    LookupTable[Symbol.getName()].push_back(Symbol);
+}
+
+std::vector<SymbolInfo>
+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..cddb57e
--- /dev/null
@@ -0,0 +1,37 @@
+//===-- 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::SymbolInfo> &Symbols);
+
+  std::vector<clang::find_all_symbols::SymbolInfo>
+  search(llvm::StringRef Identifier) override;
+
+private:
+  std::map<std::string, std::vector<clang::find_all_symbols::SymbolInfo>>
+      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..2426561
--- /dev/null
@@ -0,0 +1,370 @@
+//===-- 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/ExternalSemaSource.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 clang::ExternalSemaSource {
+public:
+  explicit Action(SymbolIndexManager &SymbolIndexMgr, bool MinimizeIncludePaths)
+      : SymbolIndexMgr(SymbolIndexMgr),
+        MinimizeIncludePaths(MinimizeIncludePaths) {}
+
+  std::unique_ptr<clang::ASTConsumer>
+  CreateASTConsumer(clang::CompilerInstance &Compiler,
+                    StringRef InFile) override {
+    Filename = 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);
+    Compiler->getSema().addExternalSource(this);
+
+    clang::ParseAST(Compiler->getSema(), Compiler->getFrontendOpts().ShowStats,
+                    Compiler->getFrontendOpts().SkipFunctionBodies);
+  }
+
+  /// 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 {
+    // Ignore spurious callbacks from SFINAE contexts.
+    if (getCompilerInstance().getSema().isSFINAEContext())
+      return false;
+
+    clang::ASTContext &context = getCompilerInstance().getASTContext();
+    std::string QueryString =
+        T.getUnqualifiedType().getAsString(context.getPrintingPolicy());
+    DEBUG(llvm::dbgs() << "Query missing complete type '" << QueryString
+                       << "'");
+    query(QueryString, "", tooling::Range());
+    return false;
+  }
+
+  /// 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 {
+    // Ignore spurious callbacks from SFINAE contexts.
+    if (getCompilerInstance().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 (!getCompilerInstance().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, getCompilerInstance().getSourceManager(),
+                               getCompilerInstance().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 = getCompilerInstance().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");
+    query(QueryString, TypoScopeString, SymbolRange);
+
+    // FIXME: We should just return the name we got as input here and prevent
+    // clang from trying to correct the typo by itself. That may change the
+    // identifier to something that's not wanted by the user.
+    return clang::TypoCorrection();
+  }
+
+  StringRef filename() const { return Filename; }
+
+  /// Get the minimal include for a given path.
+  std::string minimizeInclude(StringRef Include,
+                              const clang::SourceManager &SourceManager,
+                              clang::HeaderSearch &HeaderSearch) {
+    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
+  getIncludeFixerContext(const clang::SourceManager &SourceManager,
+                         clang::HeaderSearch &HeaderSearch) {
+    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.getLineNumber(),
+                                    Symbol.getContexts(),
+                                    Symbol.getNumOccurrences());
+    }
+    return IncludeFixerContext(QuerySymbol, SymbolScopedQualifiers,
+                               SymbolCandidates, QuerySymbolRange);
+  }
+
+private:
+  /// Query the database for a given identifier.
+  bool query(StringRef Query, StringRef ScopedQualifiers, tooling::Range Range) {
+    assert(!Query.empty() && "Empty query!");
+
+    // Skip other identifiers once we have discovered an identfier successfully.
+    if (!MatchedSymbols.empty())
+      return false;
+
+    DEBUG(llvm::dbgs() << "Looking up '" << Query << "' at ");
+    DEBUG(getCompilerInstance()
+              .getSourceManager()
+              .getLocForStartOfFile(
+                  getCompilerInstance().getSourceManager().getMainFileID())
+              .getLocWithOffset(Range.getOffset())
+              .print(llvm::dbgs(), getCompilerInstance().getSourceManager()));
+    DEBUG(llvm::dbgs() << " ...");
+
+    QuerySymbol = Query.str();
+    QuerySymbolRange = Range;
+    SymbolScopedQualifiers = ScopedQualifiers;
+
+    // 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();
+    MatchedSymbols = SymbolIndexMgr.search(QueryString);
+    if (MatchedSymbols.empty() && !ScopedQualifiers.empty())
+      MatchedSymbols = SymbolIndexMgr.search(Query);
+    DEBUG(llvm::dbgs() << "Having found " << MatchedSymbols.size()
+                       << " symbols\n");
+    return !MatchedSymbols.empty();
+  }
+
+  /// The client to use to find cross-references.
+  SymbolIndexManager &SymbolIndexMgr;
+
+  /// The absolute path to the file being processed.
+  std::string Filename;
+
+  /// The symbol being queried.
+  std::string QuerySymbol;
+
+  /// The scoped qualifiers of QuerySymbol. It is represented as a sequence of
+  /// names and scope resolution operatiors ::, ending with a scope resolution
+  /// operator (e.g. a::b::). Empty if the symbol is not in a specific scope.
+  std::string SymbolScopedQualifiers;
+
+  /// The replacement range of the first discovered QuerySymbol.
+  tooling::Range QuerySymbolRange;
+
+  /// 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;
+
+  /// Whether we should use the smallest possible include path.
+  bool MinimizeIncludePaths = true;
+};
+
+} // namespace
+
+IncludeFixerActionFactory::IncludeFixerActionFactory(
+    SymbolIndexManager &SymbolIndexMgr, IncludeFixerContext &Context,
+    StringRef StyleName, bool MinimizeIncludePaths)
+    : SymbolIndexMgr(SymbolIndexMgr), Context(Context),
+      MinimizeIncludePaths(MinimizeIncludePaths) {}
+
+IncludeFixerActionFactory::~IncludeFixerActionFactory() = default;
+
+bool IncludeFixerActionFactory::runInvocation(
+    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(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);
+
+  Context = 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();
+}
+
+llvm::Expected<tooling::Replacements>
+createInsertHeaderReplacements(StringRef Code, StringRef FilePath,
+                               StringRef Header,
+                               const clang::format::FormatStyle &Style) {
+  if (Header.empty())
+    return tooling::Replacements();
+  std::string IncludeName = "#include " + Header.str() + "\n";
+  // Create replacements for the new header.
+  clang::tooling::Replacements Insertions = {
+      tooling::Replacement(FilePath, UINT_MAX, 0, IncludeName)};
+
+  auto CleanReplaces = cleanupAroundReplacements(Code, Insertions, Style);
+  if (!CleanReplaces)
+    return CleanReplaces;
+  return formatReplacements(Code, *CleanReplaces, 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..e1cb52c
--- /dev/null
@@ -0,0 +1,80 @@
+//===-- 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/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 Context A context for the symbol being queried.
+  /// \param StyleName Fallback style for reformatting.
+  /// \param MinimizeIncludePaths whether inserted include paths are optimized.
+  IncludeFixerActionFactory(SymbolIndexManager &SymbolIndexMgr,
+                            IncludeFixerContext &Context, StringRef StyleName,
+                            bool MinimizeIncludePaths = true);
+
+  ~IncludeFixerActionFactory() override;
+
+  bool
+  runInvocation(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;
+
+  /// The context that contains all information about the symbol being queried.
+  IncludeFixerContext &Context;
+
+  /// 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 header
+/// insertion.
+///
+/// \param Code The source code.
+/// \param FilePath The source file path.
+/// \param Header The header being inserted.
+/// \param Style clang-format style being used.
+///
+/// \return Replacements for inserting and sorting headers on success;
+/// otherwise, an llvm::Error carrying llvm::StringError is returned.
+llvm::Expected<tooling::Replacements> createInsertHeaderReplacements(
+    StringRef Code, StringRef FilePath, StringRef Header,
+    const clang::format::FormatStyle &Style = clang::format::getLLVMStyle());
+
+} // 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..d6fbdb5
--- /dev/null
@@ -0,0 +1,98 @@
+//===-- 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();
+    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(
+    llvm::StringRef Name, llvm::StringRef ScopeQualifiers,
+    std::vector<find_all_symbols::SymbolInfo> Symbols,
+    tooling::Range Range)
+    : SymbolIdentifier(Name), SymbolScopedQualifiers(ScopeQualifiers),
+      MatchedSymbols(std::move(Symbols)), SymbolRange(Range) {
+  for (const auto &Symbol : MatchedSymbols) {
+    HeaderInfos.push_back({Symbol.getFilePath().str(),
+                           createQualifiedNameForReplacement(
+                               SymbolIdentifier, ScopeQualifiers, 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..f5d3e91
--- /dev/null
@@ -0,0 +1,72 @@
+//===-- 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 the symbol being queried.
+class IncludeFixerContext {
+public:
+  IncludeFixerContext() = default;
+  IncludeFixerContext(llvm::StringRef Name, llvm::StringRef ScopeQualifiers,
+                      const std::vector<find_all_symbols::SymbolInfo> Symbols,
+                      tooling::Range Range);
+
+  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;
+  };
+
+  /// \brief Get symbol name.
+  llvm::StringRef getSymbolIdentifier() const { return SymbolIdentifier; }
+
+  /// \brief Get replacement range of the symbol.
+  tooling::Range getSymbolRange() const { return SymbolRange; }
+
+  const std::vector<HeaderInfo> &getHeaderInfos() const { return HeaderInfos; }
+
+private:
+  friend struct llvm::yaml::MappingTraits<IncludeFixerContext>;
+
+  /// \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 SymbolIdentifier;
+
+  /// \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 SymbolScopedQualifiers;
+
+  /// \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 replacement range of SymbolIdentifier.
+  tooling::Range SymbolRange;
+
+  /// \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..7a84f6b
--- /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<clang::find_all_symbols::SymbolInfo>
+  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..970a612
--- /dev/null
@@ -0,0 +1,126 @@
+//===-- 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"
+
+#define DEBUG_TYPE "include-fixer"
+
+namespace clang {
+namespace include_fixer {
+
+using clang::find_all_symbols::SymbolInfo;
+
+/// Sorts SymbolInfos based on the popularity info in SymbolInfo.
+static void rankByPopularity(std::vector<SymbolInfo> &Symbols) {
+  // First collect occurrences per header file.
+  llvm::DenseMap<llvm::StringRef, unsigned> HeaderPopularity;
+  for (const SymbolInfo &Symbol : Symbols) {
+    unsigned &Popularity = HeaderPopularity[Symbol.getFilePath()];
+    Popularity = std::max(Popularity, Symbol.getNumOccurrences());
+  }
+
+  // Sort by the gathered popularities. Use file name as a tie breaker so we can
+  // deduplicate.
+  std::sort(Symbols.begin(), Symbols.end(),
+            [&](const SymbolInfo &A, const SymbolInfo &B) {
+              auto APop = HeaderPopularity[A.getFilePath()];
+              auto BPop = HeaderPopularity[B.getFilePath()];
+              if (APop != BPop)
+                return APop > BPop;
+              return A.getFilePath() < B.getFilePath();
+            });
+}
+
+std::vector<find_all_symbols::SymbolInfo>
+SymbolIndexManager::search(llvm::StringRef Identifier) 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<clang::find_all_symbols::SymbolInfo> MatchedSymbols;
+  while (MatchedSymbols.empty() && !Names.empty()) {
+    std::vector<clang::find_all_symbols::SymbolInfo> Symbols;
+    for (const auto &DB : SymbolIndices) {
+      auto Res = DB->search(Names.back().str());
+      Symbols.insert(Symbols.end(), Res.begin(), Res.end());
+    }
+
+    DEBUG(llvm::dbgs() << "Searching " << Names.back() << "... got "
+                       << Symbols.size() << " results...\n");
+
+    for (const auto &Symbol : Symbols) {
+      // Match the identifier name without qualifier.
+      if (Symbol.getName() == Names.back()) {
+        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(Symbol);
+        }
+      }
+    }
+    Names.pop_back();
+    TookPrefix = true;
+  }
+
+  rankByPopularity(MatchedSymbols);
+  return MatchedSymbols;
+}
+
+} // namespace include_fixer
+} // namespace clang
diff --git a/include-fixer/SymbolIndexManager.h b/include-fixer/SymbolIndexManager.h
new file mode 100644 (file)
index 0000000..f886404
--- /dev/null
@@ -0,0 +1,45 @@
+//===-- 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"
+
+namespace clang {
+namespace include_fixer {
+
+/// This class provides an interface for finding the header files corresponding
+/// to an indentifier in the source code from multiple symbol databases.
+class SymbolIndexManager {
+public:
+  void addSymbolIndex(std::unique_ptr<SymbolIndex> DB) {
+    SymbolIndices.push_back(std::move(DB));
+  }
+
+  /// Search for header files to be included for an identifier.
+  /// \param Identifier The identifier being searched for. May or may not be
+  ///                   fully qualified.
+  /// \returns A list of inclusion candidates, in a format ready for being
+  ///          pasted after an #include token.
+  // FIXME: Move mapping from SymbolInfo to headers out of
+  // SymbolIndexManager::search and return SymbolInfos instead of header paths.
+  std::vector<find_all_symbols::SymbolInfo>
+  search(llvm::StringRef Identifier) const;
+
+private:
+  std::vector<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..82517af
--- /dev/null
@@ -0,0 +1,60 @@
+//===-- 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;
+
+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(clang::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<SymbolInfo> YamlSymbolIndex::search(llvm::StringRef Identifier) {
+  std::vector<SymbolInfo> Results;
+  for (const auto &Symbol : Symbols) {
+    if (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..57c04da
--- /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<clang::find_all_symbols::SymbolInfo>
+  search(llvm::StringRef Identifier) override;
+
+private:
+  explicit YamlSymbolIndex(
+      std::vector<clang::find_all_symbols::SymbolInfo> Symbols)
+      : Symbols(std::move(Symbols)) {}
+
+  std::vector<clang::find_all_symbols::SymbolInfo> 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..a9bb838
--- /dev/null
@@ -0,0 +1,37 @@
+//===-- 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/Token.h"
+#include "llvm/Support/Path.h"
+
+namespace clang {
+namespace find_all_symbols {
+
+void FindAllMacros::MacroDefined(const Token &MacroNameTok,
+                                 const MacroDirective *MD) {
+  SourceLocation Loc = SM->getExpansionLoc(MacroNameTok.getLocation());
+  std::string FilePath = getIncludePath(*SM, Loc, Collector);
+  if (FilePath.empty()) return;
+
+  SymbolInfo Symbol(MacroNameTok.getIdentifierInfo()->getName(),
+                    SymbolInfo::SymbolKind::Macro, FilePath,
+                    SM->getSpellingLineNumber(Loc), {});
+
+  Reporter->reportSymbol(SM->getFileEntryForID(SM->getMainFileID())->getName(),
+                         Symbol);
+}
+
+} // 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..41e846a
--- /dev/null
@@ -0,0 +1,47 @@
+//===-- 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 {
+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;
+
+private:
+  // 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..60c0615
--- /dev/null
@@ -0,0 +1,226 @@
+//===-- 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;
+}
+
+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,
+                    SM.getExpansionLineNumber(Loc), 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(classTemplateSpecializationDecl()),
+            unless(isExplicitTemplateSpecialization()));
+
+  // 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.
+  MatchFinder->addMatcher(varDecl(CommonFilter,
+                                  anyOf(ExternCMatcher, CCMatcher),
+                                  unless(parmVarDecl()))
+                              .bind("decl"),
+                          this);
+
+  // Matchers for C-style record declarations in extern "C" {...}.
+  MatchFinder->addMatcher(
+      recordDecl(CommonFilter, ExternCMatcher, isDefinition()).bind("decl"),
+      this);
+
+  // Matchers for C++ record declarations.
+  auto CxxRecordDecl =
+      cxxRecordDecl(CommonFilter, CCMatcher, isDefinition(),
+                    unless(isExplicitTemplateSpecialization()));
+  MatchFinder->addMatcher(CxxRecordDecl.bind("decl"), this);
+
+  // 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`.
+  MatchFinder->addMatcher(functionDecl(CommonFilter,
+                                       unless(hasParent(friendDecl())),
+                                       anyOf(ExternCMatcher, CCMatcher))
+                              .bind("decl"),
+                          this);
+
+  // Matcher for typedef and type alias declarations.
+  //
+  // typedef and type alias can come from C-style headers and C++ heaeders.
+  // For C-style header, `DeclContxet` can be either `TranslationUnitDecl`
+  // or `LinkageSpecDecl`.
+  // For C++ header, `DeclContext ` can be one of `TranslationUnitDecl`,
+  // `NamespaceDecl`.
+  // With the following context matcher, we can match `typedefNameDecl` from
+  // both C-style header and C++ header (except for those in classes).
+  // "cc_matchers" are not included since template-related matchers are not
+  // applicable on `TypedefNameDecl`.
+  MatchFinder->addMatcher(
+      typedefNameDecl(CommonFilter, anyOf(HasNSOrTUCtxMatcher,
+                                          hasDeclContext(linkageSpecDecl())))
+          .bind("decl"),
+      this);
+
+  // Matchers for enum declarations.
+  MatchFinder->addMatcher(enumDecl(CommonFilter, isDefinition(),
+                                   anyOf(HasNSOrTUCtxMatcher, ExternCMatcher))
+                              .bind("decl"),
+                          this);
+
+  // 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.
+  MatchFinder->addMatcher(
+      enumConstantDecl(
+          CommonFilter,
+          unless(isInScopedEnum()),
+          anyOf(hasDeclContext(enumDecl(HasNSOrTUCtxMatcher)), ExternCMatcher))
+          .bind("decl"),
+      this);
+}
+
+void FindAllSymbols::run(const MatchFinder::MatchResult &Result) {
+  // Ignore Results in failing TUs.
+  if (Result.Context->getDiagnostics().hasErrorOccurred()) {
+    return;
+  }
+
+  const NamedDecl *ND = Result.Nodes.getNodeAs<NamedDecl>("decl");
+  assert(ND && "Matched declaration must be a NamedDecl!");
+  const SourceManager *SM = Result.SourceManager;
+
+  llvm::Optional<SymbolInfo> Symbol =
+      CreateSymbolInfo(ND, *SM, Collector);
+  if (Symbol)
+    Reporter->reportSymbol(
+        SM->getFileEntryForID(SM->getMainFileID())->getName(), *Symbol);
+}
+
+} // 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..1d84cc4
--- /dev/null
@@ -0,0 +1,57 @@
+//===-- 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 clang::ast_matchers::MatchFinder::MatchCallback {
+public:
+  explicit FindAllSymbols(SymbolReporter *Reporter,
+                          HeaderMapCollector *Collector = nullptr)
+      : Reporter(Reporter), Collector(Collector) {}
+
+  void registerMatchers(clang::ast_matchers::MatchFinder *MatchFinder);
+
+  void
+  run(const clang::ast_matchers::MatchFinder::MatchResult &result) override;
+
+private:
+  // 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..7eac294
--- /dev/null
@@ -0,0 +1,33 @@
+//===-- 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"
+
+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<clang::ASTConsumer>
+FindAllSymbolsAction::CreateASTConsumer(clang::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..7dd7645
--- /dev/null
@@ -0,0 +1,61 @@
+//===-- 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 "FindAllMacros.h"
+#include "FindAllSymbols.h"
+#include "HeaderMapCollector.h"
+#include "PragmaCommentHandler.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendActions.h"
+#include "clang/Tooling/Tooling.h"
+
+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) {}
+
+  virtual 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..717a188
--- /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$", "<fstream>"},
+      {"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$", "<string>"},
+      {"bits/mask_array.h$", "<valarray>"},
+      {"bits/memoryfwd.h$", "<string>"},
+      {"bits/move.h$", "<utility>"},
+      {"bits/nested_exception.h$", "<exception>"},
+      {"bits/ostream.tcc$", "<ostream>"},
+      {"bits/ostream_insert.h$", "<string>"},
+      {"bits/postypes.h$", "<iosfwd>"},
+      {"bits/ptr_traits.h$", "<unordered_set>"},
+      {"bits/random.h$", "<random>"},
+      {"bits/random.tcc$", "<random>"},
+      {"bits/range_access.h$", "<string>"},
+      {"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$", "<fstream>"},
+      {"bits/c++locale.h$", "<string>"},
+      {"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..c4d248a
--- /dev/null
@@ -0,0 +1,119 @@
+//===-- 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 SymbolKind = clang::find_all_symbols::SymbolInfo::SymbolKind;
+
+LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(SymbolInfo)
+LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(std::string)
+LLVM_YAML_IS_SEQUENCE_VECTOR(SymbolInfo::Context)
+
+namespace llvm {
+namespace yaml {
+template <> struct MappingTraits<SymbolInfo> {
+  static void mapping(IO &io, SymbolInfo &Symbol) {
+    io.mapRequired("Name", Symbol.Name);
+    io.mapRequired("Contexts", Symbol.Contexts);
+    io.mapRequired("FilePath", Symbol.FilePath);
+    io.mapRequired("LineNumber", Symbol.LineNumber);
+    io.mapRequired("Type", Symbol.Type);
+    io.mapRequired("NumOccurrences", Symbol.NumOccurrences);
+  }
+};
+
+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, int LineNumber,
+                       const std::vector<Context> &Contexts,
+                       unsigned NumOccurrences)
+    : Name(Name), Type(Type), FilePath(FilePath), Contexts(Contexts),
+      LineNumber(LineNumber), NumOccurrences(NumOccurrences) {}
+
+bool SymbolInfo::operator==(const SymbolInfo &Symbol) const {
+  return std::tie(Name, Type, FilePath, LineNumber, Contexts) ==
+         std::tie(Symbol.Name, Symbol.Type, Symbol.FilePath, Symbol.LineNumber,
+                  Symbol.Contexts);
+}
+
+bool SymbolInfo::operator<(const SymbolInfo &Symbol) const {
+  return std::tie(Name, Type, FilePath, LineNumber, Contexts) <
+         std::tie(Symbol.Name, Symbol.Type, Symbol.FilePath, Symbol.LineNumber,
+                  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;
+}
+
+bool WriteSymbolInfosToStream(llvm::raw_ostream &OS,
+                              const std::set<SymbolInfo> &Symbols) {
+  llvm::yaml::Output yout(OS);
+  for (auto Symbol : Symbols)
+    yout << Symbol;
+  return true;
+}
+
+std::vector<SymbolInfo> ReadSymbolInfosFromYAML(llvm::StringRef Yaml) {
+  std::vector<SymbolInfo> 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..78ba206
--- /dev/null
@@ -0,0 +1,127 @@
+//===-- 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 Contains all information for a Symbol.
+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;
+
+  // The default constructor is required by YAML traits in
+  // LLVM_YAML_IS_DOCUMENT_LIST_VECTOR.
+  SymbolInfo() : Type(SymbolKind::Unknown), LineNumber(-1) {}
+
+  SymbolInfo(llvm::StringRef Name, SymbolKind Type, llvm::StringRef FilePath,
+             int LineNumber, const std::vector<Context> &Contexts,
+             unsigned NumOccurrences = 0);
+
+  /// \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;
+  }
+
+  /// \brief Get a 1-based line number of the symbol's declaration.
+  int getLineNumber() const { return LineNumber; }
+
+  /// \brief The number of times this symbol was found during an indexing run.
+  unsigned getNumOccurrences() const { return NumOccurrences; }
+
+  bool operator<(const SymbolInfo &Symbol) const;
+
+  bool operator==(const SymbolInfo &Symbol) const;
+
+private:
+  friend struct llvm::yaml::MappingTraits<SymbolInfo>;
+
+  /// \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;
+
+  /// \brief The 1-based line number of of the symbol's declaration.
+  int LineNumber;
+
+  /// \brief The number of times this symbol was found during an indexing
+  /// run. Populated by the reducer and used to rank results.
+  unsigned NumOccurrences;
+};
+
+/// \brief Write SymbolInfos to a stream (YAML format).
+bool WriteSymbolInfosToStream(llvm::raw_ostream &OS,
+                              const std::set<SymbolInfo> &Symbols);
+
+/// \brief Read SymbolInfos from a YAML document.
+std::vector<SymbolInfo> 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..e066326
--- /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 reportSymbol(llvm::StringRef FileName,
+                            const SymbolInfo &Symbol) = 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..28c621a
--- /dev/null
@@ -0,0 +1,162 @@
+//===-- 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 clang::find_all_symbols::SymbolReporter {
+public:
+  ~YamlReporter() override {
+    for (const auto &Symbol : Symbols) {
+      int FD;
+      SmallString<128> ResultPath;
+      llvm::sys::fs::createUniqueFile(
+          OutputDir + "/" + llvm::sys::path::filename(Symbol.first) +
+              "-%%%%%%.yaml",
+          FD, ResultPath);
+      llvm::raw_fd_ostream OS(FD, /*shouldClose=*/true);
+      WriteSymbolInfosToStream(OS, Symbol.second);
+    }
+  }
+
+  void reportSymbol(StringRef FileName, const SymbolInfo &Symbol) override {
+    Symbols[FileName].insert(Symbol);
+  }
+
+private:
+  std::map<std::string, std::set<SymbolInfo>> Symbols;
+};
+
+bool Merge(llvm::StringRef MergeDir, llvm::StringRef OutputFile) {
+  std::error_code EC;
+  std::map<SymbolInfo, int> SymbolToNumOccurrences;
+  std::mutex SymbolMutex;
+  auto AddSymbols = [&](ArrayRef<SymbolInfo> Symbols) {
+    // Synchronize set accesses.
+    std::unique_lock<std::mutex> LockGuard(SymbolMutex);
+    for (const auto &Symbol : Symbols)
+      ++SymbolToNumOccurrences[Symbol];
+  };
+
+  // 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<SymbolInfo> Symbols =
+                ReadSymbolInfosFromYAML(Buffer.get()->getBuffer());
+            // 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;
+  }
+  std::set<SymbolInfo> Result;
+  for (const auto &Entry : SymbolToNumOccurrences) {
+    const auto &Symbol = Entry.first;
+    Result.insert(SymbolInfo(Symbol.getName(), Symbol.getSymbolKind(),
+                             Symbol.getFilePath(), Symbol.getLineNumber(),
+                             Symbol.getContexts(), Entry.second));
+  }
+  WriteSymbolInfosToStream(OS, Result);
+  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/tool/CMakeLists.txt b/include-fixer/tool/CMakeLists.txt
new file mode 100644 (file)
index 0000000..3a10627
--- /dev/null
@@ -0,0 +1,23 @@
+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.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..d32a13c
--- /dev/null
@@ -0,0 +1,374 @@
+//===-- 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 "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/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(std::string)
+LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(IncludeFixerContext::HeaderInfo)
+
+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> {
+  static void mapping(IO &IO, IncludeFixerContext &Context) {
+    IO.mapRequired("SymbolIdentifier", Context.SymbolIdentifier);
+    IO.mapRequired("HeaderInfos", Context.HeaderInfos);
+    IO.mapRequired("Range", Context.SymbolRange);
+  }
+};
+} // namespace yaml
+} // namespace llvm
+
+namespace {
+cl::OptionCategory IncludeFixerCategory("Tool options");
+
+enum DatabaseFormatTy {
+  fixed, ///< Hard-coded mapping.
+  yaml,  ///< Yaml database created by find-all-symbols.
+};
+
+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"),
+               clEnumValEnd),
+    cl::init(yaml), cl::cat(IncludeFixerCategory));
+
+cl::opt<std::string> Input("input",
+                           cl::desc("String to initialize the database"),
+                           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"
+             "    \"SymbolIdentifier\": \"foo\",\n"
+             "    \"Range\": {\"Offset\":0, \"Length\": 3},\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"
+             "     SymbolIdentifier: foo,\n"
+             "     Range: {Offset: 0, Length: 3},\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 "
+                   "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) {
+  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::SymbolInfo> 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(find_all_symbols::SymbolInfo(
+            Split.first.trim(),
+            find_all_symbols::SymbolInfo::SymbolKind::Unknown,
+            CommaSplits[I].trim(), 1, {}, /*NumOccurrences=*/E - I));
+    }
+    SymbolIndexMgr->addSymbolIndex(
+        llvm::make_unique<include_fixer::InMemorySymbolIndex>(Symbols));
+    break;
+  }
+  case yaml: {
+    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;
+    }
+
+    SymbolIndexMgr->addSymbolIndex(std::move(*DB));
+    break;
+  }
+  }
+  return SymbolIndexMgr;
+}
+
+void writeToJson(llvm::raw_ostream &OS, const IncludeFixerContext& Context) {
+  OS << "{\n"
+        "  \"SymbolIdentifier\": \""
+     << Context.getSymbolIdentifier() << "\",\n";
+  OS << "  \"Range\": {";
+  OS << " \"Offset\":" << Context.getSymbolRange().getOffset() << ",";
+  OS << " \"Length\":" << Context.getSymbolRange().getLength() << " },\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());
+
+  // 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(options.getSourcePathList().front(), Code->getBuffer());
+  }
+
+  StringRef FilePath = options.getSourcePathList().front();
+  format::FormatStyle InsertStyle = format::getStyle("file", FilePath, Style);
+
+  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;
+    }
+
+    auto Replacements = clang::include_fixer::createInsertHeaderReplacements(
+        Code->getBuffer(), FilePath, Context.getHeaderInfos().front().Header,
+        InsertStyle);
+    if (!Replacements) {
+      errs() << "Failed to create header insertion replacement: "
+             << llvm::toString(Replacements.takeError()) << "\n";
+      return 1;
+    }
+
+    // If a header have 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;
+        });
+    if (IsUniqueQualifiedName)
+      Replacements->insert({FilePath, Context.getSymbolRange().getOffset(),
+                            Context.getSymbolRange().getLength(),
+                            Context.getHeaderInfos().front().QualifiedName});
+    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(options.getSourcePathList().front());
+  if (!SymbolIndexMgr)
+    return 1;
+
+  // Now run our tool.
+  include_fixer::IncludeFixerContext Context;
+  include_fixer::IncludeFixerActionFactory Factory(*SymbolIndexMgr, Context,
+                                                   Style, MinimizeIncludePaths);
+
+  if (tool.run(&Factory) != 0) {
+    llvm::errs()
+        << "Clang died with a fatal error! (incorrect include paths?)\n";
+    return 1;
+  }
+
+  if (OutputHeaders) {
+    writeToJson(llvm::outs(), Context);
+    return 0;
+  }
+
+  if (Context.getHeaderInfos().empty())
+    return 0;
+
+  auto Buffer = llvm::MemoryBuffer::getFile(FilePath);
+  if (!Buffer) {
+    errs() << "Couldn't open file: " << FilePath;
+    return 1;
+  }
+
+  auto Replacements = clang::include_fixer::createInsertHeaderReplacements(
+      /*Code=*/Buffer.get()->getBuffer(), FilePath,
+      Context.getHeaderInfos().front().Header, InsertStyle);
+  if (!Replacements) {
+    errs() << "Failed to create header insertion replacement: "
+           << llvm::toString(Replacements.takeError()) << "\n";
+    return 1;
+  }
+
+  if (!Quiet)
+    llvm::errs() << "Added #include" << Context.getHeaderInfos().front().Header;
+
+  // Add missing namespace qualifiers to the unidentified symbol.
+  Replacements->insert({FilePath, Context.getSymbolRange().getOffset(),
+                        Context.getSymbolRange().getLength(),
+                        Context.getHeaderInfos().front().QualifiedName});
+
+  // 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);
+
+  if (STDINMode) {
+    auto ChangedCode =
+        tooling::applyAllReplacements(Code->getBuffer(), *Replacements);
+    if (!ChangedCode) {
+      llvm::errs() << llvm::toString(ChangedCode.takeError()) << "\n";
+      return 1;
+    }
+    llvm::outs() << *ChangedCode;
+    return 0;
+  }
+
+  // Write replacements to disk.
+  Rewriter Rewrites(SM, LangOptions());
+  tooling::applyAllReplacements(*Replacements, Rewrites);
+  return Rewrites.overwriteChangedFiles();
+}
+
+} // namespace
+
+int main(int argc, const char **argv) {
+  return includeFixerMain(argc, argv);
+}
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..ff15b8d
--- /dev/null
@@ -0,0 +1,176 @@
+# 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:
+#
+#   map ,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 ",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 subprocess
+import vim
+import json
+
+# 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"
+
+
+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)
+
+
+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.')
+  args = parser.parse_args()
+
+  # Get the current text.
+  buf = vim.current.buffer
+  text = '\n'.join(buf)
+
+  # 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)
+  symbol = include_fixer_context["SymbolIdentifier"]
+  # 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 symbol:
+    print "The file is fine, no need to add a header."
+    return
+
+  if not unique_headers:
+    print "Couldn't find a header for {0}.".format(symbol)
+    return
+
+  try:
+    # If there is only one suggested header, insert it directly.
+    if len(unique_headers) == 1 or maximum_suggested_headers == 1:
+      InsertHeaderToVimBuffer({"SymbolIdentifier": symbol,
+                               "Range": include_fixer_context["Range"],
+                               "HeaderInfos": header_infos}, text)
+      print "Added #include {0} for {1}.".format(unique_headers[0], symbol)
+      return
+
+    selected = GetUserSelection("choose a header file for {0}.".format(symbol),
+                                unique_headers, maximum_suggested_headers)
+    selected_header_infos = [
+      header for header in header_infos if header["Header"] == selected]
+
+    # Insert a selected header.
+    InsertHeaderToVimBuffer({"SymbolIdentifier": symbol,
+                             "Range": include_fixer_context["Range"],
+                             "HeaderInfos": selected_header_infos}, 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..17d1eb1
--- /dev/null
@@ -0,0 +1,25 @@
+set(LLVM_LINK_COMPONENTS
+  Option
+  Support
+  )
+
+add_clang_executable(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..b3aa3c9
--- /dev/null
@@ -0,0 +1,421 @@
+//===--- 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.
+CoverageChecker *CoverageChecker::createCoverageChecker(
+  StringRef ModuleMapPath, std::vector<std::string> &IncludePaths,
+  ArrayRef<std::string> CommandLine, clang::ModuleMap *ModuleMap) {
+
+  return new 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 (Module::submodule_const_iterator 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..a0e5f6f
--- /dev/null
@@ -0,0 +1,165 @@
+//===-- 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 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..e1e913e
--- /dev/null
@@ -0,0 +1,994 @@
+//===- 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 (CommandLineArguments::const_iterator 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(LambdaCapture C) { 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(LambdaCapture C) { 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..9a26895
--- /dev/null
@@ -0,0 +1,549 @@
+//===--- 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)),
+    HeaderSearchOpts(new HeaderSearchOptions()),
+    HeaderInfo(new HeaderSearch(HeaderSearchOpts, *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() {
+  typedef std::vector<std::string>::iterator Iter;
+  // For each input file.
+  for (Iter 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];
+    CoverageChecker *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 (clang::Module::submodule_const_iterator 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..a3c412e
--- /dev/null
@@ -0,0 +1,227 @@
+//=====-- 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;
+  /// Options controlling the \#include directive.
+  llvm::IntrusiveRefCntPtr<clang::HeaderSearchOptions> HeaderSearchOpts;
+  /// 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..e1c32df
--- /dev/null
@@ -0,0 +1,321 @@
+//===--- 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 (std::vector<Module *>::iterator I = SubModules.begin(),
+                                       E = SubModules.end();
+       I != E; ++I) {
+    if (!(*I)->output(OS, Indent))
+      return false;
+  }
+
+  // Output header files.
+  for (std::vector<std::string>::iterator 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 (std::vector<Module *>::iterator 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.
+  Module *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..f594cd5
--- /dev/null
@@ -0,0 +1,1409 @@
+//===--- 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->getArgumentNum(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 (std::vector<InclusionPathHandle>::iterator
+             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 (std::vector<MacroExpansionInstance>::iterator
+             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 (std::vector<InclusionPathHandle>::iterator
+             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 (std::vector<ConditionalExpansionInstance>::iterator
+             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 (std::vector<StringHandle>::const_iterator 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 (std::vector<HeaderHandle>::const_iterator 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 (std::vector<HeaderInclusionPath>::const_iterator
+             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);
+    MacroExpansionMapIter 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);
+    ConditionalExpansionMapIter 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 (MacroExpansionMapIter 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 (std::vector<MacroExpansionInstance>::iterator
+               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 (std::vector<InclusionPathHandle>::iterator
+                 IIP = MacroInfo.InclusionPathHandles.begin(),
+                 EIP = MacroInfo.InclusionPathHandles.end();
+             IIP != EIP; ++IIP) {
+          const std::vector<HeaderHandle> &ip = getInclusionPath(*IIP);
+          int 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 (ConditionalExpansionMapIter 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 (std::vector<ConditionalExpansionInstance>::iterator
+               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 (std::vector<InclusionPathHandle>::iterator
+                 IIP = MacroInfo.InclusionPathHandles.begin(),
+                 EIP = MacroInfo.InclusionPathHandles.end();
+             IIP != EIP; ++IIP) {
+          const std::vector<HeaderHandle> &ip = getInclusionPath(*IIP);
+          int 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..dbf7110
--- /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) {
+  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 << "[";
+  // The argument tokens might include end tokens, so we reflect how
+  // how getUnexpArgument provides the arguments.
+  for (int I = 0, E = Value->getNumArguments(); I < E; ++I) {
+    const clang::Token *Current = Value->getUnexpArgument(I);
+    int TokenCount = Value->getArgLength(Current) + 1; // include EOF
+    E -= TokenCount;
+    if (I)
+      SS << ", ";
+    // We're assuming tokens are contiguous, as otherwise we have no
+    // other way to get at them.
+    --TokenCount;
+    for (int TokenIndex = 0; TokenIndex < TokenCount; ++TokenIndex, ++Current) {
+      if (TokenIndex)
+        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() << ">";
+      }
+    }
+  }
+  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..f91d1bc
--- /dev/null
@@ -0,0 +1,240 @@
+//===--- 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"
+
+/// \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() {}
+
+  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() {}
+
+  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) 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..51c844c
--- /dev/null
@@ -0,0 +1,234 @@
+//===--- 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 (std::vector<Argument>::const_iterator 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..22ee4ee
--- /dev/null
@@ -0,0 +1,63 @@
+# 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
+  # Base line deps.
+  FileCheck count not
+
+  # clang-tidy tests require it.
+  clang-headers
+
+  # For the clang-tidy libclang integration test.
+  c-index-test
+
+  # Individual tools we test.
+  clang-apply-replacements
+  clang-include-fixer
+  clang-query
+  clang-rename
+  clang-tidy
+  find-all-symbols
+  modularize
+  pp-trace
+
+  # Unit tests
+  ExtraToolsUnitTests
+  )
+
+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/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..155104f
--- /dev/null
@@ -0,0 +1,20 @@
+---
+MainSourceFile:     source1.cpp
+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..9a7a156
--- /dev/null
@@ -0,0 +1,8 @@
+---
+MainSourceFile:     source2.cpp
+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..5d962cd
--- /dev/null
@@ -0,0 +1,16 @@
+---
+MainSourceFile: source1.cpp
+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..d8877f7
--- /dev/null
@@ -0,0 +1,16 @@
+---
+MainSourceFile: source2.cpp
+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..0ce57b6
--- /dev/null
@@ -0,0 +1,8 @@
+---
+MainSourceFile: source1.cpp
+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..0ee548a
--- /dev/null
@@ -0,0 +1,8 @@
+---
+MainSourceFile:      source1.cpp
+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..ee85843
--- /dev/null
@@ -0,0 +1,8 @@
+---
+MainSourceFile:  no.cpp
+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..e87f067
--- /dev/null
@@ -0,0 +1,22 @@
+# These replacements are out of order on purpose to test that they get sorted
+# so that formatting happens correctly.
+---
+MainSourceFile:  yes.cpp
+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-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-rename/ClassFindByName.cpp b/test/clang-rename/ClassFindByName.cpp
new file mode 100644 (file)
index 0000000..4cd090c
--- /dev/null
@@ -0,0 +1,11 @@
+// RUN: cat %s > %t.cpp
+// RUN: clang-rename -old-name=Foo -new-name=Bar %t.cpp -i --
+// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s
+
+class Foo {         // CHECK: class Bar
+};
+
+int main() {
+  Foo *Pointer = 0; // CHECK: Bar *Pointer = 0;
+  return 0;
+}
diff --git a/test/clang-rename/ClassNameInFunctionDefenition.cpp b/test/clang-rename/ClassNameInFunctionDefenition.cpp
new file mode 100644 (file)
index 0000000..c0e5f07
--- /dev/null
@@ -0,0 +1,17 @@
+// Currently unsupported test.
+// RUN: cat %s > %t.cpp
+// FIXME: clang-rename doesn't recognize symbol in class function definition.
+
+class Foo {
+public:
+  void foo(int x);
+};
+
+void Foo::foo(int x) {}
+//   ^ this one
+
+int main() {
+  Foo obj;
+  obj.foo(0);
+  return 0;
+}
diff --git a/test/clang-rename/ClassReplacements.cpp b/test/clang-rename/ClassReplacements.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-rename/ClassSimpleRenaming.cpp b/test/clang-rename/ClassSimpleRenaming.cpp
new file mode 100644 (file)
index 0000000..72710d0
--- /dev/null
@@ -0,0 +1,13 @@
+// RUN: cat %s > %t.cpp
+// RUN: clang-rename -offset=136 -new-name=Bar %t.cpp -i --
+// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s
+
+class Foo {};       // CHECK: class Bar
+
+int main() {
+  Foo *Pointer = 0; // CHECK: Bar *Pointer = 0;
+  return 0;
+}
+
+// Use grep -FUbo 'Foo' <file> to get the correct offset of Cla when changing
+// this file.
diff --git a/test/clang-rename/ComplicatedClassType.cpp b/test/clang-rename/ComplicatedClassType.cpp
new file mode 100644 (file)
index 0000000..80daeab
--- /dev/null
@@ -0,0 +1,30 @@
+// Unsupported test.
+// RUN: cat %s > %t.cpp
+// FIXME: This test contains very simple constructions likely to be seen in any
+// project and therefore passing this test is a slight sign of success.
+// Currently, the test fails badly.
+
+class Foo {                           // CHECK: class Bar {
+ public:
+  Foo(int value = 0) : x(value) {}    // Bar(int value=0) : x(value) {}
+
+  Foo& operator++(int) {              // Bar& operator++(int) {
+    x++;
+    return *this;
+  }
+
+  bool operator<(Foo const& rhs) {    // bool operator<(Bar const &rhs) {
+    return this->x < rhs.x;
+  }
+
+ private:
+  int x;
+};
+
+int main() {
+  Foo* Pointer = 0;                   // CHECK: Bar *Pointer = 0;
+  Foo Variable = Foo(10);             // CHECK: Bar Variable = Bar(10);
+  for (Foo it; it < Variable; it++) { // for (Bar it; it < Variable; it++) {}
+  }
+  return 0;
+}
diff --git a/test/clang-rename/ConstCastExpr.cpp b/test/clang-rename/ConstCastExpr.cpp
new file mode 100644 (file)
index 0000000..6d6914f
--- /dev/null
@@ -0,0 +1,18 @@
+// RUN: cat %s > %t.cpp
+// RUN: clang-rename -offset=136 -new-name=Bar %t.cpp -i --
+// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s
+
+class Foo {                         // CHECK: class Bar {
+public:
+  int getValue() {
+    return 0;
+  }
+};
+
+int main() {
+  const Foo *C = new Foo();         // CHECK: const Bar *C = new Bar();
+  const_cast<Foo *>(C)->getValue(); // CHECK: const_cast<Bar *>(C)->getValue();
+}
+
+// Use grep -FUbo 'Cla' <file> to get the correct offset of foo when changing
+// this file.
diff --git a/test/clang-rename/ConstructExpr.cpp b/test/clang-rename/ConstructExpr.cpp
new file mode 100644 (file)
index 0000000..d7f104c
--- /dev/null
@@ -0,0 +1,12 @@
+// RUN: cat %s > %t.cpp
+// RUN: clang-rename -offset=136 -new-name=Boo %t.cpp -i --
+// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s
+
+class Foo {};         // CHECK: class Boo {};
+
+int main() {
+  Foo *C = new Foo(); // CHECK: Boo *C = new Boo();
+}
+
+// Use grep -FUbo 'Boo' <file> to get the correct offset of foo when changing
+// this file.
diff --git a/test/clang-rename/CtorFindByDeclaration.cpp b/test/clang-rename/CtorFindByDeclaration.cpp
new file mode 100644 (file)
index 0000000..3fb25ae
--- /dev/null
@@ -0,0 +1,13 @@
+// RUN: cat %s > %t.cpp
+// RUN: clang-rename -offset=174 -new-name=Bar %t.cpp -i --
+// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s
+
+class Foo {   // CHECK: class Bar
+public:
+  Foo();      // CHECK: Bar();
+};
+
+Foo::Foo() {} // CHECK: Bar::Bar()
+
+// Use grep -FUbo 'C' <file> to get the correct offset of foo when changing
+// this file.
diff --git a/test/clang-rename/CtorFindByDefinition.cpp b/test/clang-rename/CtorFindByDefinition.cpp
new file mode 100644 (file)
index 0000000..2f7e65c
--- /dev/null
@@ -0,0 +1,13 @@
+// RUN: cat %s > %t.cpp
+// RUN: clang-rename -offset=212 -new-name=Bar %t.cpp -i --
+// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s
+
+class Foo {   // CHECK: class Bar
+public:
+  Foo();      // CHECK: Bar();
+};
+
+Foo::Foo() {} // CHECK: Bar::Bar()
+
+// Use grep -FUbo 'C' <file> to get the correct offset of foo when changing
+// this file.
diff --git a/test/clang-rename/CtorInitializer.cpp b/test/clang-rename/CtorInitializer.cpp
new file mode 100644 (file)
index 0000000..1fa1665
--- /dev/null
@@ -0,0 +1,16 @@
+// RUN: cat %s > %t.cpp
+// RUN: clang-rename -offset=163 -new-name=Bar %t.cpp -i --
+// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s
+
+class Baz {};
+
+class Qux {
+  Baz Foo;            // CHECK: Baz Bar;
+public:
+  Qux();
+};
+
+Qux::Qux() : Foo() {} // CHECK: Qux::Qux() : Bar() {}
+
+// Use grep -FUbo 'Foo' <file> to get the correct offset of foo when changing
+// this file.
diff --git a/test/clang-rename/DeclRefExpr.cpp b/test/clang-rename/DeclRefExpr.cpp
new file mode 100644 (file)
index 0000000..2059ff0
--- /dev/null
@@ -0,0 +1,20 @@
+// RUN: cat %s > %t.cpp
+// RUN: clang-rename -offset=161 -new-name=Bar %t.cpp -i --
+// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s
+
+class C {
+public:
+  static int Foo; // CHECK: static int Bar;
+};
+
+int foo(int x) { return 0; }
+#define MACRO(a) foo(a)
+
+int main() {
+  C::Foo = 1;     // CHECK: C::Bar
+  MACRO(C::Foo);    // CHECK: C::Bar
+  int y = C::Foo; // CHECK: C::Bar
+}
+
+// Use grep -FUbo 'X' <file> to get the correct offset of foo when changing
+// this file.
diff --git a/test/clang-rename/DtorDeclaration.cpp b/test/clang-rename/DtorDeclaration.cpp
new file mode 100644 (file)
index 0000000..239ae57
--- /dev/null
@@ -0,0 +1,14 @@
+// RUN: cat %s > %t.cpp
+// RUN: clang-rename -offset=175 -new-name=Bar %t.cpp -i --
+// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s
+
+class Foo { // CHECK: class Bar {
+public:
+  ~Foo();   // CHECK: ~Bar();
+};
+
+Foo::~Foo() { // CHECK: Bar::~Bar()
+}
+
+// Use grep -FUbo 'Bar' <file> to get the correct offset of foo when changing
+// this file.
diff --git a/test/clang-rename/DtorDefinition.cpp b/test/clang-rename/DtorDefinition.cpp
new file mode 100644 (file)
index 0000000..d817397
--- /dev/null
@@ -0,0 +1,14 @@
+// RUN: cat %s > %t.cpp
+// RUN: clang-rename -offset=219 -new-name=Bar %t.cpp -i --
+// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s
+
+class Foo {     // CHECK: class Bar {
+public:
+  ~Foo();       // CHECK: ~Bar();
+};
+
+Foo::~Foo() {}  // CHECK: Bar::~Bar()
+
+
+// Use grep -FUbo 'Foo' <file> to get the correct offset of foo when changing
+// this file.
diff --git a/test/clang-rename/DynamicCastExpr.cpp b/test/clang-rename/DynamicCastExpr.cpp
new file mode 100644 (file)
index 0000000..b940dd7
--- /dev/null
@@ -0,0 +1,26 @@
+// RUN: cat %s > %t.cpp
+// RUN: clang-rename -offset=195 -new-name=Bar %t.cpp -i -- -frtti
+// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s
+
+class Baz {
+  virtual int getValue() const = 0;
+};
+
+class Foo : public Baz {                           // CHECK: class Bar : public Baz {
+public:
+  int getValue() const {
+    return 0;
+  }
+};
+
+int main() {
+  Foo foo;                                         // FIXME: Bar foo; <- this one fails
+  const Baz &Reference = foo;
+  const Baz *Pointer = &foo;
+
+  dynamic_cast<const Foo &>(Reference).getValue(); // CHECK: dynamic_cast<const Bar &>(Reference).getValue();
+  dynamic_cast<const Foo *>(Pointer)->getValue();  // CHECK: dynamic_cast<const Bar *>(Pointer)->getValue();
+}
+
+// Use grep -FUbo 'Foo' <file> to get the correct offset of foo when changing
+// this file.
diff --git a/test/clang-rename/Field.cpp b/test/clang-rename/Field.cpp
new file mode 100644 (file)
index 0000000..2179558
--- /dev/null
@@ -0,0 +1,14 @@
+// RUN: cat %s > %t.cpp
+// RUN: clang-rename -offset=148 -new-name=Bar %t.cpp -i --
+// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s
+
+class Baz {
+  int Foo;              // CHECK: Bar;
+public:
+  Baz();
+};
+
+Baz::Baz() : Foo(0) {}  // CHECK: Baz::Baz() : Bar(0) {}
+
+// Use grep -FUbo 'Foo' <file> to get the correct offset of foo when changing
+// this file.
diff --git a/test/clang-rename/FunctionMacro.cpp b/test/clang-rename/FunctionMacro.cpp
new file mode 100644 (file)
index 0000000..1cdac27
--- /dev/null
@@ -0,0 +1,21 @@
+// RUN: cat %s > %t.cpp
+// RUN: clang-rename -offset=199 -new-name=macro_function %t.cpp -i --
+// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s
+
+#define moo foo // CHECK: #define moo macro_function
+
+int foo() {     // CHECK: int macro_function() {
+  return 42;
+}
+
+void boo(int value) {}
+
+void qoo() {
+  foo();        // CHECK: macro_function();
+  boo(foo());   // CHECK: boo(macro_function());
+  moo();
+  boo(moo());
+}
+
+// Use grep -FUbo 'foo;' <file> to get the correct offset of foo when changing
+// this file.
diff --git a/test/clang-rename/MemberExprMacro.cpp b/test/clang-rename/MemberExprMacro.cpp
new file mode 100644 (file)
index 0000000..24ba05f
--- /dev/null
@@ -0,0 +1,21 @@
+// RUN: cat %s > %t.cpp
+// RUN: clang-rename -offset=156 -new-name=Bar %t.cpp -i --
+// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s
+
+class Baz {
+public:
+  int Foo;          // CHECK: int Bar;
+};
+
+int qux(int x) { return 0; }
+#define MACRO(a) qux(a)
+
+int main() {
+  Baz baz;
+  baz.Foo = 1;      // CHECK: baz.Bar = 1;
+  MACRO(baz.Foo);   // CHECK: MACRO(baz.Bar);
+  int y = baz.Foo;  // CHECK: int y = baz.Bar;
+}
+
+// Use grep -FUbo 'Foo' <file> to get the correct offset of foo when changing
+// this file.
diff --git a/test/clang-rename/Namespace.cpp b/test/clang-rename/Namespace.cpp
new file mode 100644 (file)
index 0000000..5eea4a6
--- /dev/null
@@ -0,0 +1,14 @@
+// RUN: cat %s > %t.cpp
+// RUN: clang-rename -offset=143 -new-name=llvm %t.cpp -i --
+// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s
+
+namespace foo { // CHECK: namespace llvm {
+  int x;
+}
+
+void boo() {
+  foo::x = 42;  // CHECK: llvm::x = 42;
+}
+
+// Use grep -FUbo 'foo;' <file> to get the correct offset of foo when changing
+// this file.
diff --git a/test/clang-rename/NoNewName.cpp b/test/clang-rename/NoNewName.cpp
new file mode 100644 (file)
index 0000000..d50efd6
--- /dev/null
@@ -0,0 +1,4 @@
+// Check for an error while -new-name argument has not been passed to
+// clang-rename.
+// RUN: not clang-rename -offset=133 %s 2>&1 | FileCheck %s
+// CHECK: clang-rename: no new name provided.
diff --git a/test/clang-rename/ReinterpretCastExpr.cpp b/test/clang-rename/ReinterpretCastExpr.cpp
new file mode 100644 (file)
index 0000000..2d5ecd2
--- /dev/null
@@ -0,0 +1,17 @@
+// RUN: cat %s > %t.cpp
+// RUN: clang-rename -offset=133 -new-name=X %t.cpp -i --
+// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s
+class Cla {
+public:
+  int getValue() const {
+    return 0;
+  }
+};
+
+int main() {
+  void *C = new Cla();
+  reinterpret_cast<const Cla *>(C)->getValue(); // CHECK: reinterpret_cast<const X *>
+}
+
+// Use grep -FUbo 'Cla' <file> to get the correct offset of foo when changing
+// this file.
diff --git a/test/clang-rename/StaticCastExpr.cpp b/test/clang-rename/StaticCastExpr.cpp
new file mode 100644 (file)
index 0000000..b75102a
--- /dev/null
@@ -0,0 +1,25 @@
+// RUN: cat %s > %t.cpp
+// RUN: clang-rename -offset=152 -new-name=Bar %t.cpp -i --
+// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s
+
+class Baz {
+};
+
+class Foo : public Baz {                          // CHECK: class Bar : public Baz {
+public:
+  int getValue() const {
+    return 0;
+  }
+};
+
+int main() {
+  Foo foo;                                        // FIXME: Bar foo;
+  const Baz &Reference = foo;
+  const Baz *Pointer = &foo;
+
+  static_cast<const Foo &>(Reference).getValue(); // CHECK: static_cast<const Bar &>(Reference).getValue();
+  static_cast<const Foo *>(Pointer)->getValue();  // CHECK: static_cast<const Bar *>(Pointer)->getValue();
+}
+
+// Use grep -FUbo 'Foo' <file> to get the correct offset of foo when changing
+// this file.
diff --git a/test/clang-rename/TemplateTypename.cpp b/test/clang-rename/TemplateTypename.cpp
new file mode 100644 (file)
index 0000000..0641a05
--- /dev/null
@@ -0,0 +1,12 @@
+// Currently unsupported test.
+// RUN: cat %s > %t.cpp
+// FIXME: clang-rename should be able to rename template parameters correctly.
+
+template <typename T>
+T foo(T arg, T& ref, T* ptr) {
+  T value;
+  int number = 42;
+  value = (T)number;
+  value = static_cast<T>(number);
+  return value;
+}
diff --git a/test/clang-rename/UserDefinedConversion.cpp b/test/clang-rename/UserDefinedConversion.cpp
new file mode 100644 (file)
index 0000000..e810d12
--- /dev/null
@@ -0,0 +1,13 @@
+// Currently unsupported test.
+// RUN: cat %s > %t.cpp
+// FIXME: clang-rename should handle conversions from a class type to another
+// type.
+
+class Foo {};             // CHECK: class Bar {};
+
+class Baz {               // CHECK: class Bar {
+  operator Foo() const {  // CHECK: operator Bar() const {
+    Foo foo;              // CHECK: Bar foo;
+    return foo;
+  }
+};
diff --git a/test/clang-rename/UserDefinedConversionFindByTypeDeclaration.cpp b/test/clang-rename/UserDefinedConversionFindByTypeDeclaration.cpp
new file mode 100644 (file)
index 0000000..af85a2c
--- /dev/null
@@ -0,0 +1,24 @@
+// Currently unsupported test.
+// RUN: cat %s > %t.cpp
+// FIXME: while renaming class/struct clang-rename should be able to change
+// this type name corresponding user-defined conversions, too.
+
+class Foo {                         // CHECK: class Bar {
+//    ^ offset must be here
+public:
+  Foo() {}                          // CHECK: Bar() {}
+};
+
+class Baz {
+public:
+  operator Foo() const {            // CHECK: operator Bar() const {
+    Foo foo;                        // CHECK: Bar foo;
+    return foo;
+  }
+};
+
+int main() {
+  Baz boo;
+  Foo foo = static_cast<Foo>(boo);  // CHECK: Bar foo = static_cast<Bar>(boo);
+  return 0;
+}
diff --git a/test/clang-rename/Variable.cpp b/test/clang-rename/Variable.cpp
new file mode 100644 (file)
index 0000000..02935bd
--- /dev/null
@@ -0,0 +1,27 @@
+// RUN: cat %s > %t.cpp
+// RUN: clang-rename -offset=148 -new-name=Bar %t.cpp -i --
+// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s
+
+namespace A {
+int Foo;                // CHECK: int Bar;
+}
+int Foo;                // CHECK: int Foo;
+int Qux = Foo;          // CHECK: int Qux = Foo;
+int Baz = A::Foo;       // CHECK: Baz = A::Bar;
+void fun() {
+  struct {
+    int Foo;            // CHECK: int Foo;
+  } b = {100};
+  int Foo = 100;        // CHECK: int Foo = 100;
+  Baz = Foo;            // CHECK: Baz = Foo;
+  {
+    extern int Foo;     // CHECK: extern int Foo;
+    Baz = Foo;          // CHECK: Baz = Foo;
+    Foo = A::Foo + Baz; // CHECK: Foo = A::Bar + Baz;
+    A::Foo = b.Foo;     // CHECK: A::Bar = b.Foo;
+  }
+  Foo = b.Foo;          // Foo = b.Foo;
+}
+
+// Use grep -FUbo 'Foo' <file> to get the correct offset of foo when changing
+// this file.
diff --git a/test/clang-rename/VariableMacro.cpp b/test/clang-rename/VariableMacro.cpp
new file mode 100644 (file)
index 0000000..2a0cd87
--- /dev/null
@@ -0,0 +1,18 @@
+// RUN: cat %s > %t.cpp
+// RUN: clang-rename -offset=208 -new-name=Z %t.cpp -i --
+// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s
+
+#define Y X // CHECK: #define Y Z
+
+void foo(int value) {}
+
+void macro() {
+  int X;    // CHECK: int Z;
+  X = 42;   // CHECK: Z = 42;
+  Y -= 0;
+  foo(X);   // CHECK: foo(Z);
+  foo(Y);
+}
+
+// Use grep -FUbo 'foo;' <file> to get the correct offset of foo when changing
+// this file.
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/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/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.h b/test/clang-tidy/Inputs/modernize-pass-by-value/header.h
new file mode 100644 (file)
index 0000000..81c73d3
--- /dev/null
@@ -0,0 +1,7 @@
+class 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-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/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/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/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..8073633
--- /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/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-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..ae2d967
--- /dev/null
@@ -0,0 +1,52 @@
+// RUN: %check_clang_tidy %s cert-err58-cpp %t -- -- -std=c++11 -target x86_64-pc-linux-gnu
+
+struct S {
+  S() noexcept(false);
+};
+
+struct T {
+  T() noexcept;
+};
+
+struct U {
+  U() {}
+};
+
+struct V {
+  explicit V(const char *) {} // Can throw
+};
+
+
+S s;
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: construction of 's' with static storage duration may throw an exception that cannot be caught [cert-err58-cpp]
+// CHECK-MESSAGES: 4:3: note: possibly throwing constructor declared here
+T t; // ok
+U u;
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: construction of 'u' with static storage duration may throw an exception that cannot be caught
+// CHECK-MESSAGES: 12:3: note: possibly throwing constructor declared here
+V v("v");
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: construction of 'v' with static storage duration may throw an exception that cannot be caught
+// CHECK-MESSAGES: 16:12: note: possibly throwing constructor declared here
+
+void f(S s1, T t1, U u1, V v1) { // ok, ok, ok, ok
+  S s2; // ok
+  T t2; // ok
+  U u2; // ok
+  V v2("v"); // ok
+
+  thread_local S s3;
+  // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: construction of 's3' with thread_local storage duration may throw an exception that cannot be caught
+  thread_local T t3; // ok
+  thread_local U u3;
+  // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: construction of 'u3' with thread_local storage duration may throw an exception that cannot be caught
+  thread_local V v3("v");
+  // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: construction of 'v3' with thread_local storage duration may throw an exception that cannot be caught
+
+  static S s4;
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: construction of 's4' with static storage duration may throw an exception that cannot be caught
+  static T t4; // ok
+  static U u4;
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: construction of 'u4' with static storage duration may throw an exception that cannot be caught
+  static V v4("v");
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: construction of 'v4' with static storage duration may throw an exception that cannot be caught
+}
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..ca64119
--- /dev/null
@@ -0,0 +1,135 @@
+#!/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' \
+                       else ['--']
+  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..88a0c01
--- /dev/null
@@ -0,0 +1,18 @@
+// 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
+struct A {
+  virtual void f() {}
+  virtual void g() {}
+};
+// CHECK-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
+  void g() {}
+// CHECK-SANITY: [[@LINE-1]]:8: warning: annotate this
+// CHECK-NOT: warning:
+};
+// CHECK-SANITY-NOT: Suppressed
+// CHECK: Suppressed 1 warnings (1 due to line filter).
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..7304f06
--- /dev/null
@@ -0,0 +1,24 @@
+// REQUIRES: shell
+// 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/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-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..8f22302
--- /dev/null
@@ -0,0 +1,76 @@
+// 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
+}
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..5596c43
--- /dev/null
@@ -0,0 +1,369 @@
+// 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 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
+};
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/custom-diagnostics.cpp b/test/clang-tidy/custom-diagnostics.cpp
new file mode 100644 (file)
index 0000000..61a9292
--- /dev/null
@@ -0,0 +1,12 @@
+// RUN: clang-tidy -checks='-*,modernize-use-override,clang-diagnostic-shadow,clang-diagnostic-float-conversion' %s -- | count 0
+// 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
+
+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..9d2fb5c
--- /dev/null
@@ -0,0 +1,19 @@
+// 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
+
+// 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]
+
+// CHECK2: :[[@LINE+2]]:9: warning: implicit conversion from 'double' to 'int' changes value from 1.5 to 1 [clang-diagnostic-literal-conversion]
+// CHECK3: :[[@LINE+1]]:9: warning: implicit conversion from 'double' to 'int' changes value
+int a = 1.5;
+
+// CHECK2: :[[@LINE+2]]:11: warning: single-argument constructors must be marked explicit
+// CHECK3: :[[@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/file-filter.cpp b/test/clang-tidy/file-filter.cpp
new file mode 100644 (file)
index 0000000..1f52513
--- /dev/null
@@ -0,0 +1,45 @@
+// 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='.*' %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='header2\.h' %s -- -I %S/Inputs/file-filter -isystem %S/Inputs/file-filter/system 2>&1 | FileCheck --check-prefix=CHECK3 %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
+
+#include "header1.h"
+// CHECK-NOT: warning:
+// CHECK2: header1.h:1:12: warning: single-argument constructors must be marked explicit
+// CHECK3-NOT: warning:
+// CHECK4: header1.h:1:12: warning: single-argument constructors
+
+#include "header2.h"
+// CHECK-NOT: warning:
+// CHECK2: header2.h:1:12: warning: single-argument constructors
+// CHECK3: header2.h:1:12: warning: single-argument constructors
+// CHECK4: header2.h:1:12: warning: single-argument constructors
+
+#include <system-header.h>
+// CHECK-NOT: warning:
+// CHECK2-NOT: warning:
+// CHECK3-NOT: warning:
+// CHECK4: system-header.h:1:12: warning: single-argument constructors
+
+class A { A(int); };
+// CHECK: :[[@LINE-1]]:11: warning: single-argument constructors
+// CHECK2: :[[@LINE-2]]:11: warning: single-argument constructors
+// CHECK3: :[[@LINE-3]]:11: warning: single-argument constructors
+// CHECK4: :[[@LINE-4]]:11: warning: single-argument constructors
+
+// CHECK-NOT: warning:
+// CHECK2-NOT: warning:
+// CHECK3-NOT: warning:
+// CHECK4-NOT: warning:
+
+// CHECK: Suppressed 3 warnings (3 in non-user code)
+// CHECK: Use -header-filter=.* to display errors from all non-system headers.
+// CHECK2: Suppressed 1 warnings (1 in non-user code)
+// CHECK2: Use -header-filter=.* {{.*}}
+// CHECK3: Suppressed 2 warnings (2 in non-user code)
+// CHECK3: Use -header-filter=.* {{.*}}
+// CHECK4-NOT: Suppressed {{.*}} warnings
+// CHECK4-NOT: Use -header-filter=.* {{.*}}
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..80b215d
--- /dev/null
@@ -0,0 +1,17 @@
+// 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 {
+}
+// 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..b49ce7b
--- /dev/null
@@ -0,0 +1,131 @@
+// 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 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);
+};
+
+struct B {
+  B(std::initializer_list<int> list1) {}
+  B(const std::initializer_list<unsigned> &list2) {}
+  B(std::initializer_list<unsigned> &&list3) {}
+
+  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) {}
+};
+
+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);
+}
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..891aee3
--- /dev/null
@@ -0,0 +1,8 @@
+// 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
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..df16745
--- /dev/null
@@ -0,0 +1,22 @@
+// 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;
+}
+
+#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..16b3453
--- /dev/null
@@ -0,0 +1,150 @@
+// 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: possibly redundant cast between typedefs of the same type [google-readability-casting]
+  // CHECK-FIXES: {{^}}  (Typedef2)t1;
+  (const char*)t1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: possibly redundant cast {{.*}}
+  // CHECK-FIXES: {{^}}  (const char*)t1;
+  (Typedef1)cpc;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: possibly redundant cast {{.*}}
+  // CHECK-FIXES: {{^}}  (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);
+
+  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 reinterpret_cast {{.*}}
+  // CHECK-FIXES: char *pc5 = const_cast<char*>(reinterpret_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);
+
+  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 reinterpret_cast {{.*}}
+  // CHECK-FIXES: const char *pc3 = reinterpret_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);
+
+  // CHECK-MESSAGES-NOT: warning:
+  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> {};
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..4e38100
--- /dev/null
@@ -0,0 +1,40 @@
+// RUN: %check_clang_tidy %s google-readability-namespace-comments %t
+
+namespace n1 {
+namespace n2 {
+
+
+
+
+
+// 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-memset-zero-length.cpp b/test/clang-tidy/google-runtime-memset-zero-length.cpp
new file mode 100644 (file)
index 0000000..7599c75
--- /dev/null
@@ -0,0 +1,62 @@
+// RUN: %check_clang_tidy %s google-runtime-memset %t
+
+void *memset(void *, int, __SIZE_TYPE__);
+
+namespace std {
+  using ::memset;
+}
+
+template <int i, typename T>
+void memtmpl() {
+  memset(0, sizeof(int), i);
+  memset(0, sizeof(T), sizeof(T));
+  memset(0, sizeof(T), 0);
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: memset of size zero, potentially swapped argument
+// CHECK-FIXES: memset(0, 0, sizeof(T));
+  memset(0, sizeof(int), 0);
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: memset of size zero, potentially swapped argument
+// CHECK-FIXES: memset(0, 0, sizeof(int));
+}
+
+void foo(void *a, int xsize, int ysize) {
+  memset(a, sizeof(int), 0);
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: memset of size zero, potentially swapped argument
+// CHECK-FIXES: memset(a, 0, sizeof(int));
+#define M memset(a, sizeof(int), 0);
+  M
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: memset of size zero, potentially swapped argument
+// CHECK-FIXES: #define M memset(a, sizeof(int), 0);
+  ::memset(a, xsize *
+           ysize, 0);
+// CHECK-MESSAGES: :[[@LINE-2]]:3: warning: memset of size zero, potentially swapped argument
+// CHECK-FIXES: ::memset(a, 0, xsize *
+// CHECK-FIXES-NEXT: ysize);
+  std::memset(a, sizeof(int), 0x00);
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: memset of size zero, potentially swapped argument
+// CHECK-FIXES: std::memset(a, 0x00, sizeof(int));
+
+  const int v = 0;
+  memset(a, sizeof(int), v);
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: memset of size zero, potentially swapped argument
+// CHECK-FIXES: memset(a, v, sizeof(int));
+
+  memset(a, sizeof(int), v + v);
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: memset of size zero, potentially swapped argument
+// CHECK-FIXES: memset(a, v + v, sizeof(int));
+
+  memset(a, sizeof(int), v + 1);
+
+  memset(a, -1, sizeof(int));
+  memset(a, 0xcd, 1);
+
+  // Don't warn when the fill char and the length are both known to be
+  // zero.  No bug is possible.
+  memset(a, 0, v);
+  memset(a, v, 0);
+
+  // -1 is clearly not a length by virtue of being negative, so no warning
+  // despite v == 0.
+  memset(a, -1, v);
+
+  memtmpl<0, int>();
+}
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..c96f28f
--- /dev/null
@@ -0,0 +1,139 @@
+// RUN: %check_clang_tidy %s google-runtime-references %t
+
+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; }
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..9d83775
--- /dev/null
@@ -0,0 +1,5 @@
+// REQUIRES: shell
+// 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..445beb1
--- /dev/null
@@ -0,0 +1,37 @@
+// 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"
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..aa99420
--- /dev/null
@@ -0,0 +1,32 @@
+// RUN: %check_clang_tidy %s llvm-twine-local %t
+
+namespace llvm {
+class Twine {
+public:
+  Twine(const char *);
+  Twine(int);
+  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";
+}
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.cpp b/test/clang-tidy/misc-argument-comment.cpp
new file mode 100644 (file)
index 0000000..80180ee
--- /dev/null
@@ -0,0 +1,48 @@
+// 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);
+}
+
+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(/*zzZ=*/0, /*xxx=*/1, /*yyy=*/2);
+  // CHECK-MESSAGES: [[@LINE-1]]:13: warning: argument name 'zzZ' 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); }
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.hpp b/test/clang-tidy/misc-definitions-in-headers.hpp
new file mode 100644 (file)
index 0000000..1c9c96e
--- /dev/null
@@ -0,0 +1,165 @@
+// 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: function '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 <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: function '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.
+
+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;
+}
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..b427a18
--- /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-inaccurate-erase.cpp b/test/clang-tidy/misc-inaccurate-erase.cpp
new file mode 100644 (file)
index 0000000..4c75003
--- /dev/null
@@ -0,0 +1,76 @@
+// 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 T> struct vector {
+  typedef vec_iterator<T> iterator;
+
+  iterator begin();
+  iterator end();
+
+  void erase(iterator);
+  void erase(iterator, 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());
+
+  // 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-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.cpp b/test/clang-tidy/misc-misplaced-widening-cast.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..d39f725
--- /dev/null
@@ -0,0 +1,160 @@
+// 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 a trivially-copyable type 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 of a trivially-copyable type
+  // CHECK-FIXES: return x2;
+}
+
+int *f3(int *x3) {
+  return std::move(x3);
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: std::move of the variable of a trivially-copyable type
+  // 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
+  // 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
+  // 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
+  // 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); });
+}
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..55d309e
--- /dev/null
@@ -0,0 +1,140 @@
+// RUN: %check_clang_tidy %s misc-move-constructor-init %t -- -- -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: 23:3: note: copy constructor being called
+  // CHECK-MESSAGES: 24: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 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]]:28: warning: value argument 'M' can be moved to avoid copy [misc-move-constructor-init]
+  // 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 {
+  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-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..289cc13
--- /dev/null
@@ -0,0 +1,44 @@
+// 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;
+};
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..98e52b9
--- /dev/null
@@ -0,0 +1,43 @@
+// RUN: %check_clang_tidy %s misc-non-copyable-objects %t\r
+\r
+typedef struct FILE {} FILE;\r
+typedef struct pthread_cond_t {} pthread_cond_t;\r
+typedef int pthread_mutex_t;\r
+\r
+// CHECK-MESSAGES: :[[@LINE+1]]:13: warning: 'f' declared as type 'FILE', which is unsafe to copy; did you mean 'FILE *'? [misc-non-copyable-objects]\r
+void g(FILE f);\r
+// CHECK-MESSAGES: :[[@LINE+1]]:24: warning: 'm' declared as type 'pthread_mutex_t', which is unsafe to copy; did you mean 'pthread_mutex_t *'?\r
+void h(pthread_mutex_t m);\r
+// CHECK-MESSAGES: :[[@LINE+1]]:23: warning: 'c' declared as type 'pthread_cond_t', which is unsafe to copy; did you mean 'pthread_cond_t *'?\r
+void i(pthread_cond_t c);\r
+\r
+struct S {\r
+  pthread_cond_t c; // ok\r
+  // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: 'f' declared as type 'FILE', which is unsafe to copy; did you mean 'FILE *'?\r
+  FILE f;\r
+};\r
+\r
+void func(FILE *f) {\r
+  // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: 'f1' declared as type 'FILE', which is unsafe to copy; did you mean 'FILE *'?\r
+  FILE f1; // match\r
+  // CHECK-MESSAGES: :[[@LINE+2]]:8: warning: 'f2' declared as type 'FILE', which is unsafe to copy; did you mean 'FILE *'?\r
+  // CHECK-MESSAGES: :[[@LINE+1]]:13: warning: expression has opaque data structure type 'FILE'; type should only be used as a pointer and not dereferenced\r
+  FILE f2 = *f;\r
+  // CHECK-MESSAGES: :[[@LINE+1]]:15: warning: 'f3' declared as type 'FILE', which is unsafe to copy; did you mean 'FILE *'?\r
+  struct FILE f3;\r
+  // CHECK-MESSAGES: :[[@LINE+1]]:16: warning: expression has opaque data structure type 'FILE'; type should only be used as a pointer and not dereferenced\r
+  (void)sizeof(*f);\r
+  (void)sizeof(FILE);\r
+  // CHECK-MESSAGES: :[[@LINE+1]]:5: warning: expression has opaque data structure type 'FILE'; type should only be used as a pointer and not dereferenced\r
+  g(*f);\r
+\r
+  pthread_mutex_t m; // ok\r
+  h(m); // ok\r
+\r
+  pthread_cond_t c; // ok\r
+  i(c); // ok\r
+\r
+  pthread_mutex_t *m1 = &m; // ok\r
+  // 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\r
+  h(*m1);\r
+}
\ No newline at end of file
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-pointer-and-integral-operation-cxx98.cpp b/test/clang-tidy/misc-pointer-and-integral-operation-cxx98.cpp
new file mode 100644 (file)
index 0000000..4834a46
--- /dev/null
@@ -0,0 +1,45 @@
+// RUN: %check_clang_tidy %s misc-pointer-and-integral-operation %t -- -- -std=c++98
+
+bool* pb;
+char* pc;
+int* pi;
+
+int Test() {
+  pb = false;
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: suspicious assignment from bool to pointer [misc-pointer-and-integral-operation]
+  pc = '\0';
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: suspicious assignment from char to pointer
+
+  pb = (false?false:false);
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: suspicious assignment from bool to pointer
+  pb = (4 != 5?false:false);
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: suspicious assignment from bool to pointer
+
+  if (pb < false) return 0;
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious operation between pointer and bool literal
+  if (pb != false) return 0;
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious operation between pointer and bool literal
+  if (pc < '\0') return 0;
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious operation between pointer and character literal
+  if (pc != '\0') return 0;
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious operation between pointer and character literal
+  if (pi < '\0') return 0;
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious operation between pointer and character literal
+  if (pi != '\0') return 0;
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious operation between pointer and character literal
+
+  return 1;
+}
+
+int Valid() {
+  *pb = false;
+  *pc = '\0';
+
+  pb += 0;
+  pc += 0;
+  pi += 0;
+
+  pb += (pb != 0);
+  pc += (pc != 0);
+  pi += (pi != 0);
+}
diff --git a/test/clang-tidy/misc-pointer-and-integral-operation.cpp b/test/clang-tidy/misc-pointer-and-integral-operation.cpp
new file mode 100644 (file)
index 0000000..6591f0a
--- /dev/null
@@ -0,0 +1,30 @@
+// RUN: %check_clang_tidy %s misc-pointer-and-integral-operation %t
+
+bool* pb;
+char* pc;
+int* pi;
+
+int Test() {
+  if (pi < 0) return 0;
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious comparison of pointer with zero [misc-pointer-and-integral-operation]
+  if (pi <= 0) return 0;
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious comparison of pointer with zero
+
+  if (nullptr <= pb) return 0;
+  // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: suspicious comparison of pointer with null
+  if (pc < nullptr) return 0;
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious comparison of pointer with null
+  if (pi > nullptr) return 0;
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: suspicious comparison of pointer with null
+
+  return 1;
+}
+
+int Valid() {
+  *pb = false;
+  *pc = '\0';
+
+  pi += (pi != nullptr);
+  pi -= (pi == nullptr);
+  pc += (pb != nullptr);
+}
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..b8e6d58
--- /dev/null
@@ -0,0 +1,485 @@
+// 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 < 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
+
+  // 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..de315e3
--- /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..0fca945
--- /dev/null
@@ -0,0 +1,27 @@
+// RUN: %check_clang_tidy %s misc-static-assert %t -- -- -std=c11\r
+// RUN: clang-tidy %s -checks=-*,misc-static-assert -- -std=c99 | count 0\r
+\r
+void abort() {}\r
+#ifdef NDEBUG\r
+#define assert(x) 1\r
+#else\r
+#define assert(x)                                                              \\r
+  if (!(x))                                                                    \\r
+  abort()\r
+#endif\r
+\r
+void f(void) {\r
+  int x = 1;\r
+  assert(x == 0);\r
+  // CHECK-FIXES: {{^  }}assert(x == 0);\r
+\r
+  #define static_assert(x, msg) _Static_assert(x, msg)\r
+  assert(11 == 5 + 6);\r
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found assert() that could be\r
+  // CHECK-FIXES: {{^  }}static_assert(11 == 5 + 6, "");\r
+  #undef static_assert\r
+\r
+  assert(10 == 5 + 5);\r
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: found assert() that could be\r
+  // CHECK-FIXES: {{^  }}static_assert(10 == 5 + 5, "");\r
+}\r
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..2a645d5
--- /dev/null
@@ -0,0 +1,140 @@
+// 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(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-constructor.cpp b/test/clang-tidy/misc-string-constructor.cpp
new file mode 100644 (file)
index 0000000..f6ed7eb
--- /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-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..12ee464
--- /dev/null
@@ -0,0 +1,12 @@
+// RUN: clang-tidy %s -checks="-*,misc-suspicious-semicolon" -- 2>&1 | FileCheck %s
+
+// 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-NOT: [misc-suspicious-semicolon]
+  int a
+}
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..46f0d60
--- /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..4bf4f33
--- /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 a pointer value; should throw a non-pointer value and 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..7ad9610
--- /dev/null
@@ -0,0 +1,89 @@
+// RUN: %check_clang_tidy %s misc-unconventional-assign-operator %t -- -- -std=c++11 -isystem %S/Inputs/Headers
+
+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;
+  }
+};
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..e614f22
--- /dev/null
@@ -0,0 +1,23 @@
+// RUN: clang-tidy %s -checks=-*,misc-undelegated-constructor -- -std=c++98 | count 0\r
+\r
+// Note: this test expects no diagnostics, but FileCheck cannot handle that,\r
+// hence the use of | count 0.\r
+\r
+struct Ctor;\r
+Ctor foo();\r
+\r
+struct Ctor {\r
+  Ctor();\r
+  Ctor(int);\r
+  Ctor(int, int);\r
+  Ctor(Ctor *i) {\r
+    Ctor();\r
+    Ctor(0);\r
+    Ctor(1, 2);\r
+    foo();\r
+  }\r
+};\r
+\r
+Ctor::Ctor() {\r
+  Ctor(1);\r
+}\r
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.cpp b/test/clang-tidy/misc-unused-using-decls.cpp
new file mode 100644 (file)
index 0000000..618a9f6
--- /dev/null
@@ -0,0 +1,154 @@
+// RUN: %check_clang_tidy %s misc-unused-using-decls %t -- -- -fno-delayed-template-parsing
+
+// ----- 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;
+
+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
+
+// ----- 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;
+
+// ----- 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;
+}
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..40b5fc2
--- /dev/null
@@ -0,0 +1,70 @@
+// 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));
+}
+
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..e1588ab
--- /dev/null
@@ -0,0 +1,147 @@
+// 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>
+#include <complex.h>
+#include <ctype.h>
+#include <errno.h>
+#include <float.h>
+#include <inttypes.h>
+#include <iso646.h>
+#include <limits.h>
+#include <locale.h>
+#include <math.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <wchar.h>
+#include <wctype.h>
+
+// Headers deprecated since C++11: expect no diagnostics.
+#include <fenv.h>
+#include <stdalign.h>
+#include <stdbool.h>
+#include <tgmath.h>
+#include <uchar.h>
+
+// CHECK-MESSAGES: :[[@LINE-29]]:10: warning: inclusion of deprecated C++ header 'assert.h'; consider using 'cassert' instead [modernize-deprecated-headers]
+// CHECK-MESSAGES: :[[@LINE-29]]:10: warning: inclusion of deprecated C++ header 'complex.h'; consider using 'ccomplex' instead
+// CHECK-MESSAGES: :[[@LINE-29]]:10: warning: inclusion of deprecated C++ header 'ctype.h'; consider using 'cctype' instead
+// CHECK-MESSAGES: :[[@LINE-29]]:10: warning: inclusion of deprecated C++ header 'errno.h'; consider using 'cerrno' instead
+// CHECK-MESSAGES: :[[@LINE-29]]:10: warning: inclusion of deprecated C++ header 'float.h'; consider using 'cfloat' instead
+// CHECK-MESSAGES: :[[@LINE-29]]:10: warning: inclusion of deprecated C++ header 'inttypes.h'; consider using 'cinttypes' instead
+// CHECK-MESSAGES: :[[@LINE-29]]:10: warning: inclusion of deprecated C++ header 'iso646.h'; consider using 'ciso646' instead
+// CHECK-MESSAGES: :[[@LINE-29]]:10: warning: inclusion of deprecated C++ header 'limits.h'; consider using 'climits' instead
+// CHECK-MESSAGES: :[[@LINE-29]]:10: warning: inclusion of deprecated C++ header 'locale.h'; consider using 'clocale' instead
+// CHECK-MESSAGES: :[[@LINE-29]]:10: warning: inclusion of deprecated C++ header 'math.h'; consider using 'cmath' instead
+// CHECK-MESSAGES: :[[@LINE-29]]:10: warning: inclusion of deprecated C++ header 'setjmp.h'; consider using 'csetjmp' instead
+// CHECK-MESSAGES: :[[@LINE-29]]:10: warning: inclusion of deprecated C++ header 'signal.h'; consider using 'csignal' instead
+// CHECK-MESSAGES: :[[@LINE-29]]:10: warning: inclusion of deprecated C++ header 'stdarg.h'; consider using 'cstdarg' instead
+// CHECK-MESSAGES: :[[@LINE-29]]:10: warning: inclusion of deprecated C++ header 'stddef.h'; consider using 'cstddef' instead
+// CHECK-MESSAGES: :[[@LINE-29]]:10: warning: inclusion of deprecated C++ header 'stdint.h'; consider using 'cstdint' instead
+// CHECK-MESSAGES: :[[@LINE-29]]:10: warning: inclusion of deprecated C++ header 'stdio.h'; consider using 'cstdio' instead
+// CHECK-MESSAGES: :[[@LINE-29]]:10: warning: inclusion of deprecated C++ header 'stdlib.h'; consider using 'cstdlib' instead
+// CHECK-MESSAGES: :[[@LINE-29]]:10: warning: inclusion of deprecated C++ header 'string.h'; consider using 'cstring' instead
+// CHECK-MESSAGES: :[[@LINE-29]]:10: warning: inclusion of deprecated C++ header 'time.h'; consider using 'ctime' instead
+// CHECK-MESSAGES: :[[@LINE-29]]:10: warning: inclusion of deprecated C++ header 'wchar.h'; consider using 'cwchar' instead
+// CHECK-MESSAGES: :[[@LINE-29]]:10: warning: inclusion of deprecated C++ header 'wctype.h'; consider using 'cwctype' instead
+
+// CHECK-FIXES: #include <cassert>
+// CHECK-FIXES: #include <ccomplex>
+// CHECK-FIXES: #include <cctype>
+// CHECK-FIXES: #include <cerrno>
+// CHECK-FIXES: #include <cfloat>
+// CHECK-FIXES: #include <cinttypes>
+// CHECK-FIXES: #include <ciso646>
+// CHECK-FIXES: #include <climits>
+// CHECK-FIXES: #include <clocale>
+// CHECK-FIXES: #include <cmath>
+// CHECK-FIXES: #include <csetjmp>
+// CHECK-FIXES: #include <csignal>
+// CHECK-FIXES: #include <cstdarg>
+// CHECK-FIXES: #include <cstddef>
+// CHECK-FIXES: #include <cstdint>
+// CHECK-FIXES: #include <cstdio>
+// CHECK-FIXES: #include <cstdlib>
+// CHECK-FIXES: #include <cstring>
+// CHECK-FIXES: #include <ctime>
+// CHECK-FIXES: #include <cwchar>
+// CHECK-FIXES: #include <cwctype>
+
+#include "assert.h"
+#include "complex.h"
+#include "ctype.h"
+#include "errno.h"
+#include "float.h"
+#include "inttypes.h"
+#include "iso646.h"
+#include "limits.h"
+#include "locale.h"
+#include "math.h"
+#include "setjmp.h"
+#include "signal.h"
+#include "stdarg.h"
+#include "stddef.h"
+#include "stdint.h"
+#include "stdio.h"
+#include "stdlib.h"
+#include "string.h"
+#include "time.h"
+#include "wchar.h"
+#include "wctype.h"
+
+// Headers deprecated since C++11; expect no diagnostics
+#include "fenv.h"
+#include "stdalign.h"
+#include "stdbool.h"
+#include "tgmath.h"
+#include "uchar.h"
+
+// CHECK-MESSAGES: :[[@LINE-29]]:10: warning: inclusion of deprecated C++ header 'assert.h'; consider using 'cassert' instead
+// CHECK-MESSAGES: :[[@LINE-29]]:10: warning: inclusion of deprecated C++ header 'complex.h'; consider using 'ccomplex' instead
+// CHECK-MESSAGES: :[[@LINE-29]]:10: warning: inclusion of deprecated C++ header 'ctype.h'; consider using 'cctype' instead
+// CHECK-MESSAGES: :[[@LINE-29]]:10: warning: inclusion of deprecated C++ header 'errno.h'; consider using 'cerrno' instead
+// CHECK-MESSAGES: :[[@LINE-29]]:10: warning: inclusion of deprecated C++ header 'float.h'; consider using 'cfloat' instead
+// CHECK-MESSAGES: :[[@LINE-29]]:10: warning: inclusion of deprecated C++ header 'inttypes.h'; consider using 'cinttypes' instead
+// CHECK-MESSAGES: :[[@LINE-29]]:10: warning: inclusion of deprecated C++ header 'iso646.h'; consider using 'ciso646' instead
+// CHECK-MESSAGES: :[[@LINE-29]]:10: warning: inclusion of deprecated C++ header 'limits.h'; consider using 'climits' instead
+// CHECK-MESSAGES: :[[@LINE-29]]:10: warning: inclusion of deprecated C++ header 'locale.h'; consider using 'clocale' instead
+// CHECK-MESSAGES: :[[@LINE-29]]:10: warning: inclusion of deprecated C++ header 'math.h'; consider using 'cmath' instead
+// CHECK-MESSAGES: :[[@LINE-29]]:10: warning: inclusion of deprecated C++ header 'setjmp.h'; consider using 'csetjmp' instead
+// CHECK-MESSAGES: :[[@LINE-29]]:10: warning: inclusion of deprecated C++ header 'signal.h'; consider using 'csignal' instead
+// CHECK-MESSAGES: :[[@LINE-29]]:10: warning: inclusion of deprecated C++ header 'stdarg.h'; consider using 'cstdarg' instead
+// CHECK-MESSAGES: :[[@LINE-29]]:10: warning: inclusion of deprecated C++ header 'stddef.h'; consider using 'cstddef' instead
+// CHECK-MESSAGES: :[[@LINE-29]]:10: warning: inclusion of deprecated C++ header 'stdint.h'; consider using 'cstdint' instead
+// CHECK-MESSAGES: :[[@LINE-29]]:10: warning: inclusion of deprecated C++ header 'stdio.h'; consider using 'cstdio' instead
+// CHECK-MESSAGES: :[[@LINE-29]]:10: warning: inclusion of deprecated C++ header 'stdlib.h'; consider using 'cstdlib' instead
+// CHECK-MESSAGES: :[[@LINE-29]]:10: warning: inclusion of deprecated C++ header 'string.h'; consider using 'cstring' instead
+// CHECK-MESSAGES: :[[@LINE-29]]:10: warning: inclusion of deprecated C++ header 'time.h'; consider using 'ctime' instead
+// CHECK-MESSAGES: :[[@LINE-29]]:10: warning: inclusion of deprecated C++ header 'wchar.h'; consider using 'cwchar' instead
+// CHECK-MESSAGES: :[[@LINE-29]]:10: warning: inclusion of deprecated C++ header 'wctype.h'; consider using 'cwctype' instead
+
+// CHECK-FIXES: #include <cassert>
+// CHECK-FIXES: #include <ccomplex>
+// CHECK-FIXES: #include <cctype>
+// CHECK-FIXES: #include <cerrno>
+// CHECK-FIXES: #include <cfloat>
+// CHECK-FIXES: #include <cinttypes>
+// CHECK-FIXES: #include <ciso646>
+// CHECK-FIXES: #include <climits>
+// CHECK-FIXES: #include <clocale>
+// CHECK-FIXES: #include <cmath>
+// CHECK-FIXES: #include <csetjmp>
+// CHECK-FIXES: #include <csignal>
+// CHECK-FIXES: #include <cstdarg>
+// CHECK-FIXES: #include <cstddef>
+// CHECK-FIXES: #include <cstdint>
+// CHECK-FIXES: #include <cstdio>
+// CHECK-FIXES: #include <cstdlib>
+// CHECK-FIXES: #include <cstring>
+// CHECK-FIXES: #include <ctime>
+// CHECK-FIXES: #include <cwchar>
+// CHECK-FIXES: #include <cwctype>
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..d798880
--- /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>
+#include <complex.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fenv.h>
+#include <float.h>
+#include <inttypes.h>
+#include <iso646.h>
+#include <limits.h>
+#include <locale.h>
+#include <math.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <stdalign.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <tgmath.h>
+#include <time.h>
+#include <uchar.h>
+#include <wchar.h>
+#include <wctype.h>
+
+// CHECK-MESSAGES: :[[@LINE-27]]:10: warning: inclusion of deprecated C++ header 'assert.h'; consider using 'cassert' instead [modernize-deprecated-headers]
+// CHECK-MESSAGES: :[[@LINE-27]]:10: warning: inclusion of deprecated C++ header 'complex.h'; consider using 'ccomplex' instead
+// CHECK-MESSAGES: :[[@LINE-27]]:10: warning: inclusion of deprecated C++ header 'ctype.h'; consider using 'cctype' instead
+// CHECK-MESSAGES: :[[@LINE-27]]:10: warning: inclusion of deprecated C++ header 'errno.h'; consider using 'cerrno' instead
+// CHECK-MESSAGES: :[[@LINE-27]]:10: warning: inclusion of deprecated C++ header 'fenv.h'; consider using 'cfenv' instead
+// CHECK-MESSAGES: :[[@LINE-27]]:10: warning: inclusion of deprecated C++ header 'float.h'; consider using 'cfloat' instead
+// CHECK-MESSAGES: :[[@LINE-27]]:10: warning: inclusion of deprecated C++ header 'inttypes.h'; consider using 'cinttypes' instead
+// CHECK-MESSAGES: :[[@LINE-27]]:10: warning: inclusion of deprecated C++ header 'iso646.h'; consider using 'ciso646' instead
+// CHECK-MESSAGES: :[[@LINE-27]]:10: warning: inclusion of deprecated C++ header 'limits.h'; consider using 'climits' instead
+// CHECK-MESSAGES: :[[@LINE-27]]:10: warning: inclusion of deprecated C++ header 'locale.h'; consider using 'clocale' instead
+// CHECK-MESSAGES: :[[@LINE-27]]:10: warning: inclusion of deprecated C++ header 'math.h'; consider using 'cmath' instead
+// CHECK-MESSAGES: :[[@LINE-27]]:10: warning: inclusion of deprecated C++ header 'setjmp.h'; consider using 'csetjmp' instead
+// CHECK-MESSAGES: :[[@LINE-27]]:10: warning: inclusion of deprecated C++ header 'signal.h'; consider using 'csignal' instead
+// CHECK-MESSAGES: :[[@LINE-27]]:10: warning: inclusion of deprecated C++ header 'stdalign.h'; consider using 'cstdalign' instead
+// CHECK-MESSAGES: :[[@LINE-27]]:10: warning: inclusion of deprecated C++ header 'stdarg.h'; consider using 'cstdarg' instead
+// CHECK-MESSAGES: :[[@LINE-27]]:10: warning: inclusion of deprecated C++ header 'stdbool.h'; consider using 'cstdbool' instead
+// CHECK-MESSAGES: :[[@LINE-27]]:10: warning: inclusion of deprecated C++ header 'stddef.h'; consider using 'cstddef' instead
+// CHECK-MESSAGES: :[[@LINE-27]]:10: warning: inclusion of deprecated C++ header 'stdint.h'; consider using 'cstdint' instead
+// CHECK-MESSAGES: :[[@LINE-27]]:10: warning: inclusion of deprecated C++ header 'stdio.h'; consider using 'cstdio' instead
+// CHECK-MESSAGES: :[[@LINE-27]]:10: warning: inclusion of deprecated C++ header 'stdlib.h'; consider using 'cstdlib' instead
+// CHECK-MESSAGES: :[[@LINE-27]]:10: warning: inclusion of deprecated C++ header 'string.h'; consider using 'cstring' instead
+// CHECK-MESSAGES: :[[@LINE-27]]:10: warning: inclusion of deprecated C++ header 'tgmath.h'; consider using 'ctgmath' instead
+// CHECK-MESSAGES: :[[@LINE-27]]:10: warning: inclusion of deprecated C++ header 'time.h'; consider using 'ctime' instead
+// CHECK-MESSAGES: :[[@LINE-27]]:10: warning: inclusion of deprecated C++ header 'uchar.h'; consider using 'cuchar' instead
+// CHECK-MESSAGES: :[[@LINE-27]]:10: warning: inclusion of deprecated C++ header 'wchar.h'; consider using 'cwchar' instead
+// CHECK-MESSAGES: :[[@LINE-27]]:10: warning: inclusion of deprecated C++ header 'wctype.h'; consider using 'cwctype' instead
+
+// CHECK-FIXES: #include <cassert>
+// CHECK-FIXES: #include <ccomplex>
+// CHECK-FIXES: #include <cctype>
+// CHECK-FIXES: #include <cerrno>
+// CHECK-FIXES: #include <cfenv>
+// CHECK-FIXES: #include <cfloat>
+// CHECK-FIXES: #include <cinttypes>
+// CHECK-FIXES: #include <ciso646>
+// CHECK-FIXES: #include <climits>
+// CHECK-FIXES: #include <clocale>
+// CHECK-FIXES: #include <cmath>
+// CHECK-FIXES: #include <csetjmp>
+// CHECK-FIXES: #include <csignal>
+// CHECK-FIXES: #include <cstdalign>
+// CHECK-FIXES: #include <cstdarg>
+// CHECK-FIXES: #include <cstdbool>
+// CHECK-FIXES: #include <cstddef>
+// CHECK-FIXES: #include <cstdint>
+// CHECK-FIXES: #include <cstdio>
+// CHECK-FIXES: #include <cstdlib>
+// CHECK-FIXES: #include <cstring>
+// CHECK-FIXES: #include <ctgmath>
+// CHECK-FIXES: #include <ctime>
+// CHECK-FIXES: #include <cuchar>
+// CHECK-FIXES: #include <cwchar>
+// CHECK-FIXES: #include <cwctype>
+
+#include "assert.h"
+#include "complex.h"
+#include "ctype.h"
+#include "errno.h"
+#include "fenv.h"
+#include "float.h"
+#include "inttypes.h"
+#include "iso646.h"
+#include "limits.h"
+#include "locale.h"
+#include "math.h"
+#include "setjmp.h"
+#include "signal.h"
+#include "stdalign.h"
+#include "stdarg.h"
+#include "stdbool.h"
+#include "stddef.h"
+#include "stdint.h"
+#include "stdio.h"
+#include "stdlib.h"
+#include "string.h"
+#include "tgmath.h"
+#include "time.h"
+#include "uchar.h"
+#include "wchar.h"
+#include "wctype.h"
+
+// CHECK-MESSAGES: :[[@LINE-27]]:10: warning: inclusion of deprecated C++ header 'assert.h'; consider using 'cassert' instead
+// CHECK-MESSAGES: :[[@LINE-27]]:10: warning: inclusion of deprecated C++ header 'complex.h'; consider using 'ccomplex' instead
+// CHECK-MESSAGES: :[[@LINE-27]]:10: warning: inclusion of deprecated C++ header 'ctype.h'; consider using 'cctype' instead
+// CHECK-MESSAGES: :[[@LINE-27]]:10: warning: inclusion of deprecated C++ header 'errno.h'; consider using 'cerrno' instead
+// CHECK-MESSAGES: :[[@LINE-27]]:10: warning: inclusion of deprecated C++ header 'fenv.h'; consider using 'cfenv' instead
+// CHECK-MESSAGES: :[[@LINE-27]]:10: warning: inclusion of deprecated C++ header 'float.h'; consider using 'cfloat' instead
+// CHECK-MESSAGES: :[[@LINE-27]]:10: warning: inclusion of deprecated C++ header 'inttypes.h'; consider using 'cinttypes' instead
+// CHECK-MESSAGES: :[[@LINE-27]]:10: warning: inclusion of deprecated C++ header 'iso646.h'; consider using 'ciso646' instead
+// CHECK-MESSAGES: :[[@LINE-27]]:10: warning: inclusion of deprecated C++ header 'limits.h'; consider using 'climits' instead
+// CHECK-MESSAGES: :[[@LINE-27]]:10: warning: inclusion of deprecated C++ header 'locale.h'; consider using 'clocale' instead
+// CHECK-MESSAGES: :[[@LINE-27]]:10: warning: inclusion of deprecated C++ header 'math.h'; consider using 'cmath' instead
+// CHECK-MESSAGES: :[[@LINE-27]]:10: warning: inclusion of deprecated C++ header 'setjmp.h'; consider using 'csetjmp' instead
+// CHECK-MESSAGES: :[[@LINE-27]]:10: warning: inclusion of deprecated C++ header 'signal.h'; consider using 'csignal' instead
+// CHECK-MESSAGES: :[[@LINE-27]]:10: warning: inclusion of deprecated C++ header 'stdalign.h'; consider using 'cstdalign' instead
+// CHECK-MESSAGES: :[[@LINE-27]]:10: warning: inclusion of deprecated C++ header 'stdarg.h'; consider using 'cstdarg' instead
+// CHECK-MESSAGES: :[[@LINE-27]]:10: warning: inclusion of deprecated C++ header 'stdbool.h'; consider using 'cstdbool' instead
+// CHECK-MESSAGES: :[[@LINE-27]]:10: warning: inclusion of deprecated C++ header 'stddef.h'; consider using 'cstddef' instead
+// CHECK-MESSAGES: :[[@LINE-27]]:10: warning: inclusion of deprecated C++ header 'stdint.h'; consider using 'cstdint' instead
+// CHECK-MESSAGES: :[[@LINE-27]]:10: warning: inclusion of deprecated C++ header 'stdio.h'; consider using 'cstdio' instead
+// CHECK-MESSAGES: :[[@LINE-27]]:10: warning: inclusion of deprecated C++ header 'stdlib.h'; consider using 'cstdlib' instead
+// CHECK-MESSAGES: :[[@LINE-27]]:10: warning: inclusion of deprecated C++ header 'string.h'; consider using 'cstring' instead
+// CHECK-MESSAGES: :[[@LINE-27]]:10: warning: inclusion of deprecated C++ header 'tgmath.h'; consider using 'ctgmath' instead
+// CHECK-MESSAGES: :[[@LINE-27]]:10: warning: inclusion of deprecated C++ header 'time.h'; consider using 'ctime' instead
+// CHECK-MESSAGES: :[[@LINE-27]]:10: warning: inclusion of deprecated C++ header 'uchar.h'; consider using 'cuchar' instead
+// CHECK-MESSAGES: :[[@LINE-27]]:10: warning: inclusion of deprecated C++ header 'wchar.h'; consider using 'cwchar' instead
+// CHECK-MESSAGES: :[[@LINE-27]]:10: warning: inclusion of deprecated C++ header 'wctype.h'; consider using 'cwctype' instead
+
+// CHECK-FIXES: #include <cassert>
+// CHECK-FIXES: #include <ccomplex>
+// CHECK-FIXES: #include <cctype>
+// CHECK-FIXES: #include <cerrno>
+// CHECK-FIXES: #include <cfenv>
+// CHECK-FIXES: #include <cfloat>
+// CHECK-FIXES: #include <cinttypes>
+// CHECK-FIXES: #include <ciso646>
+// CHECK-FIXES: #include <climits>
+// CHECK-FIXES: #include <clocale>
+// CHECK-FIXES: #include <cmath>
+// CHECK-FIXES: #include <csetjmp>
+// CHECK-FIXES: #include <csignal>
+// CHECK-FIXES: #include <cstdalign>
+// CHECK-FIXES: #include <cstdarg>
+// CHECK-FIXES: #include <cstdbool>
+// CHECK-FIXES: #include <cstddef>
+// CHECK-FIXES: #include <cstdint>
+// CHECK-FIXES: #include <cstdio>
+// CHECK-FIXES: #include <cstdlib>
+// CHECK-FIXES: #include <cstring>
+// CHECK-FIXES: #include <ctgmath>
+// CHECK-FIXES: #include <ctime>
+// CHECK-FIXES: #include <cuchar>
+// CHECK-FIXES: #include <cwchar>
+// CHECK-FIXES: #include <cwctype>
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..7312b48
--- /dev/null
@@ -0,0 +1,1062 @@
+// 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
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.cpp b/test/clang-tidy/modernize-make-shared.cpp
new file mode 100644 (file)
index 0000000..ee4c686
--- /dev/null
@@ -0,0 +1,200 @@
+// RUN: %check_clang_tidy %s modernize-make-shared %t
+
+namespace std {
+
+template <typename type>
+class shared_ptr {
+public:
+  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);
+
+private:
+  type *ptr;
+};
+}
+
+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>();
+
+  // 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>();
+
+  // 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>();
+  }
+
+  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 than the type returned
+  // by the new operator.
+  auto Pderived = 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);
+
+  // Placemenet arguments should not be removed.
+  int *PInt = new int;
+  std::shared_ptr<int> Placement = std::shared_ptr<int>(new (PInt) int{3});
+}
+
+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);
+
+  // 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);
+
+  // 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});
+
+  // 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);
+}
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..8429554
--- /dev/null
@@ -0,0 +1,203 @@
+// RUN: %check_clang_tidy %s modernize-make-unique %t
+
+namespace std {
+
+template <typename T>
+class default_delete {};
+
+template <typename type, typename Deleter = std::default_delete<type>>
+class unique_ptr {
+public:
+  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);
+
+private:
+  type *ptr;
+};
+
+}
+
+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 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>();
+
+  // 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>();
+
+  // 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>();
+  }
+
+  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 than the type returned
+  // by the new operator.
+  auto Pderived = 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);
+
+  // Placemenet arguments should not be removed.
+  int *PInt = new int;
+  std::unique_ptr<int> Placement = std::unique_ptr<int>(new (PInt) int{3});
+}
+
+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);
+
+  // 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);
+
+  // 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});
+
+
+  // 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{});
+}
+
+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() {
+  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>();
+}
+
+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);
+}
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..91d8fce
--- /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: :5: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..660aaa4
--- /dev/null
@@ -0,0 +1,16 @@
+// 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 {
+};
+
+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.cpp b/test/clang-tidy/modernize-pass-by-value.cpp
new file mode 100644 (file)
index 0000000..bfb8530
--- /dev/null
@@ -0,0 +1,201 @@
+// 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 Movable {
+  int a, b, c;
+};
+
+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-FIXES: O(Movable M) : M(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-FIXES: R(ns_R::RMovable M) : M(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_;
+};
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..87be298
--- /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"}]}' -- -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.cpp b/test/clang-tidy/modernize-raw-string-literal.cpp
new file mode 100644 (file)
index 0000000..77123c0
--- /dev/null
@@ -0,0 +1,129 @@
+// RUN: %check_clang_tidy %s modernize-raw-string-literal %t
+
+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-shrink-to-fit.cpp b/test/clang-tidy/modernize-shrink-to-fit.cpp
new file mode 100644 (file)
index 0000000..1cbb0b4
--- /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-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..7ced0a4
--- /dev/null
@@ -0,0 +1,104 @@
+// 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();
+
+  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.cpp b/test/clang-tidy/modernize-use-bool-literals.cpp
new file mode 100644 (file)
index 0000000..8ba516d
--- /dev/null
@@ -0,0 +1,118 @@
+// RUN: %check_clang_tidy %s modernize-use-bool-literals %t
+
+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: {{.*}}
+// CHECK-FIXES: {{^}}bool IntToFalse(false);{{$}}
+
+bool LongLongToTrue{0x1LL};
+// CHECK-MESSAGES: :[[@LINE-1]]:21: warning: {{.*}}
+// CHECK-FIXES: {{^}}bool LongLongToTrue{true};{{$}}
+
+bool ExplicitCStyleIntToFalse = (bool)0;
+// CHECK-MESSAGES: :[[@LINE-1]]:33: warning: {{.*}}
+// CHECK-FIXES: {{^}}bool ExplicitCStyleIntToFalse = false;{{$}}
+
+bool ExplicitFunctionalIntToFalse = bool(0);
+// CHECK-MESSAGES: :[[@LINE-1]]:37: warning: {{.*}}
+// CHECK-FIXES: {{^}}bool ExplicitFunctionalIntToFalse = false;{{$}}
+
+bool ExplicitStaticIntToFalse = static_cast<bool>(0);
+// CHECK-MESSAGES: :[[@LINE-1]]:33: warning: {{.*}}
+// 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, use bool literal instead [modernize-use-bool-literals]
+// 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: {{.*}}
+// 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: {{.*}}
+// CHECK-FIXES: {{^}}bool MacroDependentBool = MACRO_DEPENDENT_CAST(0);{{$}}
+
+bool ManyMacrosDependent = MACRO_DEPENDENT_CAST(FALSE_MACRO);
+// CHECK-MESSAGES: :[[@LINE-1]]:49: warning: {{.*}}
+// CHECK-FIXES: {{^}}bool ManyMacrosDependent = MACRO_DEPENDENT_CAST(FALSE_MACRO);{{$}}
+
+class FooClass {
+  public:
+  FooClass() : JustBool(0) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: {{.*}}
+  // CHECK-FIXES: {{^ *}}FooClass() : JustBool(false) {}{{$}}
+  FooClass(int) : JustBool{0} {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:28: warning: {{.*}}
+  // CHECK-FIXES: {{^ *}}FooClass(int) : JustBool{false} {}{{$}}
+  private:
+  bool JustBool;
+  bool BoolWithBraces{0};
+  // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: {{.*}}
+  // CHECK-FIXES: {{^ *}}bool BoolWithBraces{false};{{$}}
+  bool BoolFromInt = 0;
+  // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: {{.*}}
+  // 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: {{.*}}
+  // CHECK-FIXES: {{^ *}}bool JustBool = false;{{$}}
+}
+
+int main() {
+  boolFunction(1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: {{.*}}
+  // CHECK-FIXES: {{^ *}}boolFunction(true);{{$}}
+
+  boolFunction(false);
+
+  templateFunction(0);
+
+  templateFunction(false);
+
+  valueDependentTemplateFunction<1>();
+
+  anotherTemplateFunction(1);
+
+  IntToTrue = 1;
+  // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: {{.*}}
+  // CHECK-FIXES: {{^ *}}IntToTrue = true;{{$}}
+}
diff --git a/test/clang-tidy/modernize-use-default-copy.cpp b/test/clang-tidy/modernize-use-default-copy.cpp
new file mode 100644 (file)
index 0000000..af7a1b8
--- /dev/null
@@ -0,0 +1,469 @@
+// RUN: %check_clang_tidy %s modernize-use-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]]:1: warning: use '= default' to define a trivial copy constructor [modernize-use-default]
+// CHECK-FIXES: OL::OL(const OL &Other) = default;
+OL &OL::operator=(const OL &Other) {
+  Field = Other.Field;
+  return *this;
+}
+// CHECK-MESSAGES: :[[@LINE-4]]:1: warning: use '= default' to define a trivial copy-assignment operator [modernize-use-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]]:3: 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) = 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) {}
+  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]]:1: 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) = 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]]:1: 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]]:1: 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]]:1: 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]]:1: 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]]:1: 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 */
+  }
+  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]]:1: 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]]:1: 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; }
diff --git a/test/clang-tidy/modernize-use-default-delayed.cpp b/test/clang-tidy/modernize-use-default-delayed.cpp
new file mode 100644 (file)
index 0000000..c7163e8
--- /dev/null
@@ -0,0 +1,8 @@
+// RUN: clang-tidy %s -checks=-*,modernize-use-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-default.cpp b/test/clang-tidy/modernize-use-default.cpp
new file mode 100644 (file)
index 0000000..79dc862
--- /dev/null
@@ -0,0 +1,159 @@
+// RUN: %check_clang_tidy %s modernize-use-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]]:1: warning: use '= default' to define a trivial default constructor [modernize-use-default]
+// CHECK-FIXES: OL::OL() = default;
+OL::~OL() {}
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use '= default' to define a trivial destructor [modernize-use-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;
+};
+
+// 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]]:3: warning: use '= default'
+  // CHECK-FIXES: explicit KW() = default;
+  virtual ~KW() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: 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;
+};
+
+// 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.
+  }
+  ~Comments() {
+    // Don't erase comments inside the body.
+  }
+};
+
+// Try-catch.
+struct ITC {
+  ITC() try {} catch(...) {}
+  ~ITC() try {} catch(...) {}
+};
+
+struct OTC {
+  OTC();
+  ~OTC();
+};
+OTC::OTC() try {} catch(...) {}
+OTC::~OTC() try {} catch(...) {}
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..a82a3a5
--- /dev/null
@@ -0,0 +1,424 @@
+// 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'}]}" -- -std=c++11
+
+namespace std {
+template <typename T>
+class vector {
+public:
+  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 T1, typename T2>
+class pair {
+public:
+  pair() = default;
+  pair(const pair &) = default;
+  pair(pair &&) = default;
+
+  pair(const T1 &, const T2 &) {}
+  pair(T1 &&, T2 &&) {}
+
+  template <class U1, class U2>
+  pair(const pair<U1, U2> &p){};
+  template <class U1, class U2>
+  pair(pair<U1, U2> &&p){};
+};
+
+template <typename T1, typename T2>
+pair<T1, T2> make_pair(T1, T2) {
+  return pair<T1, T2>();
+};
+
+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)));
+}
+
+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;
+  // FIXME: add functionality to change calls of std::make_pair
+  v.push_back(std::make_pair(1, 2));
+
+  // FIXME: This is not a bug, but call of make_pair should be removed in the
+  // future. This one matches because the return type of make_pair is different
+  // than the pair itself.
+  v.push_back(std::make_pair(42LL, 13));
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use emplace_back
+  // CHECK-FIXES: v.emplace_back(std::make_pair(42LL, 13));
+}
+
+void testOtherCointainers() {
+  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 delcarations 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);
+}
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..e1267bf
--- /dev/null
@@ -0,0 +1,219 @@
+// 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
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..1e39e37
--- /dev/null
@@ -0,0 +1,290 @@
+// RUN: %check_clang_tidy %s modernize-use-override %t
+
+#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 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();
+};
+
+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 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(); };
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..6cf08e0
--- /dev/null
@@ -0,0 +1,87 @@
+// 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 bla1, bla2, bla3;
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
+
+#define CODE typedef int INT
+
+CODE;
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'using' instead of 'typedef'
diff --git a/test/clang-tidy/nolint.cpp b/test/clang-tidy/nolint.cpp
new file mode 100644 (file)
index 0000000..980e904
--- /dev/null
@@ -0,0 +1,16 @@
+// RUN: %check_clang_tidy %s google-explicit-constructor,clang-diagnostic-unused-variable %t -- -extra-arg=-Wunused-variable --
+
+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
+}
+
+// CHECK-MESSAGES: Suppressed 3 warnings (3 NOLINT)
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-unnecessary-copy-initialization.cpp b/test/clang-tidy/performance-unnecessary-copy-initialization.cpp
new file mode 100644 (file)
index 0000000..b8c22ee
--- /dev/null
@@ -0,0 +1,370 @@
+// 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;
+}
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..a712305
--- /dev/null
@@ -0,0 +1,171 @@
+// 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'
+};
+
+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 NegativeValueCopyConstructor {
+  NegativeValueCopyConstructor(ExpensiveToCopyType Copy) {}
+};
+
+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.cpp b/test/clang-tidy/performance-unnecessary-value-param.cpp
new file mode 100644 (file)
index 0000000..e33f449
--- /dev/null
@@ -0,0 +1,239 @@
+// 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'
+};
+
+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 NegativeValueCopyConstructor {
+  NegativeValueCopyConstructor(ExpensiveToCopyType Copy) {}
+};
+
+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);
+}
+
+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;
+}
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..f009f9f
--- /dev/null
@@ -0,0 +1,96 @@
+// 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);
+
+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);
+};
+
+// 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) {}
+
+// 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-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..ee147f6
--- /dev/null
@@ -0,0 +1,194 @@
+// 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: }
+}
+
+#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..a3f6385
--- /dev/null
@@ -0,0 +1,151 @@
+// RUN: %check_clang_tidy %s readability-container-size-empty %t
+
+namespace std {
+template <typename T> struct vector {
+  vector();
+  unsigned long size() const;
+  bool empty() const;
+};
+
+template <typename T> struct basic_string {
+  basic_string();
+  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();
+  unsigned long size() const;
+  bool empty() const;
+};
+}
+
+
+}
+
+int main() {
+  std::set<int> intSet;
+  std::string str;
+  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()){{$}}
+  if (str.size() == 0)
+    ;
+  // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: the 'empty' method should be used
+  // CHECK-FIXES: {{^  }}if (str.empty()){{$}}
+  if (wstr.size() == 0)
+    ;
+  // 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.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]]: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 (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()){{$}}
+
+  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()){{$}}
+}
+
+#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
+  // 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);
+}
+
+void g() {
+  f<int>();
+  f<double>();
+  f<char *>();
+}
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..28fd2ec
--- /dev/null
@@ -0,0 +1,34 @@
+// RUN: %check_clang_tidy %s readability-else-after-return %t
+
+void f(int a) {
+  if (a > 0)
+    return;
+  else // comment
+// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: don't use else after return
+// CHECK-FIXES: {{^}}  // comment
+    return;
+
+  if (a > 0) {
+    return;
+  } else { // comment
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: don't use else after return
+// CHECK-FIXES:  } // comment
+    return;
+  }
+
+  if (a > 0) {
+    f(0);
+    if (a > 10)
+      return;
+  } else {
+    return;
+  }
+
+  if (a > 0)
+    f(0);
+  else if (a > 10)
+    return;
+  else
+    f(0);
+}
+
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..03c94bd
--- /dev/null
@@ -0,0 +1,54 @@
+// 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}]}' -- -std=c++11
+
+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 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)
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..3e7e0b3
--- /dev/null
@@ -0,0 +1,352 @@
+// 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: UPPER_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: lower_case}, \
+// 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
+
+int global;
+#define USE_IN_MACRO(m) auto use_##m = m
+USE_IN_MACRO(global);
+// NO warnings or fixes expected as global is used in a macro expansion
+
+#define BLA int FOO_bar
+BLA;
+// NO warnings or fixes expected as FOO_bar is from macro expansion
+
+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 {{{$}}
+    my_class();
+// CHECK-FIXES: {{^}}    CMyClass();{{$}}
+
+    ~
+      my_class();
+// (space in destructor token test, we could check trigraph but they will be deprecated)
+// CHECK-FIXES: {{^}}    ~{{$}}
+// CHECK-FIXES: {{^}}      CMyClass();{{$}}
+
+  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;{{$}}
+
+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;{{$}}
+};
+
+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>
+// 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..6ce5931
--- /dev/null
@@ -0,0 +1,55 @@
+// 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()) {}
+  int value1 = functionReturningInt() ? 1 : 2;
+  int value2 = ! functionReturningInt() ? 1 : 2;
+}
+
+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 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..11d84d9
--- /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 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 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 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..5573c9c
--- /dev/null
@@ -0,0 +1,433 @@
+// 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 boolean = true;
+
+  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 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-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-redundant-control-flow.cpp b/test/clang-tidy/readability-redundant-control-flow.cpp
new file mode 100644 (file)
index 0000000..09dec6f
--- /dev/null
@@ -0,0 +1,222 @@
+// 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: {{^ *}$}}
+
+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: {{^ *}$}}
+
+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-smartptr-get.cpp b/test/clang-tidy/readability-redundant-smartptr-get.cpp
new file mode 100644 (file)
index 0000000..61a36f6
--- /dev/null
@@ -0,0 +1,143 @@
+// 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();
+}
+
+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 = std::unique_ptr<int>().get() == NULL;
+  bb = ip.get() == nullptr;
+  bb = u->get() == NULL;
+}
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..1c31118
--- /dev/null
@@ -0,0 +1,43 @@
+// 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;
+};
+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);{{$}}
+}
+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..009168f
--- /dev/null
@@ -0,0 +1,203 @@
+// 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;
+
+  _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);{{$}}
+}
+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..1af7cfb
--- /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..08bd4b2
--- /dev/null
@@ -0,0 +1,17 @@
+// RUN: clang-tidy %s -checks='-*,clang-analyzer-*,-clang-analyzer-alpha*' -- | 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..29db69e
--- /dev/null
@@ -0,0 +1,13 @@
+// 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}}:'
+
+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-WARN-NOT: treated as
+// CHECK-WERR: 1 warning treated as error
diff --git a/test/clang-tidy/werrors-plural.cpp b/test/clang-tidy/werrors-plural.cpp
new file mode 100644 (file)
index 0000000..198e7b2
--- /dev/null
@@ -0,0 +1,18 @@
+// 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}}:'
+
+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]
+
+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-WARN-NOT: treated as
+// CHECK-WERR: 2 warnings treated as errors
diff --git a/test/clang-tidy/werrors.cpp b/test/clang-tidy/werrors.cpp
new file mode 100644 (file)
index 0000000..c98fd90
--- /dev/null
@@ -0,0 +1,10 @@
+// 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}}:'
+
+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-WARN-NOT: treated as
+// CHECK-WERR: 1 warning treated as error
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..a2f9913
--- /dev/null
@@ -0,0 +1,53 @@
+---
+Name:           foo
+Contexts:
+  - ContextType:     Namespace
+    ContextName:     a
+  - ContextType:     Namespace
+    ContextName:     b
+FilePath:        foo.h
+LineNumber:      1
+Type:            Class
+NumOccurrences:  1
+...
+---
+Name:           bar
+Contexts:
+  - ContextType:     Namespace
+    ContextName:     a
+  - ContextType:     Namespace
+    ContextName:     b
+FilePath:        ../include/bar.h
+LineNumber:      1
+Type:            Class
+NumOccurrences:  1
+...
+Name:           bar
+Contexts:
+  - ContextType:     Namespace
+    ContextName:     a
+  - ContextType:     Namespace
+    ContextName:     b
+FilePath:        ../include/bar.h
+LineNumber:      2
+Type:            Class
+NumOccurrences:  3
+...
+Name:           bar
+Contexts:
+  - ContextType:     Namespace
+    ContextName:     a
+  - ContextType:     Namespace
+    ContextName:     b
+FilePath:        ../include/zbar.h
+LineNumber:      1
+Type:            Class
+NumOccurrences:  3
+---
+Name:           b
+Contexts:
+FilePath:        var.h
+LineNumber:      1
+Type:            Variable
+NumOccurrences:  1
+...
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..8c8f64a
--- /dev/null
@@ -0,0 +1,20 @@
+---
+Name:           foo
+Contexts:
+  - ContextType:     Namespace
+    ContextName:     a
+FilePath:        foo.h
+LineNumber:      1
+Type:            Class
+NumOccurrences:  1
+...
+---
+Name:           bar
+Contexts:
+  - ContextType:     Namespace
+    ContextName:     a
+FilePath:        ../include/bar.h
+LineNumber:      1
+Type:            Class
+NumOccurrences:  1
+...
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..f5346b2
--- /dev/null
@@ -0,0 +1,20 @@
+---
+Name:           foo
+Contexts:
+  - ContextType:     Namespace
+    ContextName:     a
+FilePath:        foo.h
+LineNumber:      1
+Type:            Class
+NumOccurrences:  1
+...
+---
+Name:           bar
+Contexts:
+  - ContextType:     Namespace
+    ContextName:     a
+FilePath:        ../include/barbar.h
+LineNumber:      1
+Type:            Class
+NumOccurrences:  1
+...
diff --git a/test/include-fixer/commandline_options.cpp b/test/include-fixer/commandline_options.cpp
new file mode 100644 (file)
index 0000000..0abda21
--- /dev/null
@@ -0,0 +1,16 @@
+// REQUIRES: shell
+// 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='{SymbolIdentifier: 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='{SymbolIdentifier: 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='{SymbolIdentifier: 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..10a1af7
--- /dev/null
@@ -0,0 +1,11 @@
+// REQUIRES: shell
+// 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..41a889e
--- /dev/null
@@ -0,0 +1,9 @@
+// REQUIRES: shell
+// 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..31ae52e
--- /dev/null
@@ -0,0 +1,20 @@
+// REQUIRES: shell
+// 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..a2344fa
--- /dev/null
@@ -0,0 +1,34 @@
+# REQUIRES: shell
+# 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
+LineNumber:      1
+Type:            Class
+NumOccurrences:  1
+...
+---
+Name:            bar
+Contexts:        
+  - ContextType:     Namespace
+    ContextName:     a
+FilePath:        ../include/barbar.h
+LineNumber:      1
+Type:            Class
+NumOccurrences:  1
+...
+---
+Name:            foo
+Contexts:        
+  - ContextType:     Namespace
+    ContextName:     a
+FilePath:        foo.h
+LineNumber:      1
+Type:            Class
+NumOccurrences:  2
+...
diff --git a/test/include-fixer/prefix_variable.cpp b/test/include-fixer/prefix_variable.cpp
new file mode 100644 (file)
index 0000000..f2f565d
--- /dev/null
@@ -0,0 +1,11 @@
+// REQUIRES: shell
+// 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/ranking.cpp b/test/include-fixer/ranking.cpp
new file mode 100644 (file)
index 0000000..000f6a5
--- /dev/null
@@ -0,0 +1,10 @@
+// RUN: clang-include-fixer -db=yaml -input=%S/Inputs/fake_yaml_db.yaml -output-headers %s -- | FileCheck %s
+
+// CHECK:     "HeaderInfos": [
+// 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/yamldb.cpp b/test/include-fixer/yamldb.cpp
new file mode 100644 (file)
index 0000000..ae2bfb8
--- /dev/null
@@ -0,0 +1,9 @@
+// REQUIRES: shell
+// 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..9206668
--- /dev/null
@@ -0,0 +1,12 @@
+// REQUIRES: shell
+// 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/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..c79f4e1
--- /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-NEXT:   MacroNameTok: __STDC__
+// CHECK-NEXT:   MacroDirective: MD_Define
+// CHECK-NEXT: - Callback: MacroDefined
+// CHECK-NEXT:   MacroNameTok: __STDC_HOSTED__
+// CHECK-NEXT:   MacroDirective: MD_Define
+// CHECK-NEXT: - Callback: MacroDefined
+// CHECK-NEXT:   MacroNameTok: __cplusplus
+// CHECK-NEXT:   MacroDirective: MD_Define
+// CHECK-NEXT: - Callback: MacroDefined
+// CHECK-NEXT:   MacroNameTok: __STDC_UTF_16__
+// CHECK-NEXT:   MacroDirective: MD_Define
+// CHECK-NEXT: - Callback: MacroDefined
+// CHECK-NEXT:   MacroNameTok: __STDC_UTF_32__
+// CHECK-NEXT:   MacroDirective: MD_Define
+// CHECK-NEXT: - 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..63fa7aa
--- /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-NEXT:   MacroNameTok: __STDC__
+// CHECK-NEXT:   MacroDirective: MD_Define
+// CHECK-NEXT: - Callback: MacroDefined
+// CHECK-NEXT:   MacroNameTok: __STDC_HOSTED__
+// CHECK-NEXT:   MacroDirective: MD_Define
+// CHECK-NEXT: - Callback: MacroDefined
+// CHECK-NEXT:   MacroNameTok: __cplusplus
+// CHECK-NEXT:   MacroDirective: MD_Define
+// CHECK-NEXT: - Callback: MacroDefined
+// CHECK-NEXT:   MacroNameTok: __STDC_UTF_16__
+// CHECK-NEXT:   MacroDirective: MD_Define
+// CHECK-NEXT: - Callback: MacroDefined
+// CHECK-NEXT:   MacroNameTok: __STDC_UTF_32__
+// CHECK-NEXT:   MacroDirective: MD_Define
+// CHECK-NEXT: - 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..45ea223
--- /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-NEXT:   MacroNameTok: __STDC__
+// CHECK-NEXT:   MacroDirective: MD_Define
+// CHECK-NEXT: - Callback: MacroDefined
+// CHECK-NEXT:   MacroNameTok: __STDC_HOSTED__
+// CHECK-NEXT:   MacroDirective: MD_Define
+// CHECK-NEXT: - Callback: MacroDefined
+// CHECK-NEXT:   MacroNameTok: __cplusplus
+// CHECK-NEXT:   MacroDirective: MD_Define
+// CHECK-NEXT: - Callback: MacroDefined
+// CHECK-NEXT:   MacroNameTok: __STDC_UTF_16__
+// CHECK-NEXT:   MacroDirective: MD_Define
+// CHECK-NEXT: - Callback: MacroDefined
+// CHECK-NEXT:   MacroNameTok: __STDC_UTF_32__
+// CHECK-NEXT:   MacroDirective: MD_Define
+// CHECK-NEXT: - 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..020f1fd
--- /dev/null
@@ -0,0 +1,88 @@
+//===---- 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/ASTMatchers.h"
+#include "clang/ASTMatchers/ASTMatchFinder.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(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:
+  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..aa56f04
--- /dev/null
@@ -0,0 +1,12 @@
+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(clang-apply-replacements)
+add_subdirectory(clang-rename)
+add_subdirectory(clang-query)
+add_subdirectory(clang-tidy)
+add_subdirectory(include-fixer)
diff --git a/unittests/clang-apply-replacements/CMakeLists.txt b/unittests/clang-apply-replacements/CMakeLists.txt
new file mode 100644 (file)
index 0000000..3f8fa70
--- /dev/null
@@ -0,0 +1,17 @@
+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
+  ReformattingTest.cpp
+  )
+
+target_link_libraries(ClangApplyReplacementsTests
+  clangApplyReplacements
+  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-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-rename/CMakeLists.txt b/unittests/clang-rename/CMakeLists.txt
new file mode 100644 (file)
index 0000000..ff7e0f9
--- /dev/null
@@ -0,0 +1,23 @@
+set(LLVM_LINK_COMPONENTS
+  support
+  )
+
+get_filename_component(CLANG_RENAME_SOURCE_DIR
+  ${CMAKE_CURRENT_SOURCE_DIR}/../../clang-rename REALPATH)
+include_directories(
+  ${CLANG_RENAME_SOURCE_DIR}
+  )
+
+add_extra_unittest(ClangRenameTests
+  USRLocFindingTest.cpp
+  ${CLANG_RENAME_SOURCE_DIR}/USRFinder.cpp
+  ${CLANG_RENAME_SOURCE_DIR}/USRFindingAction.cpp
+  )
+
+target_link_libraries(ClangRenameTests
+  clangAST
+  clangBasic
+  clangFrontend
+  clangIndex
+  clangTooling
+  )
diff --git a/unittests/clang-rename/USRLocFindingTest.cpp b/unittests/clang-rename/USRLocFindingTest.cpp
new file mode 100644 (file)
index 0000000..f216119
--- /dev/null
@@ -0,0 +1,83 @@
+#include "USRFindingAction.h"
+#include "clang/Tooling/Tooling.h"
+#include "gtest/gtest.h"
+#include <map>
+#include <set>
+#include <vector>
+
+namespace clang {
+namespace rename {
+namespace test {
+
+// Determines if the symbol group invariants hold. To recap, those invariants
+// are:
+//  (1) All symbols in the same symbol group share the same USR.
+//  (2) Two symbols from two different groups do not share the same USR.
+static void testOffsetGroups(const char *Code,
+                             const std::vector<std::vector<unsigned>> Groups) {
+  std::set<std::string> AllUSRs, CurrUSR;
+
+  for (const auto &Group : Groups) {
+    // Groups the invariants do not hold then the value of USR is also invalid,
+    // but at that point the test has already failed and USR ceases to be
+    // useful.
+    std::string USR;
+    for (const auto &Offset : Group) {
+      USRFindingAction Action(Offset, std::string());
+      auto Factory = tooling::newFrontendActionFactory(&Action);
+      EXPECT_TRUE(tooling::runToolOnCode(Factory->create(), Code));
+      const auto &USRs = Action.getUSRs();
+      EXPECT_EQ(1u, USRs.size());
+      USR = USRs[0];
+      CurrUSR.insert(USR);
+    }
+    EXPECT_EQ(1u, CurrUSR.size());
+    CurrUSR.clear();
+    AllUSRs.insert(USR);
+  }
+
+  EXPECT_EQ(Groups.size(), AllUSRs.size());
+}
+
+
+TEST(USRLocFinding, FindsVarUSR) {
+  const char VarTest[] = "\n\
+namespace A {\n\
+int foo;\n\
+}\n\
+int foo;\n\
+int bar = foo;\n\
+int baz = A::foo;\n\
+void fun1() {\n\
+  struct {\n\
+    int foo;\n\
+  } b = { 100 };\n\
+  int foo = 100;\n\
+  baz = foo;\n\
+  {\n\
+    extern int foo;\n\
+    baz = foo;\n\
+    foo = A::foo + baz;\n\
+    A::foo = b.foo;\n\
+  }\n\
+ foo = b.foo;\n\
+}\n";
+  std::vector<std::vector<unsigned>> VarTestOffsets(3);
+  VarTestOffsets[0].push_back(19);
+  VarTestOffsets[0].push_back(63);
+  VarTestOffsets[0].push_back(205);
+  VarTestOffsets[0].push_back(223);
+  VarTestOffsets[1].push_back(30);
+  VarTestOffsets[1].push_back(45);
+  VarTestOffsets[1].push_back(172);
+  VarTestOffsets[1].push_back(187);
+  VarTestOffsets[2].push_back(129);
+  VarTestOffsets[2].push_back(148);
+  VarTestOffsets[2].push_back(242);
+
+  testOffsetGroups(VarTest, VarTestOffsets);
+}
+
+} // namespace test
+} // namespace rename
+} // namespace clang
diff --git a/unittests/clang-tidy/CMakeLists.txt b/unittests/clang-tidy/CMakeLists.txt
new file mode 100644 (file)
index 0000000..1e590d4
--- /dev/null
@@ -0,0 +1,33 @@
+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
+  OverlappingReplacementsTest.cpp
+  ReadabilityModuleTest.cpp)
+
+target_link_libraries(ClangTidyTests
+  clangAST
+  clangASTMatchers
+  clangBasic
+  clangFrontend
+  clangLex
+  clangTidy
+  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..621dc5e
--- /dev/null
@@ -0,0 +1,87 @@
+#include "ClangTidy.h"
+#include "ClangTidyTest.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace tidy {
+namespace test {
+
+class TestCheck : public ClangTidyCheck {
+public:
+  TestCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override {
+    Finder->addMatcher(ast_matchers::varDecl().bind("var"), this);
+  }
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override {
+    const VarDecl *Var = Result.Nodes.getNodeAs<VarDecl>("var");
+    // Add diagnostics in the wrong order.
+    diag(Var->getLocation(), "variable");
+    diag(Var->getTypeSpecStartLoc(), "type specifier");
+  }
+};
+
+TEST(ClangTidyDiagnosticConsumer, SortsErrors) {
+  std::vector<ClangTidyError> Errors;
+  runCheckOnCode<TestCheck>("int a;", &Errors);
+  EXPECT_EQ(2ul, Errors.size());
+  EXPECT_EQ("type specifier", Errors[0].Message.Message);
+  EXPECT_EQ("variable", Errors[1].Message.Message);
+}
+
+TEST(GlobList, Empty) {
+  GlobList Filter("");
+
+  EXPECT_TRUE(Filter.contains(""));
+  EXPECT_FALSE(Filter.contains("aaa"));
+}
+
+TEST(GlobList, Nothing) {
+  GlobList Filter("-*");
+
+  EXPECT_FALSE(Filter.contains(""));
+  EXPECT_FALSE(Filter.contains("a"));
+  EXPECT_FALSE(Filter.contains("-*"));
+  EXPECT_FALSE(Filter.contains("-"));
+  EXPECT_FALSE(Filter.contains("*"));
+}
+
+TEST(GlobList, Everything) {
+  GlobList Filter("*");
+
+  EXPECT_TRUE(Filter.contains(""));
+  EXPECT_TRUE(Filter.contains("aaaa"));
+  EXPECT_TRUE(Filter.contains("-*"));
+  EXPECT_TRUE(Filter.contains("-"));
+  EXPECT_TRUE(Filter.contains("*"));
+}
+
+TEST(GlobList, Simple) {
+  GlobList Filter("aaa");
+
+  EXPECT_TRUE(Filter.contains("aaa"));
+  EXPECT_FALSE(Filter.contains(""));
+  EXPECT_FALSE(Filter.contains("aa"));
+  EXPECT_FALSE(Filter.contains("aaaa"));
+  EXPECT_FALSE(Filter.contains("bbb"));
+}
+
+TEST(GlobList, Complex) {
+  GlobList Filter("*,-a.*,-b.*,a.1.*,-a.1.A.*,-..,-...,-..+,-*$,-*qwe*");
+
+  EXPECT_TRUE(Filter.contains("aaa"));
+  EXPECT_TRUE(Filter.contains("qqq"));
+  EXPECT_FALSE(Filter.contains("a."));
+  EXPECT_FALSE(Filter.contains("a.b"));
+  EXPECT_FALSE(Filter.contains("b."));
+  EXPECT_FALSE(Filter.contains("b.b"));
+  EXPECT_TRUE(Filter.contains("a.1.b"));
+  EXPECT_FALSE(Filter.contains("a.1.A.a"));
+  EXPECT_FALSE(Filter.contains("qwe"));
+  EXPECT_FALSE(Filter.contains("asdfqweasdf"));
+  EXPECT_TRUE(Filter.contains("asdfqwEasdf"));
+}
+
+} // namespace test
+} // namespace tidy
+} // namespace clang
diff --git a/unittests/clang-tidy/ClangTidyOptionsTest.cpp b/unittests/clang-tidy/ClangTidyOptionsTest.cpp
new file mode 100644 (file)
index 0000000..5a7da5a
--- /dev/null
@@ -0,0 +1,71 @@
+#include "ClangTidyOptions.h"
+#include "gtest/gtest.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);
+}
+
+} // 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..b972546
--- /dev/null
@@ -0,0 +1,141 @@
+//===--- 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())
+    Fixes.insert(Error.Fix.begin(), Error.Fix.end());
+  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..928d1ce
--- /dev/null
@@ -0,0 +1,529 @@
+//===---- 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.getStmtAs<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 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
+                                      {"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"));
+}
+
+} // 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..1d7fb24
--- /dev/null
@@ -0,0 +1,189 @@
+#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,
+                                       unsigned ExpectedWarnings) {
+  std::vector<ClangTidyError> Errors;
+  std::string Result = test::runCheckOnCode<LLVMHeaderGuardCheck>(
+      Code, &Errors, Filename, std::string("-xc++-header"));
+  return Errors.size() == ExpectedWarnings ? Result : "invalid error count";
+}
+
+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,
+                                                unsigned ExpectedWarnings) {
+  std::vector<ClangTidyError> Errors;
+  std::string Result = test::runCheckOnCode<WithEndifComment>(
+      Code, &Errors, Filename, std::string("-xc++-header"));
+  return Errors.size() == ExpectedWarnings ? Result : "invalid error count";
+}
+
+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",
+                                /*ExpectedWarnings=*/1));
+
+  // 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",
+                                /*ExpectedWarnings=*/0));
+
+  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",
+                                /*ExpectedWarnings=*/1));
+
+  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",
+                                /*ExpectedWarnings=*/1));
+
+  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",
+                                /*ExpectedWarnings=*/1));
+
+  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", /*ExpectedWarnings=*/1));
+
+  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", /*ExpectedWarnings=*/1));
+
+  // 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",
+                                /*ExpectedWarnings=*/1));
+
+  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",
+                                         /*ExpectedWarnings=*/1));
+
+  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",
+                                         /*ExpectedWarnings=*/1));
+
+  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",
+                                         /*ExpectedWarnings=*/0));
+
+  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",
+                                         /*ExpectedWarnings=*/0));
+
+  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",
+                                         /*ExpectedWarnings=*/1));
+
+  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",
+                                         /*ExpectedWarnings=*/1));
+
+  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",
+                                         /*ExpectedWarnings=*/0));
+}
+#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..a42b2e5
--- /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(/*Xxx=*/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(/*Xxx=*/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/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..ba504a7
--- /dev/null
@@ -0,0 +1,485 @@
+#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(BracesAroundStatementsCheck, 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(BracesAroundStatementsCheck, 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(BracesAroundStatementsCheck, 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(BracesAroundStatementsCheck, 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(BracesAroundStatementsCheck, 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(BracesAroundStatementsCheck, 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(BracesAroundStatementsCheck, 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(BracesAroundStatementsCheck, 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(BracesAroundStatementsCheck, 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"
+                                                        "}"));
+}
+
+} // namespace test
+} // namespace tidy
+} // namespace clang
diff --git a/unittests/include-fixer/CMakeLists.txt b/unittests/include-fixer/CMakeLists.txt
new file mode 100644 (file)
index 0000000..f82a187
--- /dev/null
@@ -0,0 +1,29 @@
+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
+  )
+
+target_link_libraries(IncludeFixerTests
+  clangBasic
+  clangFormat
+  clangFrontend
+  clangIncludeFixer
+  clangRewrite
+  clangTooling
+  clangToolingCore
+  findAllSymbols
+  )
+
+add_subdirectory(find-all-symbols)
diff --git a/unittests/include-fixer/IncludeFixerTest.cpp b/unittests/include-fixer/IncludeFixerTest.cpp
new file mode 100644 (file)
index 0000000..b0f1d90
--- /dev/null
@@ -0,0 +1,280 @@
+//===-- 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;
+
+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<SymbolInfo> Symbols = {
+      SymbolInfo("string", SymbolInfo::SymbolKind::Class, "<string>", 1,
+                 {{SymbolInfo::ContextType::Namespace, "std"}}),
+      SymbolInfo("sting", SymbolInfo::SymbolKind::Class, "\"sting\"", 1,
+                 {{SymbolInfo::ContextType::Namespace, "std"}}),
+      SymbolInfo("foo", SymbolInfo::SymbolKind::Class, "\"dir/otherdir/qux.h\"",
+                 1, {{SymbolInfo::ContextType::Namespace, "b"},
+                     {SymbolInfo::ContextType::Namespace, "a"}}),
+      SymbolInfo("bar", SymbolInfo::SymbolKind::Class, "\"bar.h\"", 1,
+                 {{SymbolInfo::ContextType::Namespace, "b"},
+                  {SymbolInfo::ContextType::Namespace, "a"}}),
+      SymbolInfo("bar", SymbolInfo::SymbolKind::Class, "\"bar2.h\"", 1,
+                 {{SymbolInfo::ContextType::Namespace, "c"},
+                  {SymbolInfo::ContextType::Namespace, "a"}}),
+      SymbolInfo("Green", SymbolInfo::SymbolKind::Class, "\"color.h\"", 1,
+                 {{SymbolInfo::ContextType::EnumDecl, "Color"},
+                  {SymbolInfo::ContextType::Namespace, "b"},
+                  {SymbolInfo::ContextType::Namespace, "a"}}),
+      SymbolInfo("Vector", SymbolInfo::SymbolKind::Class, "\"Vector.h\"", 1,
+                 {{SymbolInfo::ContextType::Namespace, "__a"},
+                  {SymbolInfo::ContextType::Namespace, "a"}},
+                 /*num_occurrences=*/2),
+      SymbolInfo("Vector", SymbolInfo::SymbolKind::Class, "\"Vector.h\"", 2,
+                 {{SymbolInfo::ContextType::Namespace, "a"}},
+                 /*num_occurrences=*/1),
+  };
+  auto SymbolIndexMgr = llvm::make_unique<include_fixer::SymbolIndexManager>();
+  SymbolIndexMgr->addSymbolIndex(
+      llvm::make_unique<include_fixer::InMemorySymbolIndex>(Symbols));
+
+  IncludeFixerContext FixerContext;
+  IncludeFixerActionFactory Factory(*SymbolIndexMgr, FixerContext, "llvm");
+
+  std::string FakeFileName = "input.cc";
+  runOnCode(&Factory, Code, FakeFileName, ExtraArgs);
+  if (FixerContext.getHeaderInfos().empty())
+    return Code;
+  auto Replaces = clang::include_fixer::createInsertHeaderReplacements(
+      Code, FakeFileName, FixerContext.getHeaderInfos().front().Header);
+  EXPECT_TRUE(static_cast<bool>(Replaces))
+      << llvm::toString(Replaces.takeError()) << "\n";
+  if (!Replaces)
+    return "";
+  clang::RewriterTestContext Context;
+  clang::FileID ID = Context.createInMemoryFile(FakeFileName, Code);
+  Replaces->insert({FakeFileName, FixerContext.getSymbolRange().getOffset(),
+                    FixerContext.getSymbolRange().getLength(),
+                    FixerContext.getHeaderInfos().front().QualifiedName});
+  clang::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"));
+
+  // FIXME: the current version of include-fixer does not get this test case
+  // right - header should be inserted before definition.
+  EXPECT_EQ(
+      "// comment\n#include \"foo.h\"\nstd::string foo;\n"
+      "#include \"dir/bar.h\"\n#include <string>\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"));
+}
+
+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}"));
+}
+
+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 { b::bar b; }";
+  std::string Expected = "#include \"a.h\"\n"
+                         "#include \"bar.h\"\n"
+                         "#include \"foo.h\"\n"
+                         "\n"
+                         "namespace a { b::bar b; }";
+  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 \"bar2.h\"\nnamespace c {\na::c::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"));
+}
+
+} // 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..baa5dca
--- /dev/null
@@ -0,0 +1,459 @@
+//===-- 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/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 clang::find_all_symbols::SymbolReporter {
+public:
+  ~TestSymbolReporter() override {}
+
+  void reportSymbol(llvm::StringRef FileName,
+                    const SymbolInfo &Symbol) override {
+    Symbols.push_back(Symbol);
+  }
+
+  bool hasSymbol(const SymbolInfo &Symbol) const {
+    for (const auto &S : Symbols) {
+      if (S == Symbol)
+        return true;
+    }
+    return false;
+  }
+
+private:
+  std::vector<SymbolInfo> Symbols;
+};
+
+class FindAllSymbolsTest : public ::testing::Test {
+public:
+  bool hasSymbol(const SymbolInfo &Symbol) {
+    return Reporter.hasSymbol(Symbol);
+  }
+
+  bool runFindAllSymbols(StringRef Code) {
+    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, 2, {});
+    SymbolInfo IncSymbol("IncHeaderClass", SymbolInfo::SymbolKind::Class,
+                         TopHeader, 1, {});
+    InMemoryFileSystem->addFile(
+        IncHeader, 0, llvm::MemoryBuffer::getMemBuffer(IncHeaderCode));
+    InMemoryFileSystem->addFile(InternalHeader, 0,
+                                llvm::MemoryBuffer::getMemBuffer(InternalCode));
+
+    std::unique_ptr<clang::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(Code));
+
+    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, 1, {});
+    SymbolInfo DirtySymbol("ExtraInternal", SymbolInfo::SymbolKind::Class,
+                           CleanHeader, 2, {});
+#endif // _MSC_VER && __MINGW32__
+    InMemoryFileSystem->addFile(FileName, 0,
+                                llvm::MemoryBuffer::getMemBuffer(Content));
+    Invocation.run();
+    EXPECT_TRUE(hasSymbol(InternalSymbol));
+    EXPECT_TRUE(hasSymbol(IncSymbol));
+#if !defined(_MSC_VER) && !defined(__MINGW32__)
+    EXPECT_TRUE(hasSymbol(DirtySymbol));
+    EXPECT_TRUE(hasSymbol(DirtyMacro));
+#endif  // _MSC_VER && __MINGW32__
+    return true;
+  }
+
+protected:
+  TestSymbolReporter Reporter;
+};
+
+TEST_F(FindAllSymbolsTest, VariableSymbols) {
+  static const char Code[] = R"(
+      extern int xargc;
+      namespace na {
+      static bool SSSS = false;
+      namespace nb { const long long *XXXX; }
+      })";
+  runFindAllSymbols(Code);
+
+  SymbolInfo Symbol =
+      SymbolInfo("xargc", SymbolInfo::SymbolKind::Variable, HeaderName, 2, {});
+  EXPECT_TRUE(hasSymbol(Symbol));
+
+  Symbol = SymbolInfo("SSSS", SymbolInfo::SymbolKind::Variable, HeaderName, 4,
+                      {{SymbolInfo::ContextType::Namespace, "na"}});
+  EXPECT_TRUE(hasSymbol(Symbol));
+
+  Symbol = SymbolInfo("XXXX", SymbolInfo::SymbolKind::Variable, HeaderName, 5,
+                      {{SymbolInfo::ContextType::Namespace, "nb"},
+                       {SymbolInfo::ContextType::Namespace, "na"}});
+  EXPECT_TRUE(hasSymbol(Symbol));
+}
+
+TEST_F(FindAllSymbolsTest, ExternCSymbols) {
+  static const char Code[] = R"(
+      extern "C" {
+      int C_Func() { return 0; }
+      struct C_struct {
+        int Member;
+      };
+      })";
+  runFindAllSymbols(Code);
+
+  SymbolInfo Symbol =
+      SymbolInfo("C_Func", SymbolInfo::SymbolKind::Function, HeaderName, 3, {});
+  EXPECT_TRUE(hasSymbol(Symbol));
+
+  Symbol =
+      SymbolInfo("C_struct", SymbolInfo::SymbolKind::Class, HeaderName, 4, {});
+  EXPECT_TRUE(hasSymbol(Symbol));
+}
+
+TEST_F(FindAllSymbolsTest, CXXRecordSymbols) {
+  static const char Code[] = 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() {}
+      };
+      };  //
+      )";
+  runFindAllSymbols(Code);
+
+  SymbolInfo Symbol =
+      SymbolInfo("Glob", SymbolInfo::SymbolKind::Class, HeaderName, 2, {});
+  EXPECT_TRUE(hasSymbol(Symbol));
+
+  Symbol = SymbolInfo("A", SymbolInfo::SymbolKind::Class, HeaderName, 6,
+                      {{SymbolInfo::ContextType::Namespace, "na"}});
+  EXPECT_TRUE(hasSymbol(Symbol));
+
+  Symbol = SymbolInfo("AAA", SymbolInfo::SymbolKind::Class, HeaderName, 7,
+                      {{SymbolInfo::ContextType::Record, "A"},
+                       {SymbolInfo::ContextType::Namespace, "na"}});
+  EXPECT_FALSE(hasSymbol(Symbol));
+}
+
+TEST_F(FindAllSymbolsTest, CXXRecordSymbolsTemplate) {
+  static const char Code[] = R"(
+      template <typename T>
+      class 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> {};
+      )";
+  runFindAllSymbols(Code);
+
+  SymbolInfo Symbol =
+      SymbolInfo("T_TEMP", SymbolInfo::SymbolKind::Class, HeaderName, 3, {});
+  EXPECT_TRUE(hasSymbol(Symbol));
+}
+
+TEST_F(FindAllSymbolsTest, FunctionSymbols) {
+  static const char Code[] = 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";
+      )";
+  runFindAllSymbols(Code);
+
+  SymbolInfo Symbol =
+      SymbolInfo("gg", SymbolInfo::SymbolKind::Function, HeaderName, 3,
+                 {{SymbolInfo::ContextType::Namespace, "na"}});
+  EXPECT_TRUE(hasSymbol(Symbol));
+
+  Symbol = SymbolInfo("f", SymbolInfo::SymbolKind::Function, HeaderName, 4,
+                      {{SymbolInfo::ContextType::Namespace, "na"}});
+  EXPECT_TRUE(hasSymbol(Symbol));
+
+  Symbol = SymbolInfo("SSSFFF", SymbolInfo::SymbolKind::Function, HeaderName, 5,
+                      {{SymbolInfo::ContextType::Namespace, "na"}});
+  EXPECT_TRUE(hasSymbol(Symbol));
+
+  Symbol = SymbolInfo("fun", SymbolInfo::SymbolKind::Function, HeaderName, 10,
+                      {{SymbolInfo::ContextType::Namespace, "nb"},
+                       {SymbolInfo::ContextType::Namespace, "na"}});
+  EXPECT_TRUE(hasSymbol(Symbol));
+}
+
+TEST_F(FindAllSymbolsTest, NamespaceTest) {
+  static const char Code[] = R"(
+      int X1;
+      namespace { int X2; }
+      namespace { namespace { int X3; } }
+      namespace { namespace nb { int X4; } }
+      namespace na { inline namespace __1 { int X5; } }
+      )";
+  runFindAllSymbols(Code);
+
+  SymbolInfo Symbol =
+      SymbolInfo("X1", SymbolInfo::SymbolKind::Variable, HeaderName, 2, {});
+  EXPECT_TRUE(hasSymbol(Symbol));
+
+  Symbol = SymbolInfo("X2", SymbolInfo::SymbolKind::Variable, HeaderName, 3,
+                      {{SymbolInfo::ContextType::Namespace, ""}});
+  EXPECT_TRUE(hasSymbol(Symbol));
+
+  Symbol = SymbolInfo("X3", SymbolInfo::SymbolKind::Variable, HeaderName, 4,
+                      {{SymbolInfo::ContextType::Namespace, ""},
+                       {SymbolInfo::ContextType::Namespace, ""}});
+  EXPECT_TRUE(hasSymbol(Symbol));
+
+  Symbol = SymbolInfo("X4", SymbolInfo::SymbolKind::Variable, HeaderName, 5,
+                      {{SymbolInfo::ContextType::Namespace, "nb"},
+                       {SymbolInfo::ContextType::Namespace, ""}});
+  EXPECT_TRUE(hasSymbol(Symbol));
+
+  Symbol = SymbolInfo("X5", SymbolInfo::SymbolKind::Variable, HeaderName, 6,
+                      {{SymbolInfo::ContextType::Namespace, "na"}});
+  EXPECT_TRUE(hasSymbol(Symbol));
+}
+
+TEST_F(FindAllSymbolsTest, DecayedTypeTest) {
+  static const char Code[] = "void DecayedFunc(int x[], int y[10]) {}";
+  runFindAllSymbols(Code);
+  SymbolInfo Symbol = SymbolInfo(
+      "DecayedFunc", SymbolInfo::SymbolKind::Function, HeaderName, 1, {});
+  EXPECT_TRUE(hasSymbol(Symbol));
+}
+
+TEST_F(FindAllSymbolsTest, CTypedefTest) {
+  static const char Code[] = R"(
+      typedef unsigned size_t_;
+      typedef struct { int x; } X;
+      using XX = X;
+      )";
+  runFindAllSymbols(Code);
+
+  SymbolInfo Symbol = SymbolInfo("size_t_", SymbolInfo::SymbolKind::TypedefName,
+                                 HeaderName, 2, {});
+  EXPECT_TRUE(hasSymbol(Symbol));
+
+  Symbol =
+      SymbolInfo("X", SymbolInfo::SymbolKind::TypedefName, HeaderName, 3, {});
+  EXPECT_TRUE(hasSymbol(Symbol));
+
+  Symbol =
+      SymbolInfo("XX", SymbolInfo::SymbolKind::TypedefName, HeaderName, 4, {});
+  EXPECT_TRUE(hasSymbol(Symbol));
+}
+
+TEST_F(FindAllSymbolsTest, EnumTest) {
+  static const char Code[] = 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;
+      )";
+  runFindAllSymbols(Code);
+
+  SymbolInfo Symbol =
+      SymbolInfo("Glob_E", SymbolInfo::SymbolKind::EnumDecl, HeaderName, 2, {});
+  EXPECT_TRUE(hasSymbol(Symbol));
+
+  Symbol =
+      SymbolInfo("G1", SymbolInfo::SymbolKind::EnumConstantDecl, HeaderName, 2,
+                 {{SymbolInfo::ContextType::EnumDecl, "Glob_E"}});
+  EXPECT_TRUE(hasSymbol(Symbol));
+
+  Symbol =
+      SymbolInfo("G2", SymbolInfo::SymbolKind::EnumConstantDecl, HeaderName, 2,
+                 {{SymbolInfo::ContextType::EnumDecl, "Glob_E"}});
+  EXPECT_TRUE(hasSymbol(Symbol));
+
+  Symbol = SymbolInfo("Altitude", SymbolInfo::SymbolKind::EnumDecl, HeaderName,
+                      3, {});
+  EXPECT_TRUE(hasSymbol(Symbol));
+  Symbol =
+      SymbolInfo("high", SymbolInfo::SymbolKind::EnumConstantDecl, HeaderName,
+                 3, {{SymbolInfo::ContextType::EnumDecl, "Altitude"}});
+  EXPECT_FALSE(hasSymbol(Symbol));
+
+  Symbol = SymbolInfo("A1", SymbolInfo::SymbolKind::EnumConstantDecl,
+                      HeaderName, 4, {{SymbolInfo::ContextType::EnumDecl, ""}});
+  EXPECT_TRUE(hasSymbol(Symbol));
+  Symbol = SymbolInfo("A2", SymbolInfo::SymbolKind::EnumConstantDecl,
+                      HeaderName, 4, {{SymbolInfo::ContextType::EnumDecl, ""}});
+  EXPECT_TRUE(hasSymbol(Symbol));
+  Symbol = SymbolInfo("", SymbolInfo::SymbolKind::EnumDecl, HeaderName, 4, {});
+  EXPECT_FALSE(hasSymbol(Symbol));
+
+  Symbol = SymbolInfo("A_ENUM", SymbolInfo::SymbolKind::EnumDecl, HeaderName, 7,
+                      {{SymbolInfo::ContextType::Record, "A"}});
+  EXPECT_FALSE(hasSymbol(Symbol));
+
+  Symbol = SymbolInfo("X1", SymbolInfo::SymbolKind::EnumDecl, HeaderName, 7,
+                      {{SymbolInfo::ContextType::EnumDecl, "A_ENUM"},
+                       {SymbolInfo::ContextType::Record, "A"}});
+  EXPECT_FALSE(hasSymbol(Symbol));
+
+  Symbol =
+      SymbolInfo("DECL", SymbolInfo::SymbolKind::EnumDecl, HeaderName, 9, {});
+  EXPECT_FALSE(hasSymbol(Symbol));
+}
+
+TEST_F(FindAllSymbolsTest, IWYUPrivatePragmaTest) {
+  static const char Code[] = R"(
+    // IWYU pragma: private, include "bar.h"
+    struct Bar {
+    };
+  )";
+  runFindAllSymbols(Code);
+
+  SymbolInfo Symbol =
+      SymbolInfo("Bar", SymbolInfo::SymbolKind::Class, "bar.h", 3, {});
+  EXPECT_TRUE(hasSymbol(Symbol));
+}
+
+TEST_F(FindAllSymbolsTest, MacroTest) {
+  static const char Code[] = R"(
+    #define X
+    #define Y 1
+    #define MAX(X, Y) ((X) > (Y) ? (X) : (Y))
+  )";
+  runFindAllSymbols(Code);
+  SymbolInfo Symbol =
+      SymbolInfo("X", SymbolInfo::SymbolKind::Macro, HeaderName, 2, {});
+  EXPECT_TRUE(hasSymbol(Symbol));
+
+  Symbol = SymbolInfo("Y", SymbolInfo::SymbolKind::Macro, HeaderName, 3, {});
+  EXPECT_TRUE(hasSymbol(Symbol));
+
+  Symbol = SymbolInfo("MAX", SymbolInfo::SymbolKind::Macro, HeaderName, 4, {});
+  EXPECT_TRUE(hasSymbol(Symbol));
+}
+
+TEST_F(FindAllSymbolsTest, MacroTestWithIWYU) {
+  static const char Code[] = R"(
+    // IWYU pragma: private, include "bar.h"
+    #define X
+    #define Y 1
+    #define MAX(X, Y) ((X) > (Y) ? (X) : (Y))
+  )";
+  runFindAllSymbols(Code);
+  SymbolInfo Symbol =
+      SymbolInfo("X", SymbolInfo::SymbolKind::Macro, "bar.h", 3, {});
+  EXPECT_TRUE(hasSymbol(Symbol));
+
+  Symbol = SymbolInfo("Y", SymbolInfo::SymbolKind::Macro, "bar.h", 4, {});
+  EXPECT_TRUE(hasSymbol(Symbol));
+
+  Symbol = SymbolInfo("MAX", SymbolInfo::SymbolKind::Macro, "bar.h", 5, {});
+  EXPECT_TRUE(hasSymbol(Symbol));
+}
+
+TEST_F(FindAllSymbolsTest, NoFriendTest) {
+  static const char Code[] = R"(
+    class WorstFriend {
+      friend void Friend();
+      friend class BestFriend;
+    };
+  )";
+  runFindAllSymbols(Code);
+  SymbolInfo Symbol = SymbolInfo("WorstFriend", SymbolInfo::SymbolKind::Class,
+                                 HeaderName, 2, {});
+  EXPECT_TRUE(hasSymbol(Symbol));
+
+  Symbol = SymbolInfo("Friend", SymbolInfo::SymbolKind::Function, HeaderName,
+                      3, {});
+  EXPECT_FALSE(hasSymbol(Symbol));
+
+  Symbol = SymbolInfo("BestFriend", SymbolInfo::SymbolKind::Class, HeaderName,
+                      4, {});
+  EXPECT_FALSE(hasSymbol(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