"method %4 is %select{not static|static}5|"
"method %4 is %select{not volatile|volatile}5|"
"method %4 is %select{not const|const}5|"
- "method %4 is %select{not inline|inline}5}3">;
+ "method %4 is %select{not inline|inline}5|"
+ "method %4 that has %5 parameter%s5|"
+ "method %4 with %ordinal5 parameter of type %6%select{| decayed from %8}7|"
+ "method %4 with %ordinal5 parameter named %6}3">;
def note_module_odr_violation_mismatch_decl_diff : Note<"but in '%0' found "
"%select{"
"method %2 is %select{not static|static}3|"
"method %2 is %select{not volatile|volatile}3|"
"method %2 is %select{not const|const}3|"
- "method %2 is %select{not inline|inline}3}1">;
+ "method %2 is %select{not inline|inline}3|"
+ "method %2 that has %3 parameter%s3|"
+ "method %2 with %ordinal3 parameter of type %4%select{| decayed from %6}5|"
+ "method %2 with %ordinal3 parameter named %4}1">;
def warn_module_uses_date_time : Warning<
"%select{precompiled header|module}0 uses __DATE__ or __TIME__">,
Inherited::VisitValueDecl(D);
}
+ void VisitParmVarDecl(const ParmVarDecl *D) {
+ // TODO: Handle default arguments.
+ Inherited::VisitParmVarDecl(D);
+ }
+
void VisitAccessSpecDecl(const AccessSpecDecl *D) {
ID.AddInteger(D->getAccess());
Inherited::VisitAccessSpecDecl(D);
Hash.AddBoolean(D->isPure());
Hash.AddBoolean(D->isDeletedAsWritten());
+ ID.AddInteger(D->param_size());
+
+ for (auto *Param : D->parameters()) {
+ Hash.AddSubDecl(Param);
+ }
+
Inherited::VisitFunctionDecl(D);
}
void ODRHash::AddCXXRecordDecl(const CXXRecordDecl *Record) {
assert(Record && Record->hasDefinition() &&
"Expected non-null record to be a definition.");
+
+ if (isa<ClassTemplateSpecializationDecl>(Record)) {
+ return;
+ }
+
AddDecl(Record);
// Filter out sub-Decls which will not be processed in order to get an
}
}
+ void AddQualType(QualType T) {
+ Hash.AddQualType(T);
+ }
+
+ void VisitQualifiers(Qualifiers Quals) {
+ ID.AddInteger(Quals.getAsOpaqueValue());
+ }
+
void Visit(const Type *T) {
ID.AddInteger(T->getTypeClass());
Inherited::Visit(T);
void VisitType(const Type *T) {}
+ void VisitAdjustedType(const AdjustedType *T) {
+ AddQualType(T->getOriginalType());
+ AddQualType(T->getAdjustedType());
+ VisitType(T);
+ }
+
+ void VisitDecayedType(const DecayedType *T) {
+ AddQualType(T->getDecayedType());
+ AddQualType(T->getPointeeType());
+ VisitAdjustedType(T);
+ }
+
+ void VisitArrayType(const ArrayType *T) {
+ AddQualType(T->getElementType());
+ ID.AddInteger(T->getSizeModifier());
+ VisitQualifiers(T->getIndexTypeQualifiers());
+ VisitType(T);
+ }
+ void VisitConstantArrayType(const ConstantArrayType *T) {
+ T->getSize().Profile(ID);
+ VisitArrayType(T);
+ }
+
+ void VisitDependentSizedArrayType(const DependentSizedArrayType *T) {
+ AddStmt(T->getSizeExpr());
+ VisitArrayType(T);
+ }
+
+ void VisitIncompleteArrayType(const IncompleteArrayType *T) {
+ VisitArrayType(T);
+ }
+
+ void VisitVariableArrayType(const VariableArrayType *T) {
+ AddStmt(T->getSizeExpr());
+ VisitArrayType(T);
+ }
+
void VisitBuiltinType(const BuiltinType *T) {
ID.AddInteger(T->getKind());
VisitType(T);
}
+ void VisitFunctionType(const FunctionType *T) {
+ AddQualType(T->getReturnType());
+ T->getExtInfo().Profile(ID);
+ Hash.AddBoolean(T->isConst());
+ Hash.AddBoolean(T->isVolatile());
+ Hash.AddBoolean(T->isRestrict());
+ VisitType(T);
+ }
+
+ void VisitFunctionNoProtoType(const FunctionNoProtoType *T) {
+ VisitFunctionType(T);
+ }
+
+ void VisitFunctionProtoType(const FunctionProtoType *T) {
+ ID.AddInteger(T->getNumParams());
+ for (auto ParamType : T->getParamTypes())
+ AddQualType(ParamType);
+
+ VisitFunctionType(T);
+ }
+
void VisitTypedefType(const TypedefType *T) {
AddDecl(T->getDecl());
Hash.AddQualType(T->getDecl()->getUnderlyingType());
MethodVolatile,
MethodConst,
MethodInline,
+ MethodNumberParameters,
+ MethodParameterType,
+ MethodParameterName,
};
// These lambdas have the common portions of the ODR diagnostics. This
return Hash.CalculateHash();
};
+ auto ComputeQualTypeODRHash = [&Hash](QualType Ty) {
+ Hash.clear();
+ Hash.AddQualType(Ty);
+ return Hash.CalculateHash();
+ };
+
switch (FirstDiffType) {
case Other:
case EndOfClass:
break;
}
+ const unsigned FirstNumParameters = FirstMethod->param_size();
+ const unsigned SecondNumParameters = SecondMethod->param_size();
+ if (FirstNumParameters != SecondNumParameters) {
+ ODRDiagError(FirstMethod->getLocation(),
+ FirstMethod->getSourceRange(), MethodNumberParameters)
+ << FirstName << FirstNumParameters;
+ ODRDiagNote(SecondMethod->getLocation(),
+ SecondMethod->getSourceRange(), MethodNumberParameters)
+ << SecondName << SecondNumParameters;
+ Diagnosed = true;
+ break;
+ }
+
+ // Need this status boolean to know when break out of the switch.
+ bool ParameterMismatch = false;
+ for (unsigned I = 0; I < FirstNumParameters; ++I) {
+ const ParmVarDecl *FirstParam = FirstMethod->getParamDecl(I);
+ const ParmVarDecl *SecondParam = SecondMethod->getParamDecl(I);
+
+ QualType FirstParamType = FirstParam->getType();
+ QualType SecondParamType = SecondParam->getType();
+ if (FirstParamType != SecondParamType &&
+ ComputeQualTypeODRHash(FirstParamType) !=
+ ComputeQualTypeODRHash(SecondParamType)) {
+ if (const DecayedType *ParamDecayedType =
+ FirstParamType->getAs<DecayedType>()) {
+ ODRDiagError(FirstMethod->getLocation(),
+ FirstMethod->getSourceRange(), MethodParameterType)
+ << FirstName << (I + 1) << FirstParamType << true
+ << ParamDecayedType->getOriginalType();
+ } else {
+ ODRDiagError(FirstMethod->getLocation(),
+ FirstMethod->getSourceRange(), MethodParameterType)
+ << FirstName << (I + 1) << FirstParamType << false;
+ }
+
+ if (const DecayedType *ParamDecayedType =
+ SecondParamType->getAs<DecayedType>()) {
+ ODRDiagNote(SecondMethod->getLocation(),
+ SecondMethod->getSourceRange(), MethodParameterType)
+ << SecondName << (I + 1) << SecondParamType << true
+ << ParamDecayedType->getOriginalType();
+ } else {
+ ODRDiagNote(SecondMethod->getLocation(),
+ SecondMethod->getSourceRange(), MethodParameterType)
+ << SecondName << (I + 1) << SecondParamType << false;
+ }
+ ParameterMismatch = true;
+ break;
+ }
+
+ DeclarationName FirstParamName = FirstParam->getDeclName();
+ DeclarationName SecondParamName = SecondParam->getDeclName();
+ if (FirstParamName != SecondParamName) {
+ ODRDiagError(FirstMethod->getLocation(),
+ FirstMethod->getSourceRange(), MethodParameterName)
+ << FirstName << (I + 1) << FirstParamName;
+ ODRDiagNote(SecondMethod->getLocation(),
+ SecondMethod->getSourceRange(), MethodParameterName)
+ << SecondName << (I + 1) << SecondParamName;
+ ParameterMismatch = true;
+ break;
+ }
+ }
+
+ if (ParameterMismatch) {
+ Diagnosed = true;
+ break;
+ }
+
break;
}
}
// expected-note@first.h:* {{but in 'FirstModule' found field 'x' with a different initializer}}
#endif
+#if defined(FIRST)
+struct S12 {
+ unsigned x[5];
+};
+#elif defined(SECOND)
+struct S12 {
+ unsigned x[7];
+};
+#else
+S12 s12;
+// expected-error@first.h:* {{'Field::S12::x' from module 'FirstModule' is not present in definition of 'Field::S12' in module 'SecondModule'}}
+// expected-note@second.h:* {{declaration of 'x' does not match}}
+#endif
+
+#if defined(FIRST)
+struct S13 {
+ unsigned x[7];
+};
+#elif defined(SECOND)
+struct S13 {
+ double x[7];
+};
+#else
+S13 s13;
+// expected-error@first.h:* {{'Field::S13::x' from module 'FirstModule' is not present in definition of 'Field::S13' in module 'SecondModule'}}
+// expected-note@second.h:* {{declaration of 'x' does not match}}
+#endif
} // namespace Field
namespace Method {
// expected-note@first.h:* {{but in 'FirstModule' found method 'A' is const}}
#endif
+#if defined(FIRST)
+struct S9 {
+ void A(int x) {}
+ void A(int x, int y) {}
+};
+#elif defined(SECOND)
+struct S9 {
+ void A(int x, int y) {}
+ void A(int x) {}
+};
+#else
+S9 s9;
+// expected-error@second.h:* {{'Method::S9' has different definitions in different modules; first difference is definition in module 'SecondModule' found method 'A' that has 2 parameters}}
+// expected-note@first.h:* {{but in 'FirstModule' found method 'A' that has 1 parameter}}
+#endif
+
+#if defined(FIRST)
+struct S10 {
+ void A(int x) {}
+ void A(float x) {}
+};
+#elif defined(SECOND)
+struct S10 {
+ void A(float x) {}
+ void A(int x) {}
+};
+#else
+S10 s10;
+// expected-error@second.h:* {{'Method::S10' has different definitions in different modules; first difference is definition in module 'SecondModule' found method 'A' with 1st parameter of type 'float'}}
+// expected-note@first.h:* {{but in 'FirstModule' found method 'A' with 1st parameter of type 'int'}}
+#endif
+
+#if defined(FIRST)
+struct S11 {
+ void A(int x) {}
+};
+#elif defined(SECOND)
+struct S11 {
+ void A(int y) {}
+};
+#else
+S11 s11;
+// expected-error@second.h:* {{'Method::S11' has different definitions in different modules; first difference is definition in module 'SecondModule' found method 'A' with 1st parameter named 'y'}}
+// expected-note@first.h:* {{but in 'FirstModule' found method 'A' with 1st parameter named 'x'}}
+#endif
+
+#if defined(FIRST)
+struct S12 {
+ void A(int x) {}
+};
+#elif defined(SECOND)
+struct S12 {
+ void A(int x = 1) {}
+};
+#else
+S12 s12;
+// TODO: This should produce an error.
+#endif
+
+#if defined(FIRST)
+struct S13 {
+ void A(int x = 1 + 0) {}
+};
+#elif defined(SECOND)
+struct S13 {
+ void A(int x = 1) {}
+};
+#else
+S13 s13;
+// TODO: This should produce an error.
+#endif
+
+#if defined(FIRST)
+struct S14 {
+ void A(int x[2]) {}
+};
+#elif defined(SECOND)
+struct S14 {
+ void A(int x[3]) {}
+};
+#else
+S14 s14;
+// expected-error@second.h:* {{'Method::S14' has different definitions in different modules; first difference is definition in module 'SecondModule' found method 'A' with 1st parameter of type 'int *' decayed from 'int [3]'}}
+// expected-note@first.h:* {{but in 'FirstModule' found method 'A' with 1st parameter of type 'int *' decayed from 'int [2]'}}
+#endif
} // namespace Method
// Naive parsing of AST can lead to cycles in processing. Ensure
// Interesting cases that should not cause errors. struct S should not error
// while struct T should error at the access specifier mismatch at the end.
namespace AllDecls {
-#define CREATE_ALL_DECL_STRUCT(NAME, ACCESS) \
- typedef int INT; \
- struct NAME { \
- public: \
- private: \
- protected: \
- static_assert(1 == 1, "Message"); \
- static_assert(2 == 2); \
- \
- int x; \
- double y; \
- \
- INT z; \
- \
- unsigned a : 1; \
- unsigned b : 2 * 2 + 5 / 2; \
- \
- mutable int c = sizeof(x + y); \
- \
- void method() {} \
- static void static_method() {} \
- virtual void virtual_method() {} \
- virtual void pure_virtual_method() = 0; \
- inline void inline_method() {} \
- void volatile_method() volatile {} \
- void const_method() const {} \
- \
- typedef int typedef_int; \
- using using_int = int; \
- \
- ACCESS: \
+#define CREATE_ALL_DECL_STRUCT(NAME, ACCESS) \
+ typedef int INT; \
+ struct NAME { \
+ public: \
+ private: \
+ protected: \
+ static_assert(1 == 1, "Message"); \
+ static_assert(2 == 2); \
+ \
+ int x; \
+ double y; \
+ \
+ INT z; \
+ \
+ unsigned a : 1; \
+ unsigned b : 2 * 2 + 5 / 2; \
+ \
+ mutable int c = sizeof(x + y); \
+ \
+ void method() {} \
+ static void static_method() {} \
+ virtual void virtual_method() {} \
+ virtual void pure_virtual_method() = 0; \
+ inline void inline_method() {} \
+ void volatile_method() volatile {} \
+ void const_method() const {} \
+ \
+ typedef int typedef_int; \
+ using using_int = int; \
+ \
+ void method_one_arg(int x) {} \
+ void method_one_arg_default_argument(int x = 5 + 5) {} \
+ void method_decayed_type(int x[5]) {} \
+ \
+ int constant_arr[5]; \
+ \
+ ACCESS: \
};
#if defined(FIRST)
#endif
}
+namespace ParameterTest {
+#if defined(FIRST)
+class X {};
+template <typename G>
+class S {
+ public:
+ typedef G Type;
+ static inline G *Foo(const G *a, int * = nullptr);
+};
+
+template<typename G>
+G* S<G>::Foo(const G* aaaa, int*) {}
+#elif defined(SECOND)
+template <typename G>
+class S {
+ public:
+ typedef G Type;
+ static inline G *Foo(const G *a, int * = nullptr);
+};
+
+template<typename G>
+G* S<G>::Foo(const G* asdf, int*) {}
+#else
+S<X> s;
+#endif
+}
+
+
// Keep macros contained to one file.
#ifdef FIRST
#undef FIRST